URL Scheme — 모바일 앱이 운영 체제에 등록하는 사용자 정의 URI 프로토콜로, myapp://path와 같은 링크를 통해 열릴 수 있습니다. RFC 3986에 따르면, URI 스킴은 주소의 모든 후속 구성 요소의 구문과 의미를 정의합니다. 이러한 링크로 이동할 때 시스템은 고유 식별자로 등록된 앱을 식별하고 링크에서 추출된 매개변수와 함께 앱을 실행합니다. URL Scheme 기반의 딥 링크는 더 현대적인 대안의 등장에도 불구하고 모바일 플랫폼에서 앱 간 탐색의 기본 메커니즘으로 남아 있습니다.
주요 내용
URL Scheme — 앱이 사용자 정의 링크를 통해 호출을 수신하기 위해 운영 체제에 등록하는 고유한 프로토콜 식별자입니다. 사용자가 myapp://profile/123과 같은 링크를 클릭하면 시스템은 myapp 스킴을 등록한 앱을 식별하고 전체 URI와 함께 제어권을 전달합니다. 이 메커니즘을 통해 앱은 서버 인프라 없이도 데이터를 교환하고 서로를 열 수 있습니다.
URL Scheme의 개념은 웹 표준 RFC 3986에서 직접 차용되었으며, 여기서 URI 스킴은 모든 범용 리소스 식별자의 첫 번째 구성 요소입니다. 모바일 개발에서이 아이디어는 앱 간 통신에 적용되며, HTTP 서버 대신 앱 자체가 링크 핸들러 역할을 합니다.
많은 인기 앱이 타사 서비스와의 통합을 위해 자체 URL Scheme을 등록합니다. 예를 들어, Spotify는 spotify:// 스킴을 사용하고, Telegram은 tg://를 사용하며, Instagram은 instagram://를 사용합니다. 개발자는 또한 내부 탐색 및 종단 간 화면 테스트를 위해 appname:// 스킴을 자주 만듭니다.
URL Scheme은 앱의 특정 섹션으로 즉시 이동해야 하는 푸시 알림, 이메일 뉴스레터 및 QR 코드에서 여전히 널리 사용됩니다. 그러나 iOS 9 및 Android 6부터 단순한 스킴을 점차 보완하고 대체하는 대안 메커니즘이 등장했습니다.
사용자 정의 URI의 구조는 일반 RFC 3986 사양을 따르며 여러 구성 요소로 구성됩니다. 스킴이 먼저 지정되고 콜론으로 주소의 나머지 부분과 구분됩니다. 스킴 뒤에는 호스트, 포트, 경로, 쿼리 매개변수 및 프래그먼트가 올 수 있으며, 각각은 선택 사항입니다.
전체 구문은 scheme://host/path?key=value#fragment와 같습니다. 스킴은 유일한 필수 요소이며, 나머지는 특정 구현의 필요에 따라 결정됩니다. 스킴 뒤의 이중 슬래시는 역사적으로 HTTP에서 차용되었으며 사양상 엄격히 필수는 아니지만 관례로 보편적으로 사용됩니다.
URI 구조를 시각적으로 표현하기 위해 구성 요소 표가 사용됩니다. 각 요소는 고유한 목적과 필수 수준을 가지고 있습니다.
| 구성 요소 | 예시 | 필수 |
|---|---|---|
| Scheme | myapp | 예 |
| Host | profile | 아니오 |
| Path | /user/42 | 아니오 |
| Query | ?id=42&tab=main | 아니오 |
| Fragment | #section2 | 아니오 |
개발자는 URI 구조를 자유롭게 선택할 수 있어 유연성을 제공하지만 앱의 다른 버전 간에 호환성 문제가 발생합니다. URL Scheme 형식을 앱의 공개 API의 일부로 문서화하고 변경 시 버전을 관리하는 것이 좋습니다.
iOS는 프로젝트의 Info.plist 파일에 각 URL Scheme을 명시적으로 등록해야 합니다. 개발자는 CFBundleURLTypes 배열을 추가하며, 각 요소에는 식별자(CFBundleURLName)와 지원되는 스킴 목록(CFBundleURLSchemes)이 포함됩니다. 등록 후 시스템은 등록된 스킴에 대한 모든 수신 호출을 자동으로 앱으로 전달합니다.
수신 URL Scheme 처리는 앱 델리게이트의 application(_:open:options:) 메서드를 통해 이루어집니다. 이 메서드는 URL 객체를 받아 경로와 쿼리 매개변수를 추출하여 탐색 결정을 내립니다. 핸들러는 작업의 성공 여부를 나타내는 Bool 값을 반환해야 합니다.
다음은 Swift로 작성된 URL Scheme 핸들러 구현 예시입니다. 코드는 URLComponents를 사용하여 수신 URI에서 호스트와 쿼리 매개변수를 추출하는 방법을 보여줍니다.
func application(
_ app: UIApplication,
open url: URL,
options: [UIApplication.OpenURLOptionsKey: Any]
) -> Bool {
let host = url.host
let params = URLComponents(
url: url,
resolvingAgainstBaseURL: false
)?.queryItems
if host == "profile" {
navigateToProfile(params)
}
return true
}
이 메서드는 쿼리 매개변수의 안전한 구문 분석을 위해 URLComponents를 사용합니다. 이 접근 방식은 수동 문자열 구문 분석보다 선호되는데, 매개변수 값의 퍼센트 인코딩 및 특수 문자 디코딩을 자동으로 처리하기 때문입니다.
Android는 Intent Filter 시스템을 사용하여 URL Scheme 기반의 딥 링크를 라우팅합니다. 개발자는 링크를 처리해야 하는 Activity 태그 내에서 AndroidManifest.xml에 필터를 선언합니다. 필터에는 VIEW 작업, BROWSABLE 및 DEFAULT 카테고리, 그리고 스킴, 호스트 및 pathPrefix를 지정하는 data 태그가 포함됩니다.
사용자가 사용자 정의 스킴이 있는 링크를 클릭하면 시스템은 설치된 모든 앱의 Intent Filter를 확인합니다. 일치하는 여러 앱이 발견되면 사용자에게 선택 대화상자가 표시됩니다. BROWSABLE 카테고리는 브라우저에서 링크를 처리할 수 있도록 합니다.
Activity에서 myapp 스킴을 처리하기 위해 AndroidManifest.xml에 Intent Filter를 선언하는 예시입니다. 올바른 딥 링크 라우팅을 위해 action과 category의 조합이 필수입니다.
<activity android:name=".MainActivity">
<intent-filter>
<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="profile"
android:pathPrefix="/user" />
</intent-filter>
</activity>
Activity에서 필터를 구성한 후 URI를 얻으려면 intent.getData()를 호출해야 합니다. intent와 데이터가 null인지 확인하는 것이 중요합니다. Activity는 런처에서 표준 실행 시와 같이 수신 딥 링크 없이 시작될 수 있기 때문입니다.
URL Scheme의 쿼리 매개변수는 물음표 뒤에 key=value 형식으로 앰퍼샌드로 구분되어 전달됩니다. 이 형식은 HTTP 요청과 동일하며 플랫폼의 표준 도구로 쉽게 처리됩니다. 허용된 URI 문자 집합에 속하지 않는 모든 문자에 대해 퍼센트 인코딩을 사용하여 매개변수를 인코딩해야 합니다.
매개변수가 포함된 전체 링크 예시: myapp://profile?userId=42&source=email&ref=abc123. URL을 추출한 후 앱은 모든 쿼리 항목을 순차적으로 구문 분석하고 그 값을 기반으로 대상 화면으로의 탐색을 결정합니다.
복잡한 데이터를 전달할 때 URI 길이 제한을 고려하는 것이 중요합니다. iOS에서 URL Scheme의 최대 길이는 2KB로 제한되며, 그 이상이면 시스템이 링크를 자릅니다. Android에서는 제한이 약 8KB이지만 정확한 값은 OS 버전과 기기 제조사에 따라 다릅니다. 대량의 데이터의 경우 URL Scheme을 통해 세션 식별자만 전달하고 나머지 데이터는 서버에서 로드하는 것이 좋습니다.
URL Scheme의 주요 단점은 앱이 기기에 설치되지 않은 경우 링크를 처리할 수 없다는 것입니다. 브라우저에 오류가 표시되고 사용자는 탐색 컨텍스트를 잃게 됩니다. 이 문제를 해결하기 위해 Apple은 iOS 9에서 Universal Links를, Google은 Android 6에서 App Links를 도입했습니다. 두 메커니즘 모두 앱과 연결된 웹 도메인을 통해 등록됩니다.
Universal Links와 App Links는 일반 HTTPS 링크처럼 작동하지만, 앱이 설치된 경우 선택 대화상자 없이 앱을 엽니다. 앱이 설치되지 않은 경우 링크는 동일한 도메인의 웹 페이지를 열어 사용자 경험을 유지합니다. 따라서 프로덕션 환경에서 선호되는 대안입니다.
iOS 및 Android의 URL Scheme에는 내장된 폴백 메커니즘이 없습니다. 개발자는 중간 서버 솔루션을 사용합니다. 링크는 JavaScript를 통해 앱 설치 여부를 확인하고 스킴이나 앱 스토어로 리디렉션하는 웹 페이지로 연결됩니다. Firebase Dynamic Links와 Branch.io는 설치 상태를 자동으로 확인하고 사용자 정의 서버 파이프라인을 개발할 필요 없이 사용자를 라우팅하는 지연 딥 링크를 지원하는 이 문제의 기성 솔루션을 제공합니다.
iOS 15+ 및 Android 12+에서 URL Scheme을 사용할 때 추가적인 복잡성이 발생합니다. 여기서 개인정보 보호 규칙이 강화되었습니다. Safari는 사전 확인 없이 등록되지 않은 스킴을 열려는 시도를 차단하고, Android 12는 PackageManager를 통해 설치된 앱의 가시성을 제한합니다. 이러한 변경으로 인해 앱 간 통신을 위한 URL Scheme의 사용은 이전 플랫폼 버전보다 신뢰성이 떨어집니다.
자주 묻는 질문
URL Scheme은 암호화 없이 사용자 정의 프로토콜을 사용하는 반면, Universal Links는 도메인 확인과 함께 HTTPS를 통해 작동합니다. Universal Links는 앱 선택 대화상자를 표시하지 않으며 앱이 기기에 설치되지 않은 경우에도 올바르게 처리됩니다.
네, 하지만 모든 비ASCII 문자는 RFC 3986에 따라 퍼센트 인코딩을 사용하여 인코딩해야 합니다. 이전 OS 버전 및 브라우저와의 호환성을 보장하려면 URL Scheme에서 키릴 문자 사용을 피하는 것이 좋습니다.
iOS나 Android 모두 스킴 수에 제한이 없습니다. 실제로 앱은 1개에서 5개의 스킴을 사용합니다. 예를 들어, Telegram은 tg://, t.me/, telegram:// 및 telegram.me:// 스킴을 등록합니다.
iOS에서는 canOpenURL(_:) 메서드를 사용하며, 스킴이 등록된 경우 true를 반환합니다. Android에서는 PackageManager.queryIntentActivities()를 통해 확인합니다. 두 플랫폼 모두 구성에 스킴을 미리 지정해야 합니다.
아니요, URL Scheme은 데이터를 암호화하지 않습니다. 동일한 스킴을 등록한 모든 앱이 링크를 가로챌 수 있습니다. 보안을 위해 HTTPS와 함께 Universal Links 또는 프로토콜 수준의 종단 간 암호화를 사용하세요.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.