# 지연 딥링크

앱이 없는 사람이 링크를 열면 Mocco는 스토어로 보냅니다. **지연 딥링크**는 설치한 뒤 첫 실행에서 앱이 링크 화면을 열게 합니다. Mocco는 링크로 앱이 열릴 때마다 세기도 하므로, API로 읽는 링크 통계에 클릭 수와 함께 열림과 첫 실행이 잡힙니다.

이 문서는 앱이 보내는 호출을 설명합니다. React Native 앱에서는 [Mocco SDK](https://www.mocco.club/docs/ko/articles/ww6qzh-react-native)가 이 호출을 대신 보냅니다. SDK를 쓰지 않는 앱(네이티브 Android·iOS, Flutter)은 API를 직접 호출합니다.

## 시작하기 전에

- 링크 호스트와 앱 정보를 설정하고 링크가 앱을 열게 합니다([링크로 앱 열기](https://www.mocco.club/docs/ko/articles/ikc6rm-open-the-app) 참고).
- 프로젝트의 **API 키**에서 `links:app` 권한의 **퍼블리셔블** 키를 만듭니다([서버나 앱에서 링크 만들기](https://www.mocco.club/docs/ko/articles/wmyyuw-api) 참고). 앱에 넣어도 안전합니다.

## Android에서 동작하는 방식

1. 앱이 없는 사람이 Android 폰에서 링크를 누릅니다.
2. Mocco가 설치 리퍼러에 일회용 코드를 담아 Google Play로 보냅니다.
3. 설치한 뒤 앱이 첫 실행에서 설치 리퍼러를 한 번 읽어 Mocco에 보냅니다.
4. Mocco가 링크를 알려 주고, 앱이 그 화면을 엽니다.

코드는 한 번만 쓸 수 있습니다. 처음 보낸 설치가 링크를 받고, 같은 설치가 다시 물으면 같은 링크를 받습니다. 코드는 링크를 누르고 7일이 지나면 쓸 수 없습니다. 중간에 문제가 생기면 Mocco는 `none`을 돌려주고 앱은 평소처럼 열립니다.

링크로 앱이 열린 횟수는 두 플랫폼 모두 셉니다.

## iOS의 지연 링크

iOS에는 설치 리퍼러가 없고, Mocco는 IP 주소나 브라우저 정보를 맞춰 보며 누가 어떤 링크로 설치했는지 짐작하지 않습니다. 대신 사용자가 직접 코드를 설치 너머로 가져갑니다.

1. 앱이 없는 iPhone 사용자가 링크를 누릅니다. Mocco는 바로 App Store로 보내지 않고 짧은 페이지를 보여 줍니다. 페이지에는 **앱 받기**, **앱에서 열기**가 있고, 링크에 웹 대체 주소가 있으면 **웹에서 계속하기**도 있습니다.
2. **앱 받기**를 누르면 일회용 코드(`mocco:`와 영문·숫자 22자)가 클립보드에 복사된 뒤 App Store가 열립니다. 누르기 전에는 아무것도 복사하지 않고, 코드 말고는 복사하지 않습니다. 페이지에도 이렇게 안내합니다.
3. 첫 실행에서 앱이 클립보드를 한 번 읽습니다. 이 기능을 켰을 때만 읽습니다. 앱이 코드를 `token`으로 Mocco에 보내면, Mocco가 Android와 같은 방식으로 링크를 알려 줍니다.

**앱에서 열기**는 앱이 이미 있는데 링크를 눌렀을 때 앱 대신 Safari가 열린 사람을 위한 버튼입니다. 프로젝트의 두 번째 링크 호스트(`<이름>-open.<링크 도메인>`)에 있는 같은 링크로 가서 iOS가 앱을 열게 합니다. 앱이 없으면 바로 App Store로 갑니다.

이 페이지는 원래 App Store로 갈 iOS 사용자에게만 나옵니다. Safari, Chrome 같은 브라우저 모두 해당합니다. 크롤러는 그대로 공유 미리보기를 받고, iOS 웹 대체 주소이 있으면 그쪽이 먼저입니다. 링크를 바로 App Store로 보내려면 링크 설정에서 **iOS에서**를 **바로 App Store로**로 바꾸세요(API와 MCP에서는 `iosBehavior: "direct"`).

### 클립보드 읽기

클립보드를 읽으면 iOS가 붙여넣기 확인창("*앱 이름*에서 *Safari*의 내용을 붙여넣으려고 합니다")을 띄울 수 있습니다. 두 가지 방법이 있습니다.

- **첫 실행에서 읽기.** [React Native SDK](https://www.mocco.club/docs/ko/articles/ww6qzh-react-native)에서 `expo-clipboard`와 함께 `ios.readClipboardOnFirstLaunch`를 켭니다. SDK는 첫 실행에서 한 번만 읽고, Mocco 코드와 정확히 같지 않은 내용은 무시합니다. 클립보드에 쓰거나 지우지 않습니다.
- **사용자가 붙여넣게 하기.** 첫 화면에 expo-clipboard의 `ClipboardPasteButton`(iOS 16 이상)을 두고, 받은 내용을 `links.resolveFromPaste(text)`에 넘깁니다. 사용자가 직접 붙여넣기를 고른 것이라 확인창이 뜨지 않습니다.

SDK 없이 만들 때는 클립보드를 직접 읽고, 문자열 전체가 `^mocco:[A-Za-z0-9_-]{22}$`와 맞는지 확인한 뒤 `mocco:` 뒷부분을 `token`으로 보내세요. 접두사가 붙은 채로 보내도 Mocco가 받습니다.

### App Store 개인정보 라벨

앱이 클립보드를 읽는다면 App Store Connect의 개인정보 항목에 적으세요. Mocco 코드는 **제품 상호작용**에 관한 **사용 데이터**이고, 사용자 신원과 **연결되지 않으며** **추적에 쓰이지 않습니다**. 답하기 전에 앱의 다른 데이터 사용도 확인하세요.

### 복사한 사람 수

링크 통계에 **랜딩 페이지 복사**(`landingCopies`)가 나옵니다. **앱 받기**를 눌러 코드를 복사한 사람 수입니다. iOS 랜딩 페이지 조회 수(iOS 클릭)와 확인된 지연 링크(`deferredResolved`)와 함께 보면 설치까지 이어진 사람이 얼마나 되는지 알 수 있습니다.

## 모든 호출에 앱 정보를 보냅니다

호출마다 세 값을 담습니다.

- `installId`: 앱이 첫 실행에서 만들어 보관하는 임의 UUID입니다. 기기 식별자를 쓰지 마세요.
- `platform`: `ios` 또는 `android`
- `appVersion`: 앱 버전(예: `2.4.0`)

Mocco는 이 호출에서 폰의 IP 주소나 브라우저 정보를 저장하지 않습니다.

## 첫 실행에서: 지연 링크 확인하기

Android에서는 Google의 Install Referrer 라이브러리(`InstallReferrerClient`)로 첫 실행에서 설치 리퍼러를 **한 번** 읽습니다. Play는 이 값을 약 90일 동안 보관합니다. 문자열을 그대로 보냅니다.

```http
POST /v1/links/deferred/resolve
Authorization: Bearer <퍼블리셔블 키>
Content-Type: application/json

{
  "installId": "6f1c2c3e-0d1b-4c4a-9a51-6a2f7c1e9b10",
  "platform": "android",
  "appVersion": "2.4.0",
  "referrer": "utm_source=google-play&utm_medium=organic&mocco_click=..."
}
```

다른 파라미터가 섞여 있어도 Mocco가 문자열에서 코드를 찾습니다. 응답은 링크이거나

```json
{
  "status": "resolved",
  "link": {
    "id": "…",
    "url": "https://acme.<링크 도메인>/spring",
    "deepLinkPath": "/sale/spring",
    "params": { "promo": "SPRING" },
    "campaign": "spring-2026"
  }
}
```

`{ "status": "none" }`이며, 이때 앱은 평소처럼 열립니다. `deepLinkPath`가 가리키는 화면을 `params`와 함께 엽니다.

리퍼러가 없어도 설치마다 첫 실행에서 한 번 호출하세요. Mocco가 첫 실행으로 셉니다.

## 링크로 앱이 열렸을 때: 링크 뜻 묻기

링크가 앱을 열면 시스템은 화면이 아니라 짧은 링크(`https://acme.<링크 도메인>/spring`)를 넘겨줍니다. Mocco에 그 뜻을 묻습니다.

```http
POST /v1/links/open
Authorization: Bearer <퍼블리셔블 키>
Content-Type: application/json

{
  "installId": "6f1c2c3e-0d1b-4c4a-9a51-6a2f7c1e9b10",
  "platform": "ios",
  "appVersion": "2.4.0",
  "url": "https://acme.<링크 도메인>/spring"
}
```

응답 모양은 같습니다. 내 링크 도메인의 링크도 됩니다. 비활성화되었거나, 만료되었거나, 없거나, 다른 프로젝트의 링크면 `none`을 돌려주므로 앱은 대신 홈 화면을 열면 됩니다.

## 한도

주소 하나가 1분에 지연 링크 호출을 60번, 열림 호출을 120번 할 수 있습니다. 넘으면 Mocco가 `Retry-After`와 함께 `429`를 돌려줍니다.

## 숫자를 보는 곳

`GET /v1/links/{id}/stats`와 AI 어시스턴트가 Mocco MCP 도구로 읽는 통계에 클릭 수와 함께 **열림**(`opens`), **첫 실행**(`firstOpens`), **확인된 지연 링크**(`deferredResolved`), **iOS 랜딩 페이지 복사**(`landingCopies`)가 나옵니다. 콘솔의 통계 차트는 클릭 수를 보여 줍니다.
