TIL-04: Deep Link

현재 회사에서 진행 중인 프로젝트 내용 중의 하나로 고객사 직원들이 QR 코드를 카메라 앱으로 찍고 곧바로 mobile 앱의 특정 화면으로 이동하도록 해야 하는 기능을 만들어야 합니다.
그래서 이와 관련된 Deep Link 라는 개념에 대해 공부해보기로 했습니다.
Deep Link란?
한 문장 정의: Deep Link는 링크를 통해 특정 모바일 앱의 특정 화면으로 직접 이동시키는 기술입니다. 웹 URL 클릭, QR 스캔, 푸시 알림, 광고 등 다양한 경로에서 사용됩니다.
"Deep"이라는 이름은 앱의 깊은 곳(특정 화면)으로 바로 들어간다는 의미입니다. 앱 홈 화면이 아니라, 예를 들어 "출근 확인 화면"으로 곧바로 이동하는 것입니다.
왜 필요한가?
QR 코드를 스캔하면 URL이 나옵니다.
https://tna.example.com/clock?store=STORE_001&action=clock_in&ts=...&sig=...
이 URL을 처리하는 시나리오가 세 가지 있습니다.
시나리오 1: 웹 브라우저가 열림 (Deep Link 없음)
직원이 QR 스캔 → 브라우저 열림 → 로그인 페이지 → 로그인 → 출근 처리
(느리고 번거로움)
시나리오 2: 앱이 바로 열림 (Deep Link 있음, 앱이 이미 설치됨)
직원이 QR 스캔 → 앱의 출근 화면이 즉시 열림 → 출근 처리
(빠르고 편함)
시나리오 3: 앱이 설치되어 있지 않음 (Deep Link 있음)
직원이 QR 스캔 → 앱이 없으면 앱스토어로 이동 또는 웹 페이지로 Fallback
(합리적임)
Deep Link가 없으면 매번 브라우저를 거쳐야 하고, 있으면 QR 스캔 → 즉시 앱 실행이 가능합니다.
Deep Link의 3가지 유형
Deep Link 기술은 시간에 따라 진화해왔습니다. 이걸 먼저 이해해야 Universal Link와 App Link가 왜 등장했는지 알 수 있습니다.
유형 1: Custom URI Scheme
mytnapp://clock?store=STORE_001&action=clock_in
가장 원시적인 방식입니다. 앱이 mytnapp://이라는 자기만의 스킴을 등록하고, 이 스킴으로 시작하는 URL이 호출되면 OS가 해당 앱을 실행합니다.
동작 원리:
1. 앱이 설치될 때 OS에 등록: "mytnapp:// 스킴은 내가 처리합니다."
2. 사용자가 mytnapp://... 링크를 클릭
3. OS가 등록된 앱을 찾아서 실행
4. 앱이 URL을 파싱하여 해당 화면으로 이동
치명적인 문제들:
첫째, 소유권 검증이 없습니다. 아무 앱이나 mytnapp:// 스킴을 등록할 수 있습니다. 악성 앱이 같은 스킴을 등록하면 사용자의 데이터가 악성 앱으로 갈 수 있습니다. 보안적으로 위험합니다.
둘째, 앱이 없으면 아무 일도 안 일어납니다. https://는 브라우저가 열리지만, mytnapp://은 처리할 앱이 없으면 그냥 에러입니다. Fallback 처리가 자동으로 되지 않습니다.
셋째, 웹과 호환되지 않습니다. mytnapp://을 웹 브라우저에서 열면 "이 링크를 열 수 없습니다." 에러가 뜹니다.
유형 2: iOS Universal Link & Android App Link
https://tna.example.com/clock?store=STORE_001&action=clock_in
Apple과 Google이 각각 유형 1의 문제를 해결하기 위해 만든 것이 **Universal Link(iOS)**와 App Link(Android) 입니다.
핵심 아이디어: 일반 HTTPS URL을 그대로 Deep Link로 사용합니다. 커스텀 스킴이 아닌 https://를 쓰되, 이 URL의 도메인이 정말 해당 앱의 것인지를 OS 레벨에서 검증합니다.
유형 3: Deferred Deep Link
앱이 설치되어 있지 않을 때, 앱스토어로 보내서 설치한 후 최초 실행 시 원래 의도했던 화면으로 이동시키는 기술입니다. Universal Link나 App Link 위에서 동작하는 기능 레이어로, Firebase Dynamic Links(현재 deprecated)나 Branch.io 같은 서드파티 서비스가 이 역할을 합니다. 이번 학습에서는 유형 2에 집중합니다.
Universal Link(iOS)
https://myapp.com/product/123
일반 HTTPS URL을 그대로 사용하되, iOS가 "이 도메인은 이 앱과 연결되어 있다"는 것을 알고, 앱이 설치되어 있으면 앱을 열어주는 방식입니다.
작동을 위해 필요한 것 두 가지:
- 서버 측:
https://myapp.com/.well-known/apple-app-site-association(AASA) 파일 배포 - 앱 측: Associated Domains entitlement에 도메인 등록
핵심 동작 흐름:
- 앱 설치 시 iOS가 AASA 파일을 미리 다운로드하여 도메인-앱 매핑을 캐싱
- 사용자가
https://myapp.com/product/123클릭 - iOS가 캐싱된 매핑을 확인 → 앱이 있으면 앱으로 직접 열림
- 앱이 없으면 → 그냥 Safari에서 해당 URL의 웹페이지가 열림
장점: 일반 HTTPS URL이라 앱이 없어도 웹페이지로 fallback이 자연스럽고, 도메인 소유권 검증이 되므로 스킴 충돌 문제가 없습니다.
App Link(Android)
https://myapp.com/product/123
Universal Link의 Android 버전입니다. 구조가 거의 동일합니다.
필요한 것:
- 서버 측:
https://myapp.com/.well-known/assetlinks.json파일 배포 - 앱 측: AndroidManifest.xml에 intent-filter +
autoVerify="true"설정
핵심 동작 흐름:
- 앱 설치 시 Android가 assetlinks.json을 다운로드하여 도메인-앱 매핑 검증
- 사용자가 URL 클릭
- 검증된 앱이 있으면 → 앱으로 직접 열림 (선택 다이얼로그 없이)
- 앱이 없으면 → 브라우저에서 웹페이지 열림
"앱 미설치 시 스토어로 이동" 시나리오
Universal Link, App Link는 앱이 이미 설치되어 있는 경우에 대해서는 의도한대로 동작합니다. 하지만 앱이 기기에 설치되어 있지 않은 경우, 사용자를 바로 설치할 수 있는 스토어로 이동하는 것은 지원하지 않습니다.
"앱 미설치 시 스토어로 이동"이라는 요구사항을 구현하기 위해서는 보통 이렇게 시스템을 구성합니다:
서버의 웹 페이지(https://myapp.com/product/123)가 핵심 허브 역할을 합니다.
- Universal Link / App Link가 작동 → 앱이 바로 열림 (웹 페이지를 거치지 않음)
- 앱이 없어서 웹페이지가 열림 → 웹페이지의 JavaScript가 User-Agent를 판별:
- iOS → App Store로 리다이렉트
- Android → Play Store로 리다이렉트
- 데스크톱 → 웹 콘텐츠 표시
이 방식이면 하나의 URL(https://myapp.com/product/123)로 모든 시나리오를 커버할 수 있게 됩니다.
실무에서 주의해야 할 점
Universal Link 함정들:
- Safari 주소창에 직접 URL을 입력하면 Universal Link가 작동하지 않음 (Apple의 의도적 설계 - 사용자가 웹을 보려는 의도로 판단)
- 같은 도메인 내 링크 클릭은 Universal Link가 트리거되지 않음 (cross-domain이어야 함)
- 사용자가 앱 대신 Safari로 열기를 한 번 선택하면, 이후 해당 도메인은 계속 Safari로 열림
App Link 함정들:
- 일부 Android 제조사(삼성 등)의 커스텀 브라우저에서 동작이 다를 수 있음
- assetlinks.json 검증 실패 시, Android 12 미만에서는 일반 intent-filter처럼 선택 다이얼로그가 뜨고, Android 12 이상에서는 다이얼로그 없이 기본 브라우저에서 열림
Comments (0)
Checking login status...
No comments yet. Be the first to comment!
