---
type: how-to
title: 모바일 앱 연동
summary: iOS, Android, React Native, 또는 브라우저 iframe WebView에 위젯을 임베드하고, 검증된 토큰을 당신의 코드로 연결하세요.
---

Caputchin은 호스팅된 임베드 페이지를 통해 네이티브 앱 안에서 돌아갑니다. WebView를 그 페이지로 향하게 하면, 방문자가 그 안에서 챌린지를 풀고, 페이지는 그 결과 토큰(그리고 모든 오류)을 당신의 네이티브 코드로 연결합니다. 검증 자체는 웹과 동일하며, 호스트 셸만 다릅니다.

## 1. 임베드 페이지 열기

WebView를 최소한 사이트 키를 담아 `https://caputchin.com/embed`로 향하게 하세요:

```
https://caputchin.com/embed?sitekey=cpt_pub_...&game=caputchin/games/leaf-memory
```

순수 체크박스를 원하면 게임 파라미터를 생략하세요. `games=a,b` 목록은 세션마다 하나를 무작위로 고릅니다.

<Callout type="warn">
사이트 키가 게임 게이트를 켰다면(그 **검증에 게임 요구** 설정), 순수 체크박스는 통과하지 못합니다: 게이트된 키에서 임베드는 게임을 실행해야 합니다. `game`(또는 `games`) 파라미터를 넘기세요; 게임 파라미터 없는 맨 체크박스 임베드는 거기서 `gate-unavailable` 오류를 일으킵니다. 이것은 의도된 것으로, 게이트가 Caputchin 쪽에서 강제되기 때문입니다. [게임 게이트](/docs/site-keys/games)를 보세요.
</Callout>

### 쿼리 파라미터

| 파라미터 | 필수 | 효과 |
|---|---|---|
| `sitekey` | 예 | 당신의 공개 키(`cpt_pub_...`). 없으면 위젯 없는 오류 셸을 렌더링합니다. |
| `game` | 아니요 | 단일 마켓플레이스 게임 id(`owner/repo`, 또는 `owner/repo/leaf`). |
| `games` | 아니요 | 쉼표로 구분된 목록; 세션마다 하나를 무작위로 고릅니다. |
| `game-src` | 아니요 | 자체 호스팅 게임을 위한, 당신이 직접 호스팅한 게임 번들의 URL. |
| `layout` | 아니요 | `inline`, `modal`, `fullscreen`, 또는 `auto`. 임베드에서는 기본값이 `inline`. |
| `overlay-width` / `overlay-height` | 아니요 | `modal` / `fullscreen`에서 게임 대화 상자의 크기를 정합니다: `full`, 픽셀 수, 또는 `auto`(게임 자체가 선호하는 크기). [크기와 레이아웃](/docs/references/widget-sizing) 참고. |
| `locale` / `skin` / `config` | 아니요 | 언어, 스킨, 또는 구성을 고르도록 위젯에 전달됩니다. |
| `no-verify` | 아니요 | 검증 게이트 없이 게임을 실행합니다(게임만, 토큰 없음). |
| `postMessageTarget` | 아니요 | 브라우저 iframe 임베더 전용(아래 참고). 네이티브 WebView에서는 무시됩니다. |

순수 체크박스를 원하면 `game` / `games` / `game-src` 셋을 모두 생략하세요. `mode` 파라미터는 없습니다; 위젯이 트리거를 스스로 몰아갑니다.

## 2. 토큰을 당신의 코드로 연결하기

방문자가 통과하면, 임베드 페이지는 당신의 호스트에 맞는 채널을 통해 토큰을 밀어 보냅니다. 맞는 핸들러를 등록하세요.

### iOS (WKWebView, Swift)

토큰은 `caputchin`이라는 메시지 핸들러에 날 문자열로 도착합니다:

```swift
class TokenHandler: NSObject, WKScriptMessageHandler {
  func userContentController(_ controller: WKUserContentController,
                             didReceive message: WKScriptMessage) {
    guard let token = message.body as? String else { return }
    // send `token` to your backend
  }
}

let config = WKWebViewConfiguration()
config.userContentController.add(TokenHandler(), name: "caputchin")
let webView = WKWebView(frame: .zero, configuration: config)
webView.load(URLRequest(url: URL(string: "https://caputchin.com/embed?sitekey=cpt_pub_...&game=caputchin/games/leaf-memory")!))
```

### Android (WebView, Kotlin)

토큰은 `caputchin`이라는 `@JavascriptInterface`에 날 문자열로 도착합니다:

```kotlin
class TokenBridge {
  @JavascriptInterface
  fun onToken(token: String) {
    // send `token` to your backend
  }
}

webView.settings.javaScriptEnabled = true
webView.addJavascriptInterface(TokenBridge(), "caputchin")
webView.loadUrl("https://caputchin.com/embed?sitekey=cpt_pub_...&game=caputchin/games/leaf-memory")
```

### React Native (react-native-webview)

임베드는 단일 React Native 채널에 JSON 문자열을 post합니다. 그것을 파싱해 `type`으로 분기하세요:

```jsx
<WebView
  source={{ uri: "https://caputchin.com/embed?sitekey=cpt_pub_...&game=caputchin/games/leaf-memory" }}
  onMessage={(e) => {
    const msg = JSON.parse(e.nativeEvent.data);
    if (msg.type === "caputchin-token") {
      // send msg.token to your backend
    } else if (msg.type === "caputchin-error") {
      // msg.code, msg.message, msg.originalCode (optional)
    }
  }}
/>
```

### 브라우저 iframe (postMessage)

페이지를 네이티브 WebView가 아니라 브라우저 `<iframe>`에 임베드한다면, 부모 릴레이는 **기본적으로 꺼져 있으며** `?postMessageTarget=<origin>`을 통해 정확한 대상 오리진으로 옵트인할 때만 발동합니다(예: `postMessageTarget=https://app.example.com`). 와일드카드(`*`), 경로, 또는 맨 스킴·호스트·포트로 파싱되지 않는 무엇이든 거부되니, 일회용이며 사이트에 묶인 토큰이 검증되지 않은 부모로 브로드캐스트되는 일은 결코 없습니다. 부모에서 수신 대기하고, 메시지를 신뢰하기 전에 `event.origin`을 확인하세요:

```js
window.addEventListener("message", (event) => {
  if (event.origin !== "https://embed-host.example.com") return;
  const msg = event.data;
  if (msg.type === "caputchin-token") {
    // msg.token
  } else if (msg.type === "caputchin-error") {
    // msg.code, msg.message
  }
});
```

## 3. 백엔드에서 검증하기

이것은 웹과 같습니다: 토큰을 당신의 시크릿과 함께 `/siteverify`로 POST하세요. [서버 측 연동](/docs/integration-guides/verify-on-your-backend)을 보세요.

## 오류 받기 (선택)

위젯이 오류에 부딪힐 때마다, 임베드 페이지는 그것을 병렬 채널 묶음을 통해 그대로 비춥니다: iOS `caputchinError` 메시지 핸들러, Android `caputchinError` 인터페이스(`onError(json)`), `{ type: "caputchin-error", ... }`로서의 React Native 채널, 그리고 같은 게이트된 iframe 릴레이. 페이로드는 `{ code, message, originalCode? }`를 담습니다. iOS와 Android는 그것을 JSON 문자열로 받고; React Native와 iframe 릴레이는 타입 지정된 봉투를 받습니다.

이것은 전적으로 선택입니다. 오류 핸들러를 등록하지 않는 앱은 그저 오류를 받지 않으며, 임베드 페이지는 어쨌든 이미 WebView 안에서 재시도 UI를 보여 줍니다.

## 함께 보기

- 토큰 확인은 [서버 측 연동](/docs/integration-guides/verify-on-your-backend).
- 웹에서의 같은 것은 [클라이언트 측 연동](/docs/integration-guides/cdn).
