Android App Links 검증 실패, assetlinks.json·서명·리디렉션을 나누는 법

Android App Links를 상징하는 일반 스마트폰과 작은 서버 상자, 연결되지 않은 케이블이 놓인 현실적인 개발 작업대

AI를 활용하여 생성한 이미지입니다.

웹 주소를 눌렀을 때 앱이 바로 열리지 않고 브라우저나 선택 창으로 빠지는 문제는 “딥 링크가 고장 났다”는 한 문장으로 설명되지 않아요. Android App Links는 앱의 intent filter, 웹 서버의 assetlinks.json, 출시 서명 지문, 사용자의 기본 링크 설정이 함께 맞아야 해요.[1][2]

중급 개발자라면 앱 코드만 다시 빌드하기 전에 검증 경계를 나눠야 해요. manifest가 어떤 호스트를 선언했는지, 서버가 어느 호스트에서 JSON을 반환하는지, 기기가 어떤 상태를 기록했는지를 각각 확인하면 재설치 반복을 줄일 수 있어요.

App Links의 검증 흐름

android:autoVerify="true"가 들어간 intent filter는 Android 6.0(API 23) 이상에서 호스트 검증을 시도해요.[1] 시스템은 VIEW, BROWSABLE, DEFAULT, http 또는 https 조건을 가진 필터를 보고, 선언된 호스트마다 https://호스트/.well-known/assetlinks.json을 조회해요.[1]

<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="https" android:host="example.com" />
</intent-filter>

필터에 www.example.com과 example.com을 따로 넣었다면 두 호스트가 같은 사이트로 보이더라도 검증 파일을 각각 준비해야 해요. Android 문서는 여러 호스트를 선언할 때 각 호스트에서 파일을 제공해야 한다고 설명해요.[2] 한쪽만 성공해도 모든 URL 패턴이 정상이라고 단정하면 안 돼요.

점검 구간 확인할 것 흔한 실패
manifest scheme·host·autoVerify 실제 주소와 다른 호스트 선언
웹 서버 경로·HTTPS·응답 형식 리디렉션, HTML 오류 페이지, 잘못된 MIME
서명 release SHA-256 지문 debug 지문이나 Play App Signing 지문 사용
기기 상태 verified·selected·none 이전 검증 결과나 사용자 선택이 남음

assetlinks.json은 정확한 위치와 서명이 핵심이에요

웹 서버에는 https://example.com/.well-known/assetlinks.json을 HTTPS로 제공해야 해요. Android Developers 문서는 이 파일이 리디렉션 없이 접근 가능해야 하고, application/json으로 제공되어야 한다고 안내해요.[2] 브라우저에서 열리는지만 보지 말고, 앱이 선언한 각 호스트에 직접 요청해 응답 상태와 본문을 확인하세요.

[
  {
    "relation": ["delegate_permission/common.handle_all_urls"],
    "target": {
      "namespace": "android_app",
      "package_name": "com.example.app",
      "sha256_cert_fingerprints": ["RELEASE_CERT_SHA256"]
    }
  }
]

package_name은 앱의 application ID와 같아야 하고, 지문은 실제 링크를 처리하는 빌드에 서명한 인증서와 맞아야 해요.[2] Play App Signing을 사용한다면 로컬 debug 키의 지문을 그대로 복사하지 말고 Play Console의 앱 서명 정보를 확인하세요.[2]

리디렉션은 검증을 깨뜨릴 수 있어요

http://example.com을 https://www.example.com으로 보내는 리디렉션이 평소 웹사이트 이용에는 자연스러워도, App Links 검증에서는 문제가 될 수 있어요. Android의 문제 해결 문서는 HTTP에서 HTTPS로, 또는 non-www에서 www로 넘어가는 서버 리디렉션이 검증 실패를 일으킬 수 있다고 설명해요.[3]

호스트를 하나로 정했다면 manifest와 JSON 요청 주소, 실제 링크 주소를 그 호스트로 맞추세요. CDN이나 보안 프록시가 /.well-known/ 요청만 다른 응답으로 바꾸는지도 확인해야 해요. 파일이 정상 JSON인데 앱에서는 실패한다면, 앱이 요청하는 호스트와 curl 또는 브라우저로 확인한 호스트가 같은지 먼저 비교하세요.

기기에서 상태를 초기화하고 다시 확인해요

Android 12 이상에서는 검증 상태를 명령줄에서 초기화하고 다시 검증할 수 있어요. 먼저 현재 상태를 초기화한 뒤 검증을 요청하고, 결과를 조회하는 순서예요.[1]

adb shell pm set-app-links --package PACKAGE_NAME 0 all
adb shell pm verify-app-links --re-verify PACKAGE_NAME
adb shell pm get-app-links PACKAGE_NAME

검증 요청은 즉시 끝나지 않을 수 있어요. Android 문서는 비동기 검증을 위해 잠시 기다린 뒤 결과를 확인하라고 안내해요.[1] verified가 아닌 상태라면 재설치만 반복하지 말고 호스트 목록과 서버 응답을 다시 대조하세요.

Android 15의 동적 규칙은 별도 층이에요

Android 15(API 35) 이상에서는 assetlinks.json에 경로·쿼리·fragment를 다루는 동적 규칙을 추가할 수 있어요. 다만 서버 파일의 규칙이 manifest가 허용하지 않은 범위를 새로 넓혀 주는 것은 아니고, 정적 선언과 함께 적용돼요.[2] manifest를 지나치게 좁게 써 놓고 서버 JSON만 수정하는 방식으로는 범위를 확장할 수 없어요.

또한 Android 15 이상은 파일을 백그라운드에서 재검증하지만 변경 사항이 모든 기기에 반영되는 데 최대 7일이 걸릴 수 있다고 문서에 나와 있어요.[1] 개발 기기에서 즉시 바뀌어야 한다면 초기화·재검증 명령을 사용하고, 운영 사용자에게는 캐시 지연을 고려해 배포 일정을 잡아야 해요.

실패 유형별 복구 순서

  • 브라우저로만 열리면 manifest의 호스트와 assetlinks.json의 호스트를 먼저 맞춰요.
  • 파일이 301·302를 반환하면 최종 주소로 우회하지 말고 선언된 호스트에서 200 응답을 만들어요.
  • 서명 불일치라면 debug 키와 release 키, Play App Signing 키를 구분해 지문을 다시 넣어요.
  • 일부 경로만 실패하면 Android 15 동적 규칙과 manifest의 정적 경로 범위를 함께 봐요.
  • 상태가 오래 남아 있으면 기기 검증 상태를 초기화한 뒤 결과를 다시 읽어요.[3]

무선 ADB로 테스트 기기를 연결하는 과정이 막히면 Android 무선 디버깅의 pair·connect 점검 글을 참고할 수 있어요. 앱 로그인과 패스키 저장이 링크 테스트에 영향을 준다면 Credential Manager 구성 글도 함께 확인해 보세요.

서버 응답을 직접 확인하는 점검표

검증 실패를 앱 로그만으로 추측하지 않으려면 호스트별로 같은 요청을 반복해 기록하세요. 먼저 https://example.com/.well-known/assetlinks.json을 직접 요청하고, 응답 코드가 200인지, 리디렉션이 없는지, JSON 배열이 반환되는지 확인해요. 그다음 manifest에 적은 모든 host를 한 줄씩 비교하고, package_name과 release 인증서 지문을 배포된 앱과 맞춰요.[2][3]

여러 앱 변형을 동시에 설치하는 개발 환경에서는 debug와 release의 application ID 또는 서명 지문이 다를 수 있어요. 테스트용 파일에 release 지문만 넣고 debug 빌드를 검사하면 서버가 정상이어도 검증이 실패해요. 반대로 운영 도메인에 테스트 지문을 남기면 실제 설치본과 다른 앱이 연결될 수 있으므로 빌드 변형별 파일 관리 기준을 정해 두는 편이 안전해요.

Android 15 이상에서 동적 규칙을 쓴다면 “서버 파일을 수정했으니 바로 모든 휴대전화에 반영된다”라고 기대하지 마세요. 문서가 설명하는 재검증 주기와 캐시 지연을 고려해, 배포 직후에는 개발 기기에서 강제 재검증을 하고 운영 환경에서는 이전 규칙과 새 규칙이 함께 작동하는 기간을 두는 것이 좋아요.[1]

출처

  1. Verify App Links | Android Developers [1]
  2. Configure website associations and dynamic rules | Android Developers [2]
  3. Troubleshoot App Links | Android Developers [3]

함께 읽을 글