web 에서 app으로 이동하는 딥링크 알아보기.
intro
- 오래전에는 web 페이지에서 windows 의 특정 프로그램의 기능을 실행시키기 위한 기능으로 많이들 개발을 했었는데, 요즘은 그냥 앱에서 특정 동작을 하기 위한 개념으로 많이 사용되는 듯 하다.
- 겸사 겸사 관련 내용을 정리 해 봄.
- 사전적 지식은 여기로
- https://en.wikipedia.org/wiki/Deep_linking
미리 알아아할 지식
- URI (Uniform Resource Identifier)
- RFC 3986
- https://www.rfc-editor.org/rfc/rfc3986
- RFC 3986
- URI 하위 개념으로 아래와 같은 것들이 있다.
- URL (Uniform Resource Locator)
- URN (Uniform Resource Name)
- URI 구조
scheme:[//[user[:password]@]host[:port]][/path][?query][#fragment]
<스킴>://<사용자이름>:<비밀번호>@<호스트>:<포트>/<경로>?<질의>#<프레그먼트>
- 스킴 ( scheme )
- 사용할 프로토콜을 말하며, 리소스에 어떻게 요청, 접근할 것인지를 명시한다.
- 웹에서 주로 HTTP 프로토콜을 사용.
- 그 밖에 ftp, mailto(이메일), rtsp(스트리밍)과 같은 프로토콜을 사용할 수도 있고 custom 하게 만들어 사용하기도 한다.
- 첫 문자부터 콜론(:) 직전이 스킴이며, 영문, 숫자, 더하기(+), 점(.), 하이픈(-)으로 구성될 수 있으며, 영문은 소문자로 쓴다
- scheme = 영문 * (영문 / 숫자 / “+” / “-” / “.” )
- URI 스킴 목록
- http://www.iana.org/assignments/uri-schemes/uri-schemes.xhtml
딥링크
- 딥링크 방식
- URI 스킴 방식 : 앱에 URI 스킴(scheme) 값을 등록하여 딥링크 사용
- URI 스킴을 앱에 정의하는 방식으로 사용한다.
- URI schemes가 가진 문제점을 해결하기 위해 2015년 하반기에 iOS와 Android 플랫폼은 각각 새로운 딥링크를 개발하여 발표했다.
- 서버로 http 요청을 날리면 서버에서는 해당 값을 보고 http 응답 header 로 30X 등을 리턴하게 된다. 이때 특정 scheme 과 약속된 path 값을 내려준다.
- 가령 example://singup 이라고 내려주면 example 앱 에서는 회원가입 페이지를 띄운다.
- example://singup?data=abc#position 이렇게 되어있다면 회원가입 페이지에 abd 데이터를 전달하면서 position 부분을 바로 보여주는 식이 될 것이다.
- 특정 영역에 도달하기 위한 path 인 singup 부분과 더불어 파라미터와 쿼리 스트리링 data=abc 부분과 특정 영역을 가리키는 앵커(anchor)인 #position 로 구성됨.
- 참고 : https://developer.mozilla.org/ko/docs/Web/HTTP/Basics_of_HTTP/Identifying_resources_on_the_Web#uniform_resource_identifiers_(uris)_%EA%B5%AC%EB%AC%B8
- 특정 영역에 도달하기 위한 path 인 singup 부분과 더불어 파라미터와 쿼리 스트리링 data=abc 부분과 특정 영역을 가리키는 앵커(anchor)인 #position 로 구성됨.
- example://singup?data=abc#position 이렇게 되어있다면 회원가입 페이지에 abd 데이터를 전달하면서 position 부분을 바로 보여주는 식이 될 것이다.
- 가령 example://singup 이라고 내려주면 example 앱 에서는 회원가입 페이지를 띄운다.
- 널리 알려진 스킴들
- sms:// - 문자
- tel:// - 전화
- mailto:// - 메일
- geo:// - 위치/지도
- market:// - 앱마켓
- 한계점
- 앱이 설치되어 있을때만 실행할 수 있음
- 앱마다 같은 스킴을 사용하는 경우가 생김
- android 의 경우 어떤 앱으로 열지 선택하는 팝업이 떴고, ios 는 마지막에 설치한 앱이 열렸음
- 유니버셜 링크 (Universal Link)
- ios 에서 제공
- Universal Link 를 클릭했을때 앱이 설치되어있으면 os 에서 앱으로 이동 시키고, 설치되어있지 않으면 apple store 로 이동처리
- https://developer.apple.com/ios/universal-links/
- https://developer.apple.com/library/archive/documentation/General/Conceptual/AppSearch/UniversalLinks.html
- 앱링크(App Link)
- android 에서 제공
- https://developer.android.com/training/app-links?hl=ko
- 앱링크, 유니버셜 링크 공통사항
- 웹사이트 형태의 딥링크 url 로 만들어지고 해당 도메인이 이 앱의 소유자라는것을 인증해 줘야함.
- 해당 도메인에 앱의 고유 id 정보가 포함되어 있는 파일을 업로드 하고 해당 파일을 확인해서 인증처리하는 방식으로 진행됨
- 실행 동작
- 앱이 설치되어 있다면 해당 딥링크에 맞는 화면이 실행됨
- 앱이 설치되어 있지 않다면, 웹 URL로 인식되어 실제 해당 웹 링크를 열려고 시도함
- 웹서비스도 함께 제공하고 있다면 해당 웹서비스에서 맞는 컨텐츠를 보여줄것임
- 하지만 그렇지 않다면, 해당 URL로 웹으로 열었을때 앱 마켓(구글 플레이, 앱스토어)으로 이동시키도록 처리해두면 됨
- 한계점
- 브라우저 주소 입력창에 딥링크를 직접 입력하는 경우 동작하지 않는 경우가 있음
- os에서 제공하는 자체 브라우저에선 동작을 하기는 함
- 브라우저 주소 입력창에 딥링크를 직접 입력하는 경우 동작하지 않는 경우가 있음
- 웹사이트 형태의 딥링크 url 로 만들어지고 해당 도메인이 이 앱의 소유자라는것을 인증해 줘야함.
- 디퍼드 딥링크 (Deferred Deeplink)
- 앱이 설치되어있지 않으면 앱이 설치된 후 지연되어 실행되는 딥 링크.
- android
- referer 를 이용하는 방식.
- https://developer.android.com/google/play/installreferrer?hl=ko
- ios
- 공식적으로 지원하지 않음. IDFA, ADID, IP, 브라우저 쿠키, 내부 공유 저장소 등을 이용해서 알아서 잘 구현해야함.
- URI 스킴 방식 : 앱에 URI 스킴(scheme) 값을 등록하여 딥링크 사용
URL 단축
- 사실 원래의 긴 url 을 짧게 만드는 short url 도 비슷한 기술이다.
- 실행 흐름
- short url 생성하기
- 원본 url 에 해당하는 short url 을 생성한다
- 물론 생성 로직은 만들기 나름.
- 원본 url 과 short url 를 저장한다.
- short url 을 알려준다.
- 원본 url 에 해당하는 short url 을 생성한다
- 사용자가 short url 로 호출했을때
- short url 를 관리하는 서버로 요청을 보내면
- 해당 서버에서는 short url 에 매칭되는 원본 url 를 추출해서 http 30X등으로 내려준다.
- 사용자가 사용하는 client 프로그램에서 옮겨갈 주소로 다시 호출하게 된다.
- short url 생성하기
확인해보자.
-
https://www.kakaocorp.com/page/service/service/KakaoTalk 페이지에 들어가면 다운로드 항목중 iOS 를 눌러보면 아래와 같이 나온다.
-
PC 에서 호출 결과
- 중간에 보면 Location: https://apps.apple.com/kr/app/id362057947 이 보일 것이다.
% curl -i -I -L http://itunes.apple.com/kr/app/id362057947
HTTP/1.1 301 Moved Permanently
Server: 4.0.0
Content-Type: text/html; charset=UTF-8
Content-Length: 0
x-apple-jingle-correlation-key: X2IJB6D2NN7ILO6JEIUUZ7HUA4
x-apple-request-uuid: be9090f8-7a6b-7e85-bbc9-22294cfcf407
x-apple-translated-wo-url: /WebObjects/MZStore.woa/wa/viewSoftware?id=362057947&cc=kr&urlDesc=
apple-tk: false
x-b3-spanid: eacc65b41ffa4e59
x-apple-lokamai-no-cache: true
b3: be9090f87a6b7e85bbc922294cfcf407-eacc65b41ffa4e59
x-apple-aka-ttl: Generated Thu Feb 16 15:14:46 PST 2023, Expires Thu Feb 16 15:14:46 PST 2023, TTL 0s
x-b3-traceid: be9090f87a6b7e85bbc922294cfcf407
x-webobjects-loadaverage: 0
Last-Modified: Thu, 16 Feb 2023 23:14:46 GMT
apple-seq: 0.0
Access-Control-Allow-Origin: *
apple-originating-system: MZStore
x-frame-options: SAMEORIGIN
x-apple-orig-url: https://itunes.apple.com/kr/app/id362057947
x-apple-application-site: ST11
apple-timing-app: 3 ms
Location: https://apps.apple.com/kr/app/id362057947
x-apple-application-instance: 2173004
x-responding-instance: MZStore:2173004:::
Strict-Transport-Security: max-age=31536000; includeSubDomains
x-daiquiri-instance: daiquiri:41896008:st53p00it-qujn12040101:7987:22RELEASE218:daiquiri-amp-store-l7shared-int-001-st
Cache-Control: max-age=0
Date: Thu, 16 Feb 2023 23:14:46 GMT
X-Cache: TCP_MISS from a23-201-35-22.deploy.akamaitechnologies.com (AkamaiGHost/11.0.0-46340752) (-)
X-True-Cache-Key: /L/itunes.apple.com/kr/app/id362057947 vcd=2897 ci2=///
Connection: keep-alive
X-Cache-Remote: TCP_MISS from a104-70-122-21.deploy.akamaitechnologies.com (AkamaiGHost/11.0.0-46340752) (-)
Vary: X-Apple-Store-Front, Cookie
Vary: X-Apple-Store-Front, Cookie
X-Apple-Partner: origin.0
HTTP/2 200
server: daiquiri/3.0.0
content-type: text/html; charset=utf-8
x-daiquiri-rate-limit-generic: generic-minute-lim:180000;generic-minute-rem:27053;
x-daiquiri-rate-limit-timing-generic: 0
x-daiquiri-rate-limit-ip: ip-minute-lim:125;ip-minute-rem:124;
x-daiquiri-rate-limit-timing-ip: 0
content-security-policy: upgrade-insecure-requests ; default-src 'none'; img-src 'self' https://*.apple.com https://*.mzstatic.com data:; style-src 'self' https://*.apple.com 'unsafe-inline'; font-src 'self' https://*.apple.com; media-src 'self' https://*.apple.com blob:; connect-src 'self' https://*.apple.com https://*.mzstatic.com; script-src 'self' https://*.apple.com 'unsafe-eval' 'sha256-4ywTGAe4rEpoHt8XkjbkdOWklMJ/1Py/x6b3/aGbtSQ=' blob:; frame-src 'self' https://*.apple.com itmss: itms-appss: itms-bookss: itms-itunesus: itms-messagess: itms-podcasts: itms-watchs: macappstores: musics: apple-musics: podcasts: videos:;
x-xss-protection: 1; mode=block
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
x-apple-jingle-correlation-key: PDAPNOMBKVOT5TK2Z7ZOZHXU3E
strict-transport-security: max-age=31536000; includeSubDomains
x-daiquiri-instance: daiquiri:41896004:st53p00it-qujn15040101:7987:22RELEASE218:daiquiri-amp-store-l7shared-int-001-st
x-daiquiri-instance: daiquiri:48215001:st44p00it-hyhk15044901:7987:22RELEASE218:daiquiri-amp-store-shared-ext-001-st
cache-control: public, max-age=7
expires: Thu, 16 Feb 2023 23:14:53 GMT
date: Thu, 16 Feb 2023 23:14:46 GMT
x-cache: TCP_HIT from a23-201-35-95.deploy.akamaitechnologies.com (AkamaiGHost/11.0.0-46340752) (A)
set-cookie: geo=KR; domain=.apple.com
vary: X-Apple-Store-Front, Cookie
- iPhone 에서 호출 결과
- 중간에 보면 location: itms-appss://apps.apple.com/kr/app/id362057947 를 볼수가 있다. itms-appss 스킴을 사용중이다.
HTTP/2 301
server: daiquiri/3.0.0
content-type: text/html; charset=UTF-8
content-length: 0
x-apple-jingle-correlation-key: U6P2V6XYMB2GPQX64R3YHUVA4Q
x-apple-request-uuid: a79faafa-f860-7467-c2fe-e47783d2a0e4
x-apple-translated-wo-url: /WebObjects/MZStore.woa/wa/viewSoftware?id=362057947&cc=kr&urlDesc=
apple-tk: false
x-b3-spanid: 4b831187011a223c
x-apple-lokamai-no-cache: true
x-apple-lokamai-no-cache: true
b3: a79faafaf8607467c2fee47783d2a0e4-4b831187011a223c
x-apple-aka-ttl: Generated Thu Feb 16 15:33:33 PST 2023, Expires Thu Feb 16 15:33:33 PST 2023, TTL 0s, cache-maxage=0s
x-b3-traceid: a79faafaf8607467c2fee47783d2a0e4
x-webobjects-loadaverage: 0
last-modified: Thu, 16 Feb 2023 23:33:33 GMT
apple-seq: 0.0
access-control-allow-origin: *
apple-originating-system: MZStore
strict-transport-security: max-age=31536000; includeSubDomains
x-frame-options: SAMEORIGIN
x-apple-orig-url: https://apps.apple.com/kr/app/id362057947
x-apple-application-site: MR22
apple-timing-app: 5 ms
location: itms-appss://apps.apple.com/kr/app/id362057947
x-apple-application-instance: 3279002
x-responding-instance: MZStore:3279002:::
x-daiquiri-instance: daiquiri:11896007:mr47p00it-qujn04123002:7987:22RELEASE218:daiquiri-amp-store-l7shared-int-001-mr
x-daiquiri-instance: daiquiri:11896006:mr84p00it-qujn09092102:7987:22RELEASE218:daiquiri-amp-store-l7shared-int-001-mr
x-daiquiri-instance: daiquiri:14904002:mr85p00it-hyhk04184801:7987:22RELEASE218:daiquiri-amp-store-shared-ext-004-mr
expires: Thu, 16 Feb 2023 23:33:33 GMT
cache-control: max-age=0, no-cache, no-store
pragma: no-cache
date: Thu, 16 Feb 2023 23:33:33 GMT
x-cache: TCP_REFRESH_MISS from a23-67-53-198.deploy.akamaitechnologies.com (AkamaiGHost/11.0.0-46340752) (S)
x-cache-remote: TCP_REFRESH_MISS from a222-122-182-140.deploy.akamaitechnologies.com (AkamaiGHost/11.0.0-46340752) (S)
set-cookie: geo=KR; domain=.apple.com
vary: X-Apple-Store-Front, Cookie
참고
- https://www.airbridge.io/blog/deeplink-101-for-marketers-and-developers