네이티브 모바일 앱을 위한 딥링크: 라우트, 토큰, 앱에서 열기
네이티브 모바일 앱의 딥링크를 학습하세요: 라우트를 설계하고 “앱에서 열기” 동작을 정의하며 Kotlin과 SwiftUI에서 비밀을 URL에 노출하지 않고 안전하게 토큰을 전달하는 방법.

딥링크가 해야 할 일 — 쉽게 말하면
휴대폰에서 누군가 링크를 탭하면 한 가지 기대가 있습니다: 즉시 올바른 장소로 이동한다는 것. 대충 맞는 곳이 아니라, 검색창만 있는 홈 화면이나 목적을 잊은 로그인 화면이 아니라.
좋은 딥링크 경험은 다음과 같습니다:
- 앱이 설치되어 있으면 링크가 가리키는 정확한 화면을 엽니다.
- 앱이 설치되어 있지 않더라도 탭은 여전히 도움이 됩니다(예: 웹 대체 페이지나 앱 스토어 페이지를 열고 설치 후 동일한 목적지로 돌아오게 함).
- 사용자가 로그인해야 한다면 한 번 로그인하면 의도한 화면으로 도착합니다. 앱의 기본 시작 화면으로 돌아가면 안 됩니다.
- 링크에 수행할 작업(초대 수락, 주문 보기, 이메일 확인 등)이 포함되어 있다면 그 작업은 명확하고 안전해야 합니다.
대부분의 좌절감은 “어느 정도 작동하는” 링크에서 옵니다. 사용자는 잘못된 화면을 보거나 하던 일을 잃거나 반복 루프에 갇히기도 합니다: 링크 탭 → 로그인 → 대시보드 도착 → 링크 다시 탭 → 다시 로그인. 단 한 번의 추가 단계만 있어도 특히 초대나 비밀번호 재설정 같은 일회성 작업에서 사용자가 포기할 수 있습니다.
Kotlin이나 SwiftUI 코드를 작성하기 전에 링크가 의미하는 바를 결정하세요. 외부에서 어떤 화면을 열 수 있나? 앱이 닫힌 상태와 이미 실행 중인 상태에서 무엇이 달라지나? 사용자가 로그아웃 상태면 어떻게 해야 하나?
이런 기획이 나중의 많은 고통을 예방합니다: 명확한 라우트, 예측 가능한 “앱에서 열기” 동작, 그리고 비밀을 URL에 직접 넣지 않고 일회성 코드를 안전하게 전달하는 방법.
딥링크 유형과 “앱에서 열기”가 잘못되는 경우
앱을 여는 모든 링크가 동일하게 동작하는 것은 아닙니다. 서로 바꿔 쓰면 고전적인 실패가 발생합니다: 잘못된 곳을 열거나, 브라우저를 열거나, 한 플랫폼에서만 작동하는 등.
세 가지 일반 범주:
- 커스텀 스킴(예: myapp: 같은 앱 전용 스킴). 설정은 쉽지만 많은 앱과 브라우저가 이를 주의해서 처리합니다.
- Universal Links(iOS) 및 App Links(Android). 일반 웹 링크를 사용해 앱이 설치되어 있으면 앱을 열고, 그렇지 않으면 웹사이트로 대체됩니다.
- 인앱 브라우저 링크. 이메일 앱이나 메신저의 내장 브라우저에서 열린 링크는 종종 Safari나 Chrome과 다르게 동작합니다.
어디에서 탭했느냐에 따라 “앱에서 열기”의 의미가 달라질 수 있습니다. Safari에서 탭한 링크는 바로 앱으로 점프할 수 있습니다. 같은 링크라도 이메일이나 메신저에서 탭하면 먼저 내장 웹뷰가 열리고 사용자가 추가로 “앱에서 열기” 버튼을 눌러야 할 수 있습니다. Android에서는 Chrome이 App Links를 존중할 수 있지만 소셜 앱의 인앱 브라우저는 이를 무시할 수 있습니다.
콜드 스타트(Cold start)와 앱이 이미 실행 중인 상태는 다음의 함정입니다.
- 콜드 스타트: OS가 앱을 시작하고 앱이 초기화된 뒤에 딥링크를 전달합니다. 스플래시 화면을 보여주거나 인증을 확인하거나 원격 설정을 불러오면 링크가 사라질 수 있으므로, 링크를 저장해 두었다가 앱이 준비되면 재생해야 합니다.
- 이미 실행 중: 사용자가 세션 중일 때 링크를 받습니다. 네비게이션 스택이 존재하므로 동일한 목적지를 처리하는 방식이 달라질 수 있습니다(화면을 푸시할지 스택을 리셋할지 등).
간단한 예: Telegram에서 탭한 초대 링크는 종종 먼저 인앱 브라우저에서 열립니다. 앱이 OS가 항상 직접 전달한다고 가정하면 사용자는 웹 페이지를 보고 링크가 깨졌다고 생각할 수 있습니다. 이런 환경을 미리 계획하면 플랫폼별 접착 코드를 덜 작성하게 됩니다.
구현 전에 라우트를 설계하세요
대부분의 딥링크 버그는 Kotlin이나 SwiftUI 문제라기보다 기획 문제입니다. 링크가 하나의 화면에 깔끔하게 매핑되지 않거나 너무 많은 "아마도" 옵션을 담고 있으면 문제가 생깁니다.
사람들이 앱을 어떻게 생각하는지에 맞는 일관된 라우트 패턴 하나로 시작하세요: 목록, 상세, 설정, 결제, 초대. 읽기 쉽고 안정적으로 유지하세요. 이메일, QR 코드, 웹 페이지에서 재사용하게 될 것입니다.
간단한 라우트 집합 예시:
- 홈
- 주문 목록과 주문 상세(orderId)
- 계정 설정
- 초대 수락(inviteId)
- 검색(query, tab)
그다음 파라미터를 정의하세요:
- 단일 객체에는 ID를 사용하세요(orderId).
- UI 상태에는 선택적 파라미터(tab, filter)를 사용하세요.
- 모든 링크에 하나의 최선의 목적지가 있도록 기본값을 정하세요.
또한 링크가 잘못되었을 때(데이터 누락, 유효하지 않은 ID, 접근 권한 없음) 무엇이 일어날지 결정하세요. 가장 안전한 기본은 근접한 안정적인 화면(예: 목록)을 열고 짧은 메시지를 보여주는 것입니다. 빈 화면이나 문맥 없는 로그인 화면으로 사용자를 내모는 것을 피하세요.
출처별로도 계획하세요. QR 코드는 빠르게 열리고 연결이 불안정해도 견딜 수 있는 짧은 라우트를 필요로 합니다. 이메일 링크는 더 길어도 되고 추가 맥락을 포함할 수 있습니다. 웹 페이지 링크는 앱이 설치되어 있지 않을 때도 우아하게 약화되어야 합니다: 앱이 없으면 다음에 무엇을 해야 할지 설명하는 위치로 가게 하세요.
백엔드 주도 접근(예: AppMaster 같은 플랫폼으로 API 엔드포인트와 화면을 생성)을 사용하면 이 라우트 계획은 공유 계약이 됩니다: 앱은 어디로 갈지 알고 백엔드는 어떤 ID와 상태가 유효한지 압니다.
URL에 비밀을 넣지 않고 안전하게 토큰 전달하기
딥링크는 종종 안전한 봉투처럼 취급됩니다. 실제로는 그렇지 않습니다. URL에 있는 모든 것은 브라우저 기록, 스크린샷, 공유 미리보기, 분석 로그 등에 남을 수 있습니다.
URL에 비밀을 넣지 마세요. 여기에는 장기 액세스 토큰, 리프레시 토큰, 비밀번호, 개인 데이터 등 링크가 전달될 경우 누군가가 사용자를 가장할 수 있게 하는 모든 것이 포함됩니다.
더 안전한 패턴은 짧게 유효한 일회성 코드입니다. 링크에는 그 코드만 포함하고, 앱이 열리면 실제 세션으로 교환합니다. 누군가 링크를 훔쳐도 그 코드는 1~2분 후 또는 첫 번째 교환 이후 무효가 되어야 합니다.
단순한 전달 플로우:
- 링크에는 세션 토큰이 아니라 일회성 코드가 포함됩니다.
- 앱이 열리면 백엔드에 그 코드를 교환(redeem)하러 호출합니다.
- 백엔드는 만료 여부와 사용 기록을 확인한 후 사용 처리합니다.
- 백엔드는 앱에 정상 인증 세션을 반환합니다.
- 앱은 교환이 끝나면 코드 정보를 메모리에서 제거합니다.
교환 후에도 민감한 작업을 수행하기 전에 앱 내에서 신원 확인을 다시 하세요. 링크가 결제 승인, 이메일 변경, 데이터 내보내기 같은 민감한 작업을 위한 것이라면 생체인증이나 최근 재로그인 같은 빠른 재확인을 요구하세요.
결과로 얻은 세션은 안전하게 저장하세요. iOS에서는 일반적으로 Keychain, Android에서는 Keystore 기반 저장소를 사용합니다. 필요한 것만 저장하고 로그아웃, 계정 삭제, 의심스러운 재사용 감지 시 지우세요.
구체적인 예: 워크스페이스 초대 링크를 보냅니다. 링크에는 10분 만료되는 일회성 초대 코드가 들어 있습니다. 앱이 코드를 교환하고, 어떤 워크스페이스에 가입하는지 명확히 보여준 뒤 사용자 확인을 받고 최종 가입을 진행합니다.
AppMaster로 빌드하는 경우 이는 코드를 교환하고 세션을 반환하는 엔드포인트와 모바일 UI의 확인 단계로 깔끔하게 매핑됩니다.
인증과 “이어서 하기”
딥링크는 종종 개인 데이터가 있는 화면을 가리킵니다. 먼저 어떤 화면을 누구나 열 수 있게 할지(공개)와 어떤 화면을 로그인 세션이 있어야 열 수 있게 할지(보호됨)를 결정하세요. 이 한 가지 결정이 대부분의 “테스트에서는 작동했음” 놀라움을 막습니다.
간단한 규칙: 먼저 안전한 랜딩 상태로 이동한 뒤 사용자가 인증된 것을 확인하면 보호된 화면으로 이동하세요.
공개 vs 보호 화면 결정하기
딥링크는 잘못된 사람에게 전달될 수 있다고 가정하세요.
- 공개: 마케팅 페이지, 도움말 문서, 비밀번호 재설정 시작, 초대 수락 시작(아직 데이터가 표시되지 않음)
- 보호됨: 주문 상세, 메시지, 계정 설정, 관리자 화면
- 혼합형: 미리보기 화면은 괜찮지만 로그인 전에는 민감한 정보 대신 자리표시자만 보여주세요
로그인 후 이어서 정확한 위치로 돌아가기
신뢰할 수 있는 방법은: 링크를 파싱해 의도한 목적지를 저장한 뒤 인증 상태에 따라 라우팅하는 것입니다.
예: 로그아웃 상태에서 특정 지원 티켓 링크를 탭한 사용자는 앱이 중립 화면으로 열리고 로그인하라는 요청을 받은 뒤 자동으로 해당 티켓으로 이동해야 합니다.
이를 견고하게 유지하려면 로컬에 작은 “복귀 대상”(라우트 이름과 티켓 ID 등)을 짧은 만료 시간과 함께 저장하세요. 로그인 완료 후 한 번 읽고 이동한 뒤 지우세요. 로그인에 실패하거나 대상이 만료되면 안전한 홈 화면으로 폴백하세요.
예외 상황도 정중하게 처리하세요:
- 세션 만료: 짧은 메시지를 보여주고 재인증 후 계속.
- 접근 권한 취소: 목적지 셸을 열고 “더 이상 접근 권한이 없습니다”라는 메시지와 안전한 다음 단계 제안.
또한 잠금 화면 미리보기, 앱 전환 화면 스크린샷, 알림 미리보기에 민감한 데이터를 표시하지 마세요. 데이터가 로드되고 세션이 확인될 때까지 민감한 화면은 빈 상태로 두세요.
커스텀 네비게이션 스파게티를 피하는 라우팅 방식
화면마다 URL을 각자 파싱하면 딥링크는 엉망이 됩니다. 그러면 작은 결정들(무엇이 선택적이고 필수인지, 무엇이 유효한지)이 앱 전반에 흩어지고 안전하게 변경하기 어려워집니다.
라우팅을 공용 배관처럼 다루세요. 하나의 라우트 테이블과 하나의 파서만 두고 UI는 깔끔한 입력을 받게 하세요.
하나의 공유 라우트 테이블 사용
iOS와 Android가 하나의 사람 친화적인 라우트 목록에 동의하게 하세요. 이를 계약으로 생각하세요.
각 라우트는 다음에 매핑됩니다:
- 화면
- 작은 입력 모델
예: “주문 상세”는 Order 화면으로, 입력은 OrderRouteInput(id) 형태입니다. 리퍼런스 소스 같은 추가 값이 필요하면 그 값들은 뷰 코드 여기저기에 흩어놓지 말고 입력 모델에 넣으세요.
파싱과 검증을 중앙화하기
파싱, 디코딩, 검증을 한 곳에 두세요. UI는 “토큰이 있는가?”나 “ID가 유효한가?”를 묻지 않아야 합니다. UI는 유효한 라우트 입력이나 명확한 오류 상태만 받도록 하세요.
실용적인 흐름:
- URL을 수신(탭, 스캔, 공유 시트)
- 알려진 라우트로 파싱
- 필수 필드와 허용 형식 검증
- 화면 대상과 입력 모델 생성
- 단일 진입점을 통해 네비게이트
“알 수 없는 링크” 폴백 화면을 추가하세요. 단절된 화면이 아니라 유용하게 만드세요: 열 수 없었던 항목을 보여주고, 간단한 평문으로 이유를 설명하며 홈으로 가기, 검색, 로그인 같은 다음 행동을 제안하세요.
단계별: 딥링크와 “앱에서 열기” 동작 설계하기
좋은 딥링크는 가장 좋은 의미에서 지루하게 느껴집니다. 사용자는 탭하면 항상 올바른 화면으로 도착합니다. 앱이 설치되어 있든 아니든.
1단계: 중요한 진입점 선택하기
실제로 사람들이 사용하는 상위 10개 링크 유형을 나열하세요: 초대, 비밀번호 재설정, 주문 영수증, 티켓 보기, 프로모션 링크 등. 의도적으로 적게 유지하세요.
2단계: 패턴을 계약처럼 작성하기
각 진입점에 대해 하나의 정규 패턴과 올바른 화면을 열기 위한 최소 데이터를 정의하세요. 이름보다 안정적인 ID를 선호하세요. 필수와 선택을 결정하세요.
도움이 되는 규칙:
- 라우트당 목적 하나(초대, 재설정, 영수증).
- 필수 파라미터는 항상 포함, 선택 파라미터는 안전한 기본값 가짐.
- iOS(SwiftUI)와 Android(Kotlin)에서 같은 패턴 사용.
- 변경 가능성을 예상하면 버전 접두사(v1) 같은 간단한 방식 예약.
- 파라미터가 없을 때의 동작 정의(오류 화면 표시 등).
3단계: 로그인 동작과 포스트 로그인 대상 결정
링크 유형별로 로그인이 필요한지 문서화하세요. 필요하면 대상 경로를 기억하고 로그인 후 계속하세요.
예: 영수증 링크는 로그인 없이 미리보기를 허용할 수 있지만, “영수증 다운로드”는 로그인을 요구하고 로그인 후 동일 영수증으로 돌아가야 합니다.
4단계: 토큰 전달 규칙 정하기(비밀을 URL에 두지 않기)
링크에 일회성 토큰이 필요하면 유효 기간과 사용 방식(한 번만 사용 가능 등)을 정의하세요.
실용적 접근: URL에 짧은 일회성 코드를 담고, 앱이 이를 백엔드와 교환해 정상 세션을 발급받게 합니다.
5단계: 세 가지 실제 상태에서 테스트하기
딥링크는 가장자리에서 깨집니다. 각 링크 유형을 다음 상황에서 테스트하세요:
- 콜드 스타트(앱이 닫힘)
- 웜 스타트(앱이 메모리에 있음)
- 앱 미설치(링크가 여전히 합리적인 위치로 안내)
라우트, 인증 검사, 토큰 교환 규칙을 한곳에 두면 Kotlin과 SwiftUI 화면 전반에 커스텀 라우팅 로직이 흩어지는 것을 피할 수 있습니다.
딥링크를 망치는 일반적인 실수(및 방지법)
딥링크 실패는 지루한 이유에서 옵니다: 작은 가정 하나, 이름 바뀐 화면, 또는 ‘임시’ 토큰이 여기저기 남는 것 등.
실제에서 보는 실패 사례(및 해결책)
-
액세스 토큰을 URL에 넣어 로그로 유출. 쿼리 문자열은 복사되거나 공유되고 브라우저 기록과 분석·크래시 로그에 남습니다. 해결: 링크에는 짧은 일회성 코드만 넣고 앱 내에서 교환해 빨리 만료시키세요.
-
앱이 설치되어 있다고 가정(폴백 없음). 링크가 오류 페이지로 가거나 아무 동작을 하지 않으면 사용자가 포기합니다. 해결: 앱이 없을 때 무엇을 해야 할지 설명하고 설치 경로를 제공하는 웹 폴백을 만드세요. “앱에서 열어 계속하세요” 같은 간단한 페이지라도 침묵보다 낫습니다.
-
한 기기에서 여러 계정을 처리하지 않음. 잘못된 사용자로 화면이 열리는 것은 링크가 깨지는 것보다 더 나쁩니다. 해결: 링크 수신 시 활성 계정을 확인하고 사용자에게 확인 또는 계정 전환을 요청하세요. 특정 워크스페이스가 필요하면(비밀이 아닌) 워크스페이스 식별자를 포함해 검증하세요.
-
화면이나 라우트를 변경해 링크 깨짐. 라우트가 UI 이름에 묶여 있으면 탭 이름만 바뀌어도 옛 링크가 죽습니다. 해결: 초대, 티켓, 주문 같은 의도 기반의 안정적인 라우트를 설계하고 이전 버전도 유지하세요.
-
문제가 생겼을 때 추적 불가. 무슨 일이 일어났는지 재생할 방법이 없으면 지원팀은 추측만 하게 됩니다. 해결: 민감하지 않은 요청 ID를 링크에 포함시키고 서버와 앱에 로그를 남기며, 오류 메시지에 그 ID를 보여줘 문제를 재현할 수 있게 하세요.
현실 점검: 그룹 채팅에서 보낸 초대 링크를 생각해 보세요. 누군가가 업무용 폰에서 열었고 두 개 계정이 있으며, 태블릿에는 앱이 설치되어 있지 않고 링크가 동료에게 전달되었습니다. 링크가 일회성 초대 코드만 포함하고 폴백 동작을 제공하며 올바른 계정을 확인하고 요청 ID를 기록하면, 그 하나의 링크로도 모든 상황에서 성공할 수 있습니다.
예시: 항상 올바른 화면을 여는 초대 링크
초대는 전형적인 딥링크입니다: 매니저가 팀원에게 메신저로 링크를 보내고 수신자는 탭 한 번으로 초대 화면에 도착하기를 기대합니다.
시나리오: 매니저가 “Support Team” 워크스페이스에 새 지원 요원을 초대합니다. 에이전트가 Telegram에서 초대를 탭합니다.
앱이 설치되어 있으면 시스템은 앱을 열어 초대 세부 정보를 전달해야 합니다. 앱이 설치되어 있지 않으면 사용자는 초대 목적을 설명하고 설치 경로를 제공하는 간단한 웹 페이지로 이동해야 합니다. 설치 후 첫 실행에서 앱은 여전히 초대 흐름을 완료할 수 있어야 하므로 사용자가 링크를 다시 찾지 않아도 됩니다.
앱 내부에서 Kotlin과 SwiftUI 모두 흐름은 동일합니다:
- 들어온 링크에서 초대 코드를 읽습니다.
- 사용자가 로그인했는지 확인합니다.
- 백엔드에서 초대를 검증한 뒤 올바른 화면으로 라우트합니다.
검증이 핵심입니다. 링크에는 장기 세션 토큰 같은 비밀을 담지 마세요. 짧게 유효한 초대 코드만 담아 서버에서 검증한 뒤 사용 가능하게 해야 합니다.
사용자 경험은 예측 가능하게 느껴져야 합니다:
- 로그인하지 않음: 로그인 화면을 보고 로그인 후 초대 수락 화면으로 돌아갑니다.
- 로그인함: “워크스페이스 가입” 확인을 보고 올바른 워크스페이스로 이동합니다.
초대가 만료되었거나 이미 사용된 경우에는 빈 오류 화면으로 내모는 대신 명확한 메시지와 다음 단계(새 초대 요청, 계정 전환, 관리자에게 문의)를 제안하세요. “이 초대는 이미 수락되었습니다”는 “유효하지 않은 토큰”보다 낫습니다.
빠른 체크리스트 및 다음 단계
딥링크는 콜드 스타트와 웜 스타트, 로그인 상태에서 모두 동일하게 동작할 때 ‘완성된’ 것처럼 느껴집니다.
빠른 체크리스트
출시 전에 실제 기기와 OS 버전에서 각각 테스트하세요:
- 링크가 콜드 스타트와 웜 스타트에서 모두 올바른 화면을 여는가.
- URL에 민감한 정보가 없는가. 토큰이 꼭 필요하면 짧게 유효하고 가능하면 일회성으로 하는가.
- 알 수 없거나 만료되었거나 이미 사용된 링크는 명확한 화면과 안전한 다음 행동을 제공하는가.
- 이메일 앱, 브라우저, QR 코드 스캐너, 메신저 미리보기 등에서 작동하는가(일부는 링크를 미리 열기도 함).
- 로깅으로 무슨 일이 일어났는지(링크 수신, 라우트 파싱, 인증 필요 여부, 성공 또는 실패 이유)를 알 수 있는가.
동작을 검증하는 간단한 방법은 초대, 비밀번호 재설정, 주문 상세, 지원 티켓, 프로모션 같은 필수 링크 몇 개를 골라 같은 테스트 흐름으로 실행해 보는 것입니다: 이메일에서 탭, 채팅에서 탭, QR 코드 스캔, 재설치 후 열기 등.
다음 단계(유지보수성 유지)
딥링크 관련 코드가 화면 전반에 퍼지기 시작하면 라우팅과 인증을 화면별 코드가 아닌 공용 배관으로 다루세요. 라우트 파싱을 한곳에 중앙화하고 모든 목적지는 원시 URL이 아니라 정리된 파라미터를 받도록 만드세요. 인증도 마찬가지로 “지금 계속할지” vs “먼저 로그인, 그다음 계속”을 결정하는 한 개의 게이트로 처리하세요.
커스텀 접착 코드를 줄이고 싶다면 백엔드, 인증, 모바일 앱을 함께 설계하는 것이 도움이 됩니다. AppMaster (appmaster.io)는 프로덕션 수준의 백엔드와 네이티브 모바일 앱을 생성하는 노코드 플랫폼으로, 라우트 이름과 일회성 코드 교환 엔드포인트를 요구사항 변경에 맞춰 일치시키기 쉽게 해 줍니다.
다음 주에 단 한 가지를 한다면 이것을 하세요: 정해진 표준 라우트와 각 실패 케이스에 대한 정확한 폴백 동작을 문서화한 뒤, 그 규칙을 단일 라우팅 레이어에서 구현하세요.
자주 묻는 질문
딥링크는 링크가 가리키는 정확한 화면을 열어야 하며, 일반적인 홈 화면이나 대시보드로 보내면 안 됩니다. 앱이 설치되어 있지 않다면 사용자가 설치 후 동일한 목적지로 돌아올 수 있도록 안내하거나 합리적인 대체 페이지에 안내해야 합니다.
iOS의 Universal Links와 Android의 App Links는 일반 웹 URL을 사용해 앱이 설치되어 있으면 앱을 열고, 설치되어 있지 않으면 웹으로 우아하게 대체됩니다. 커스텀 스킴은 설정이 쉬우나 브라우저나 일부 앱에서 일관되게 처리되지 않을 수 있어 보조 옵션으로 사용하는 것이 좋습니다.
많은 이메일·메신저 앱은 자체 내장 브라우저(인앱 브라우저)에서 링크를 열며, 이 환경은 Safari나 Chrome과 다르게 동작해 OS에 직접 전달되지 않을 수 있습니다. 웹 대체 페이지를 명확하게 만들고 사용자가 웹 페이지에서 추가 동작(예: ‘앱에서 열기’)을 해야 하는 경우를 대비하세요.
콜드 스타트 상태에서는 앱이 스플래시 화면을 보이거나 초기화 작업을 먼저 수행하기 때문에 딥링크가 사라질 수 있습니다. 들어오는 링크 목적지를 즉시 저장해 두고 초기화가 끝난 뒤 재생(리플레이)해 내비게이션을 수행하세요.
URL에 장기 액세스 토큰, 리프레시 토큰, 비밀번호, 개인 데이터 같은 민감한 정보를 넣지 마세요. URL은 브라우저 기록, 스크린샷, 공유, 분석 로그 등에 남을 수 있습니다. 대신 짧게 유효한 일회성 코드를 링크에 담고, 앱이 열리면 백엔드와 교환해 세션을 발급받게 하세요.
링크를 파싱해 의도된 목적지를 저장한 뒤 인증 상태에 따라 라우팅하세요. 로그인 과정이 끝나면 사용자가 원래 의도한 화면으로 이동하도록 하되, 저장하는 “복귀 대상”은 작고 유효기간을 짧게 두고 한 번 사용하면 지우세요.
라우트를 공유된 계약으로 보고 파싱과 검증을 한 곳에 중앙화하세요. 화면들에 원시 URL을 전달하지 말고, UI는 이미 검증된 깔끔한 입력만 받게 하면 각 화면에서 저마다 규칙을 만들며 복잡해지는 일을 막을 수 있습니다.
앱이 받은 링크가 암시하는 워크스페이스나 테넌트와 현재 활성 계정이 일치하는지 확인한 뒤, 일치하지 않으면 사용자에게 확인하거나 계정 전환을 요청하세요. 잘못된 계정으로 민감한 내용을 열어주는 것보다 짧은 확인 절차를 거치는 편이 낫습니다.
유효하지 않거나 만료되었거나 데이터가 부족한 링크는 가장 근접한 안정적인 화면(예: 리스트 페이지)으로 보내고, 무엇을 열 수 없었는지 짧게 설명하세요. 빈 화면이나 문맥 없는 로그인 화면으로 사용자를 내버려 두지 마세요.
중요한 링크 타입을 실제 기기에서 세 가지 상태로 테스트하세요: 앱이 닫힌 상태, 앱이 이미 실행 중인 상태, 앱이 설치되지 않은 상태. 이메일, 채팅 앱, QR 스캐너 등 실제 출처에서 테스트하세요. AppMaster로 빌드한다면 백엔드와 네이티브 앱 간 라우트 이름과 일회성 코드 교환 엔드포인트를 일치시켜 커스텀 접착 코드를 줄일 수 있습니다.


