Crohasang Logo
#15

딥링크를 활용하여 앱 설치 유무를 인식하는 앱 배너 구현하기

2025년 11월 9일 오후 05:41

모바일에서 클릭했을 때, 사용자의 기기에서 앱이 깔려있으면 앱으로 이동하고, 앱이 깔려있지 않으면 앱스토어로 이동하게하는 배너를 최근 구현했다. iOS, 안드로이드마다 적용해야되는 로직이 다르기 때문에 각자 다르게 설정을 해줘야하고 또 여러 가지 방법으로 구현할 수 있기 때문에 글로 정리해보려 한다.


1. iOS

a. Smart App Banner - 사파리에서만 가능

가장 간편한 방법은 ‘스마트 앱 배너’인데, 사용자의 기기에 해당 앱이 깔려있으면 사파리에서 해당 앱이 해당하는 도메인에 접속하면 웹 사이트 상단에 자동으로 배너를 띄워준다. 구현 방법도 간단하다. HTML 헤더에다 다음과 같이 추가하면 된다.

<meta name="apple-itunes-app" content="app-id=myAppStoreID, app-argument=myURL">

구현이 쉽고 간단하지만, 사파리에서만 볼 수 있다는 점과 커스터마이징을 할 수 없다는 단점을 가지고 있다. 더 자세한 내용은 아래 링크에서 살펴볼 수 있다.

https://mine-it-record.tistory.com/710

b. Universal Link

유니버설 링크는 iOS에서 도메인 주소를 활용한 딥링크를 구현하는 방법이다. 유니버설 링크를 활용하면 해당 링크를 클릭했을 때, 기기 시스템에서 해당 앱이 기기에 깔려있는지 확인하고, 앱이 존재한다면 앱이 켜지게 된다.

특정 도메인을 클릭했을 때 시스템에서 앱과 연결되어 있는 도메인인지 판별할 수 있도록 도메인의 웹 서버에 apple-app-site-association(줄여서 AASA) 파일을 .well-known 디렉토리나 루트 디렉토리에 추가해야 한다.

AASA 파일 형식

{
	"applinks": {
		"apps": [],
		"details": [
			{
				"appID": "appId",
				"paths": [
					"NOT /앱에서 지원하지 않는 경로",
					"/앱에서 지원하는 경로",
				]
			}
		]
	}
}

paths 경로를 통해 특정 페이지에서는 유니버설 링크가 동작하지 않게 만들 수 있다. 또한, ‘/*’를 통해 사이트의 도메인의 모든 페이지를 앱에서 지원하는 경로로 설정할 수 있다.

웹 서버에 AASA 파일을 배포하면, Apple CDN에서 해당 웹 서버의 AASA 파일을 수집한 뒤, 해당 도메인의 앱이 설치되어있는 기기의 시스템에 AASA 파일의 설정을 삽입한다. 즉, 도메인 링크를 클릭했을 때 앱으로 넘어가는 동작은 기기의 시스템에서 동작한다.

→ Apple CDN에서 언제 도메인의 AASA 파일을 수집하는지는 예측할 수 없다. (24시간 ~ 48시간 정도라고 한다)

→ 잘 작동하는지 테스트를 하려면 해당 도메인 링크를 메모 앱에 적고 클릭해보면 된다. 참고로, 도메인을 직접 입력해서 접속하면 앱이 켜지지 않는다. (사용자가 해당 링크로 직접 이동하고 싶다고 시스템이 인식하기 때문)

→ 아니면 터미널에 다음과 같은 curl 요청을 날려보자

curl -v https://app-site-association.cdn-apple.com/a/v1/{your-domain}

200 응답과 함께 AASA 파일 JSON이 출력된다면 Apple CDN에 제대로 반영이 된것이다.

만약에 Not Found가 뜬다면, 아직 Apple CDN에서 반영이 안된 것이므로 조금만 더 기다려보자.

또한, 본인의 도메인에서 Apple CDN이 AASA 파일을 수집할 수 있는지 확인하려면 아래 명령어를 입력하자.

curl -v https://{your-domain}/.well-known/apple-app-site-association

참고1)

유니버설 링크가 적용된 도메인에 해당 앱이 설치된 기기로 사파리에 접속 시 위에 언급한 스마트 앱 배너와 같은 디자인의 배너가 웹 사이트 상단에 적용된다. 이미 커스텀 앱 배너가 웹 사이트에 적용되어 있었기에 어떻게 저 배너를 뺄 수 있나 여러 방면에서 고민해봤는데, 결국 해당 도메인의 유니버설 링크를 제거하기로 결론을 내렸다.

그래서 다음과 같은 방법을 적용했다.

  1. 해당 도메인의 웹 서버(nginx)에서 .well-known/apple-app-site-association의 접근을 막는다.
  2. 새로운 공유용 전문 도메인을 파고, 해당 도메인의 웹 서버에 AASA 파일을 추가한다.

이렇게 해서 유니버설 링크로 인해 자동으로 생기는 상단 배너를 없애고, 앱을 공유할 때는 새로운 공유용 전문 도메인을 활용하여 링크 클릭 시 앱이 켜지도록 구현했다.

참고2)

링크 시 사용자가 앱을 깔려 있지 않았을 경우, 앱스토어로 이동하는 로직은 사용자가 구현하여야 한다.

만약 사용자의 기기에 앱이 깔려있는 경우 해당 링크를 클릭하자마자 기기에서 앱이 켜지기 때문에, 코드에는 앱이 깔려있지 않은 기기만 접근할 수 있다. 즉, 코드에는 앱스토어로 가는 로직만 구현하면 된다.

배너를 클릭했을 때, 현재 탭에서 바로 공유용 URL이 켜지게 한다면 앱으로 이동하시겠습니까? 라는 팝업이 뜨게된다. 만약에 이 팝업이 뜨지 않고 바로 앱으로 이동하게 만들고 싶다면, 공유용 URL을 새 탭으로 키면 (window.open 사용) 팝업을 뜨지 않고 바로 앱으로 이동할 수 있다. (앱스토어 이동 팝업은 그대로 뜬다)

사실 팝업은 사라진게 아니라 새로 켜진 탭에 남아있게 되는데, 배너를 클릭해 새 탭을 킬 때, 켜진 탭을 몇초 뒤에 닫히도록 구현하면 앱에서 다시 웹으로 돌아왔을 때 다시 기존의 탭으로 돌아올 수 있다.

참고3)

만약에 사용자가 앱으로 이동하시겠습니까? 팝업에서 취소를 누른다면, 그 다음부터 사용자가 해당 배너를 누를 때 앱으로 이동하시겠습니까? 라는 배너가 뜨지 않고 무조건 웹으로 이동한다.

왜냐하면 기기의 시스템이 사용자의 이전 선택을 기억하기 때문인데, 만약 사용자가 배너를 클릭해서 다시 앱으로 이동하고 싶다면 배너를 꾹 눌러서 ‘앱으로 열기’ 옵션을 클릭하면 된다.


2. 안드로이드

a. App Link

iOS의 Universal Link와 같은 역할을 한다. iOS에서는 AASA 파일을 사용했듯이 안드로이드에서는 .well-known/assetlinks.json을 사용한다.

iOS는 AASA 파일이 Apple CDN에 반영되기까지 시간이 좀 걸렸지만, 안드로이드는 iOS에 비해 웹 서버 파일 변경이 반영되는 시간이 더 빠르다는 장점이 있다.

[
  {
    "relation": ["delegate_permission/common.handle_all_urls"],
    "target": {
      "namespace": "android_app",
      "package_name": "com.nachocode.example",
      "sha256_cert_fingerprints": [
        "AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90"
      ]
    }
  }
]

sha256_cert_fingerprints에는 앱 서명 키의 SHA-256 해시 값을 입력하면 된다. 안드로이드 개발자에게 해시 값이 무엇인지 물어보자.

앱 링크에 대한 더 자세한 내용은 아래 링크로 확인할 수 있다.

https://developer.nachocode.io/docs/guide/deep-link/app-link

b. Intent Scheme

인텐트 스킴 딥링크를 활용하면 사용자가 클릭한 링크를 앱의 인텐트로 연결할 수 있다. 인텐트 스킴은 사용자의 기기에 앱이 깔려있지 않을 경우 이동할 URL을 지정할 수 있어서 구현이 편리했다. (iOS의 커스텀 스킴은 fallback URL을 제공하지 않았다.)

intent://open
		scheme=(...)
    package=(...)
    S.browser_fallback_url=(...)
    end;

package에 열고자하는 앱의 패키지 이름을,

S.browser_fallback_url에서 앱이 설치않았을 때 대신 이동할 URL을 입력하면 된다.

인텐트 스킴에 대한 더 자세한 내용은 아래 링크에서 확인할 수 있다.

https://developer.nachocode.io/docs/guide/deep-link/intent-scheme


이번에 커스텀 앱 배너를 구현하면서 각 OS별 딥링크에 대해 익힐 수 있었다. 도메인별로 AASA 파일의 접근 여부를 제어해야 했기에 ssh에 접속해 nginx 코드를 계속 만졌는데, 실수로 서비스 접속이 아예 안되게 만들 수도 있었기에 꽤 긴장을 했던 거 같다. 코드 반영을 위해 nginx 서버를 재시동할 때마다 꽤 긴장을 했었다.

현재 앱 배너를 클릭할 때 iOS는 유니버설 링크, 안드로이드는 인텐트 스킴을 활용하여 앱/앱스토어 이동을 구현했다. 앱스토어 이동 로직을 통일시키기 위해 안드로이드도 앱 링크를 활용하도록 바꾸는게 더 좋았을까하는 생각도 드는데, 이건 안드로이드 개발자분과의 협의가 필요하니 좀 더 고민을 해보기로 했다.