사내 프로젝트를 진행하며, 외부 제휴 서비스의 링크를 통해 앱 내 특정 웹뷰나 화면으로 바로 진입해야 하는 요구사항이 있었습니다.
또한 초대 링크를 통해 사용자가 앱 실행과 동시에 특정 화면으로 이동할 수 있는 구조가 필요했습니다.이와 같은 UX를 제공하면 사용자의 앱 설치 전환율을 높일 수 있고,
설치 이후에도 자연스러운 흐름으로 서비스에 진입하게 되어 리텐션 향상에도 긍정적인 영향을 줄 수 있습니다.이 글에서는 이러한 요구사항을 해결하기 위해 적용한
Custom URL Scheme 기반 딥링크에 대해 정리해보려 합니다.
딥링크(Deep Link)는 앱 외부에서 특정 링크를 통해 앱의 특정 화면으로 바로 이동할 수 있도록 해주는 기술입니다.
일반적인 웹 링크가 브라우저의 홈 화면이나 메인 페이지로 이동시키는 것과 달리,
딥링크는 사용자를 앱 내부의 원하는 지점까지 직접 안내합니다.예를 들어, 메신저로 전달받은 링크를 눌렀을 때
앱이 실행되면서 특정 게시글, 초대 화면, 결제 페이지가 바로 열리는 경험을 한 적이 있다면 이미 딥링크를 사용해본 것입니다.왜 딥링크가 필요할까요?
모바일 앱에서 사용자의 흐름은 매우 짧고 단순합니다.
링크를 눌렀는데 앱이 실행된 후 다시 여러 단계를 거쳐 원하는 화면으로 이동해야 한다면, 그 과정에서 사용자는 쉽게 이탈하게 됩니다.딥링크를 사용하면 다음과 같은 UX를 제공할 수 있습니다.
- 외부 서비스(웹, 메신저, 이메일 등)에서 앱으로 자연스럽게 유입
- 앱 실행 후 불필요한 탐색 과정 없이 바로 목적 화면으로 이동
- 초대 링크, 이벤트 링크 등 맥락이 유지된 상태로 사용자 경험 제공
이러한 경험은 단순한 편의성을 넘어, 앱 설치 전환율과 사용자 리텐션을 높이는 핵심 요소가 됩니다.
대부분의 딥링크의 동작 흐름은 다음과 같습니다.
- 사용자가 외부에서 특정 링크를 클릭한다.
- 운영체제(iOS / Android)는 해당 링크를 처리할 앱이 있는지 확인한다.
- 앱이 설치되어 있다면 앱을 실행하고, 링크 정보를 앱에 전달한다.
- 앱은 전달받은 정보를 기반으로 적절한 화면으로 라우팅한다.
자, 서사가 길었으니 본격적으로 딥링크에 대해서 알아봅시다.
딥링크는 다양한 상황에서 활용됩니다.
- 초대 링크를 통해 특정 방이나 그룹으로 바로 진입
- 외부 웹에서 앱 내 상세 페이지로 이동
- 푸시 알림 클릭 시 관련 화면으로 이동
- 이벤트·프로모션 링크를 통해 특정 기능 강조
1. URL Scheme
가장 먼저 알아볼 것은 커스텀 URL Scheme 방식입니다.
우리가 구글에 접속하려면 도메인 주소를 입력하면 됩니다.
https://www.google.com위 주소로 우리는 구글에 접속할 수 있습니다.
URL Scheme은 이러한 방식과 굉장히 유사합니다. 앱 자체적으로 정의한 Scheme을 통해서 우리의 앱을 실행시킬 수 있습니다.
우리가 정의한 앱이 myapp이라는 가정하에
myapp://invite
위 구조로 앱을 실행시킬 수 있습니다. 물론 Android와 iOS에 적절하게 등록하여야 합니다.
좀 더 자세하게 들여다봅시다.
myapp://invite에서 myapp은 우리가 지정한 Scheme이고, invite는 host부분입니다. 전체 구조는 아래와 같습니다.
{Scheme}://{Host}/{Path1}/{Path2}..{Pathn}?{Param1}=1&{Param2}=2
Scheme은 우리가 정의한 커스텀 Scheme이며, Host는 고유한 값으로 지정할 수 있습니다. 또한 각 앱에서 사용할 여러 데이터 혹은 경로 정보를 Path, QueryParameter부분으로 넘겨줄 수 있습니다.
1. Android
android/app/src/main/AndroidManifest.xml에서 아래와 같이 Scheme을 지정할 수 있습니다.
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="myapp" android:host="invite" />
</intent-filter>
만약 Flutter 버전이 낮거나 써드파티 라이브러리를 사용한다면 아래와 같은 설정도 추가하는것을 고려해야 합니다.
<!--Flutter 버전이 3.27보다 낮다면 추가-->
<meta-data android:name="flutter_deeplinking_enabled" android:value="true" />
<!--app_links 같은 써드파티 라이브러리로 딥링크를 처리한다면 추가-->
<meta-data android:name="flutter_deeplinking_enabled" android:value="false" />
설정을 완료한 후 시뮬레이터 혹은 실기기에 빌드한 후
$ adb shell 'am start -a android.intent.action.VIEW \
-c android.intent.category.BROWSABLE \
-d "myapp://invite"' \
<com.exmaple.myapp> // 앱 패키지 이름
2. iOS
ios/Runner/Info.plist에서 아래와 같이 등록합니다.
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>io.snapplay.cpu</string>
<key>CFBundleURLSchemes</key>
<array>
<string>myapp</string>
<string>mymyapp</string>
</array>
</dict>
</array>
CFBundleURLSchemes에 array에 추가된 모든 값을 사용할 수 있습니다.
설정을 완료한 후 시뮬레이터 혹은 실기기에 빌드한 후$ xcrun simctl openurl booted myapp://invite를 통해 테스트할 수 있습니다.
혹은 두 플랫폼 모두 메모앱 실행 후 링크를 적은 뒤 눌러서 실행하는 간단한 방법도 있습니다.😀
굉장히 간단하지만 이 방식에는 단점이 존재합니다.
어떤 앱인지 구분할 방법이 없으므로 동일한 URL Scheme을 사용한다면 여러앱 중 하나를 선택해야됩니다.
실제로 각 스토어가 모두 동일한 딥링크 Scheme을 사용합니다. 테스트 시 스토어 중 하나를 선택해야합니다.
2. app links & universal links
이러한 한계를 보완하기 위해서 Android와 iOS는 각각 app links, universal links를 제공하고 있습니다.
기존 URL Scheme방식과 다르게 http, https 형식의 웹 도메인 표준을 따릅니다. 따라서 운영하는 서비스의 웹 도메인을 그대로 앱에서 딥링크에서 사용할 수 있습니다. 설정 방식은 기존 URL Scheme과 비슷합니다.
app links와 universal links는 설정 파일을 함께 웹서비스에 호스팅하여 웹 방문시 앱을 실행시킬 수 있도록 합니다.
1. Android
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" android:host="example.com" />
<data android:scheme="https" />
</intent-filter>
내가 호스팅한 웹 도메인을 명시해줍니다.
assetlinks.json파일을 생성해서 웹 서비스에 함께 호스팅합니다.
[{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app", // 앱 이름
"package_name": "com.example.myapp, // 내 패키지 명
"sha256_cert_fingerprints": // sha256키
["FF:2A:CF:7B:DD:CC:F1:03:3E:E8:B2:27:7C:A2:E3:3C:DE:13:DB:AC:8E:EB:3A:B9:72:A1:0E:26:8A:F5:EC:AF"]
}
}]
당연한 이야기지만, sha256인 경우 디버그, 릴리즈, 출시 모두 각각 다릅니다. 각각 안드로이드 스튜디오 디버그 sha256, 릴리즈 sha256, 플레이콘솔 sha256을 사용해야 합니다.
2. iOS
Targets > Runner > Signing & Capabilities > + Associated Domain를 추가하고, URL Scheme을 추가합니다.


내가 호스팅한 웹 도메인을 명시해줍니다. example.com 부분만 변경해줍니다.
apple-app-site-association JSON 파일을 생성해서 함께 호스팅합니다. JSON 포맷이지만 .json을 함께 붙이면 안됩니다.
{
"applinks": {
"apps": [],
"details": [
{
"appIDs": [
"[애플 개발자 계정 ID].[번들 Idendifier ex) com.example.myapp]
],
"paths": [
"*"
],
"components": [
{
"/": "/*"
}
]
}
]
},
"webcredentials": {
"apps": [
"[애플 개발자 계정 ID].[번들 Idendifier ex) com.example.myapp]"
]
}
}
paths에는 적용시킬 경로를 등록할 수 있습니다.
오늘은 딥 링크에 대한 개념과 적용 방법에 대해서 정리해봤습니다.
특히 서비스 규모가 커질수록, 딥링크는 단순한 편의 기능이 아니라
서비스 흐름을 연결하는 핵심 인프라에 가깝습니다.단순 링크 처리로 인식하지 않고, 앱 외부와 내부의 연결흐름을 이해하며 설계하는 과정이 중요합니다.