CDK

임베디드 흐름

CDK는 단일 인터페이스 내에서 완전한 Verifiable Parental Consent (VPC) 흐름을 처리하는 End-to-End 위젯을 제공하며, 연령 게이트, VPC, 데이터 고지, 권한 및 기본 설정을 모두 하나의 원활한 경험으로 다룹니다.

네이티브 앱에서는 네이티브 UX를 권장합니다

네이티브 모바일 앱, 콘솔, 그 밖에 브라우저에서 실행되지 않는 환경에서는 사용자 정의 워크플로와 CDK UX 가이드라인에 따라 연령 게이트와 동의 UX를 네이티브로 구축할 것을 권장합니다. 플레이어에게 가장 원활하고 브랜드에 통합된 경험을 제공합니다.

end-to-end 및 연령 게이트 위젯도 이러한 환경에서 완전히 지원됩니다.

  • 모바일: 확인 URL에 사용되는 것과 동일한 시스템 브라우저 구성요소로 위젯 URL을 표시하고, redirectUrl 콜백으로 결과를 받으세요. 표시 방법은 모바일 앱 가이드를 참조하세요.
  • 콘솔: 위젯의 단축 URL을 QR 코드로 표시해 플레이어가 휴대폰에서 흐름을 완료하도록 하고, widgetId로 서버에서 결과를 가져오세요. 서버에서 결과 가져오기를 참조하세요.

End-to-End 위젯이란 무엇인가요?

End-to-End 위젯은 단일 인터페이스에서 완전한 규정 준수 흐름을 처리하는 포괄적인 솔루션으로, 연령 게이트, VPC, 데이터 고지, 권한 및 기본 설정을 모두 하나의 원활한 경험으로 다룹니다. 이 위젯은 부모가 자녀의 기기나 자신의 기기에서 사용할 수 있어 동의 프로세스에 최대한의 유연성을 제공합니다.

위젯 URL 생성

/widget/generate-e2e-url API를 호출하여 완전한 VPC 흐름을 처리하는 end-to-end 위젯 URL을 만듭니다. 이것은 사용자가 연령 수집 및 부모 동의 프로세스를 완료할 수 있는 고유 URL을 반환합니다.

요청 예시

POST /api/v1/widget/generate-e2e-url
Content-Type: application/json
Authorization: Bearer your-api-key

{
  "jurisdiction": "US-CA"
}

구성 플래그

선택적 flags 매개변수를 사용하면 건너뛸 흐름 부분을 사용자 정의할 수 있습니다:

  • skipDataNotices: 데이터 고지 및 동의 수집 건너뛰기
  • skipVerification: 확인 단계 건너뛰기
  • skipPermissions: 권한 관리 건너뛰기
  • skipPreferences: 기본 설정 건너뛰기

플랫폼 연령 신호 전달(선택 사항)

게임이 이미 플랫폼(Apple iOS, Google Play, Xbox, Meta Horizon 또는 이전 k-ID 확인)의 연령 데이터를 가지고 있다면, 요청 본문에 platformAgeSignal로 포함하세요. 위젯은 이 신호를 내부 연령 게이트 검사로 전달하므로, 확인된 신호가 성인을 나타낼 때 연령 게이트를 건너뛰고, 추가 확인 단계 없이 확인된 연령 권한을 충족하며, 신호와 플레이어가 자체 신고한 연령 사이의 충돌을 감지할 수 있습니다.

POST /api/v1/widget/generate-e2e-url
Content-Type: application/json
Authorization: Bearer your-api-key

{
  "jurisdiction": "US-CA",
  "platformAgeSignal": {
    "name": "apple-ios",
    "ageLow": 18,
    "ageHigh": 25,
    "declarationType": "governmentIDChecked"
  }
}

지원되는 플랫폼과 필드 형식은 플랫폼 연령 신호를 참조하세요.

응답 예시

{
  "url": "https://family.k-id.com/widget?token=eyJhbGciOiJFUzM4NCIs...",
  "shortUrl": "https://family.k-id.com/s/2f6c1d3e-8a4b-4c5d-9e6f-7a8b9c0d1e2f?pid=42&s=qr",
  "widgetId": "2f6c1d3e-8a4b-4c5d-9e6f-7a8b9c0d1e2f"
}
  • url: 포함하거나 열 때 사용하는 전체 위젯 URL입니다.
  • shortUrl: url로 리디렉션되는 단축 URL로, QR 코드에 적합합니다.
  • widgetId: 이 위젯 흐름의 ID입니다. DOM 이벤트나 리디렉션 없이 결과를 서버에서 받아야 한다면 플레이어와 연결해 저장하세요. 서버에서 결과 가져오기를 참조하세요.

위젯 URL 표시

위젯 URL은 호스팅된 웹 페이지입니다. 애플리케이션에 적합한 환경에서 열어주세요:

  • 웹사이트 또는 웹 앱: iframe에 포함(아래 예 참조), 팝업 창에서 열기, 또는 전체 페이지 리디렉션.
  • 모바일 앱: 가장 브랜드에 통합된 경험을 위해 사용자 정의 워크플로로 연령 게이트와 동의 UX를 네이티브로 구축할 것을 권장합니다. 위젯을 사용하려면 시스템 브라우저 구성요소(Android의 Custom Tabs, iOS의 ASWebAuthenticationSession)에서 위젯 URL을 열고, options.redirectUrl을 전달하여 앱으로 돌아오는 딥 링크로 결과를 받으세요. 표시 방법은 모바일 앱 빠른 시작을 참조하세요.
  • 콘솔 (Switch, PlayStation, Xbox): 사용자 정의 워크플로로 연령 게이트와 동의 UX를 네이티브로 구축하고, 보호자나 플레이어가 휴대폰에서 조치해야 할 때만 챌린지 URL을 QR 코드로 표시할 것을 권장합니다. 위젯을 사용하려면, 콘솔 브라우저는 일반적으로 제한되어 있거나 존재하지 않으므로 shortUrl을 QR 코드로 표시해 플레이어가 휴대폰에서 흐름을 완료하도록 하세요. 휴대폰에서 콘솔로 리디렉션할 수 없으므로 widgetId로 서버에서 결과를 가져오세요. 서버에서 결과 가져오기를 참조하세요.

위젯 내에서 사용 가능한 방법은 호스트와 관계없이 관할권 요구 사항에 자동으로 적응합니다.

웹 예시

<div id="vpc-container">
  <iframe 
    id="vpc-widget"
    src="WIDGET_URL" 
    width="100%" 
    height="600"
    frameborder="0"
    allow="camera;payment;publickey-credentials-get;publickey-credentials-create">
  </iframe>
</div>

모바일 환경의 경우, /widget/generate-e2e-url을 호출할 때 options.redirectUrl을 전달하고 앱에서 콜백을 처리하세요. 엔드 투 엔드 예시는 모바일 앱 빠른 시작 가이드를 참조하세요.

이벤트 처리

DOM 이벤트가 도달하는 호스트

아래의 JavaScript 이벤트(Widget.AgeGate.Result, Widget.AgeGate.Challenge, Widget.ExitReview)는 postMessage로 전달됩니다. 수신하려면 호스트에 위젯의 window에 대한 활성 JavaScript 리스너가 필요합니다. 여기에는 iframe 호스트, window.open으로 연 팝업 창, JS 브리지가 있는 모바일 WebView / WKWebView가 포함됩니다(모바일 앱 빠른 시작 가이드 참조). 시스템 브라우저 구성요소(ASWebAuthenticationSession, SFSafariViewController, Chrome Custom Tabs)와 전체 페이지 최상위 리디렉션은 리스너를 노출하지 않으므로, redirectUrl 콜백으로 결과를 받고 /session/get 또는 webhooks으로 서버 측에서 확인하세요.

연령 게이트 흐름이 완료되면 플레이어의 권한과 연령 상태를 저장하기 위해 세션이 생성됩니다. 흐름에 챌린지가 필요할 때마다 챌린지가 생성됩니다. 이는 확인 가능한 보호자 동의 또는 플레이어가 보호자 동의를 건너뛸 수 있는 연령을 신고한 경우의 Automatic age assurance를 위한 것입니다.

위젯은 수신할 수 있는 JavaScript 이벤트를 발생시킵니다. Widget.AgeGate.Result 이벤트를 수신하세요. 이 이벤트의 data.status는 흐름이 어떻게 종료되었는지를 나타냅니다:

  • PASS: 플레이어가 연령 게이트를 통과했으며 세션이 생성되었습니다. data.sessionId가 존재합니다.
  • FAIL: 필요한 챌린지가 통과되지 않았습니다(예: 신뢰할 수 있는 성인이 보호자 동의를 거부했거나 플레이어가 Automatic age assurance를 완료하지 않은 경우). 세션이 생성되지 않습니다.
  • PROHIBITED: 플레이어가 이 관할 구역에서 제품의 최소 연령에 미달하여 계속 진행할 수 없습니다. 세션이 생성되지 않으며, data.age에 입력된 연령이 포함됩니다. 플레이어의 계속 진행을 차단하세요.

흐름 중에 챌린지가 생성된 경우 이벤트에는 challengeId도 포함됩니다. 챌린지 관련 이벤트에 대한 자세한 내용은 Widget.AgeGate.Challenge를 참조하세요.

UI 닫기

위젯 UI를 언제 닫을지 결정하려면 Widget.ExitReview 이벤트를 수신하세요. 이 이벤트는 사용자가 '완료' 버튼을 클릭할 때 발생하며, 흐름이 완료되었고 iframe을 닫거나 숨겨야 함을 나타냅니다.

window.addEventListener('message', (event) => {
  if (!event.origin.endsWith('.k-id.com')) {
    return;
  }

  const message = event.data;

  if (message.eventType === 'Widget.AgeGate.Result') {
    if (message.data.status === 'PASS') {
      const sessionId = message.data.sessionId;

      // challengeId가 있으면 흐름 중에 챌린지가 해결된 것입니다
      // (예: 보호자 동의 또는 Automatic age assurance).
      if (message.data.challengeId) {
        console.log('Challenge resolved, session issued:', sessionId);
      } else {
        console.log('Session created (no challenge required):', sessionId);
      }

      grantAccess(sessionId);
    } else if (message.data.status === 'FAIL') {
      // 필요한 챌린지가 통과되지 않았습니다(예: 보호자 동의가 거부되었거나
      // Automatic age assurance가 완료되지 않음). 세션 없음.
      console.log('Age gate not passed');
      restrictAccess();
    } else if (message.data.status === 'PROHIBITED') {
      // 플레이어가 제품의 최소 연령에 미달합니다.
      // 세션이 생성되지 않습니다. 계속 진행을 차단합니다.
      console.log('Player below minimum age:', message.data.age);
      restrictAccess();
    }
  }

  // 필요한 경우 챌린지 관련 이벤트 처리
  if (message.eventType === 'Widget.AgeGate.Challenge') {
    if (message.data.status === 'FAIL') {
      // 보호자가 동의를 거부. 액세스를 제한
      console.log('Consent denied');
      restrictAccess();
    }
  }

  if (message.eventType === 'Widget.ExitReview') {
    // 사용자가 '완료'를 클릭할 때 위젯 UI를 닫습니다
    closeWidget();
  }
});

리디렉션 URL로 결과 받기

시스템 브라우저 서피스(ASWebAuthenticationSession, SFSafariViewController, Chrome Custom Tabs)와 전체 페이지 최상위 리디렉션은 DOM 리스너를 노출할 수 없으므로, 대신 redirectUrl 콜백으로 연령 게이트 결과를 받습니다. /widget/generate-e2e-url 또는 /widget/generate-age-gate-url를 호출할 때 options.redirectUrl을 전달하세요. 이 URL은 http(s)와 사용자 정의 스키마 모바일 딥링크(예: myapp://age-gate/return)를 지원합니다.

연령 게이트 흐름이 완료되면 k-ID는 Widget.AgeGate.Result 이벤트가 담고 있는 것과 동일한 결과 필드를 쿼리 매개변수로 추가하여 플레이어를 해당 URL로 이동시킵니다. 리디렉션 URL의 기존 쿼리 매개변수는 유지됩니다.

매개변수설명
statusPASS, FAIL, 또는 PROHIBITED(아래 참조)
sessionId새로 생성된 세션. status가 PASS인 경우에만 존재합니다.
challengeId흐름에 관여한 챌린지(생성된 경우).
age플레이어가 입력한 연령. status가 PROHIBITED인 경우 존재합니다.

status 값은 DOM 이벤트와 일치합니다:

  • PASS: 플레이어가 연령 게이트를 통과했으며 세션이 생성되었습니다. sessionId로 액세스를 허용합니다.
  • FAIL: 필요한 챌린지가 통과되지 않았습니다(예: 보호자 동의가 거부되었거나 Automatic age assurance가 완료되지 않은 경우). 세션이 생성되지 않습니다.
  • PROHIBITED: 플레이어가 이 관할 구역에서 제품의 최소 연령에 미달합니다. 세션이 생성되지 않습니다. 플레이어의 계속 진행을 차단하세요.

예를 들어, 최소 연령에 미달하는 플레이어는 다음 URL로 이동합니다:

myapp://age-gate/return?status=PROHIBITED&age=8

리디렉션 쿼리 매개변수는 신뢰할 수 있는 결과가 아니라 UX 힌트로 취급하세요. 액세스를 허용하기 전에 Challenge.StateChange webhook 또는 /session/get를 통해 서버 측에서 결과를 확인하세요.

서버에서 결과 가져오기

콘솔에 표시된 QR 코드를 스캔한 휴대폰처럼 플레이어가 다른 기기에서 위젯을 완료하면, DOM 이벤트도 리디렉션도 애플리케이션에 도달하지 않습니다. 대신 서버가 k-ID에서 결과를 가져옵니다. 위젯 상태 폴링, webhook, 또는 둘 다를 사용하며, /widget/generate-e2e-url과 /widget/generate-age-gate-url이 반환하는 widgetId를 키로 사용합니다. 다른 위젯 엔드포인트는 widgetId를 반환하지 않습니다.

콘솔에서는 네이티브 UX를 권장합니다

사용자 정의 워크플로로 연령 게이트와 동의 UX를 네이티브로 구축하고, 챌린지가 필요할 때만 챌린지 URL을 QR 코드로 표시해 휴대폰으로 넘길 것을 권장합니다. 아래 위젯 흐름도 콘솔에서 작동하지만, 플레이어가 흐름 전체를 휴대폰에서 진행하게 됩니다.

  1. 서버에서 위젯 URL을 생성하고, QR 코드를 표시하기 전에 widgetId를 플레이어와 연결해 저장하세요. shortUrl은 불투명한 값으로 취급하고 반환된 그대로 인코딩하세요.
  2. GET /widget/get-status(한 번에 최대 100개 위젯은 POST /widget/get-status)를 폴링하거나 webhook으로 결과를 가져오세요. webhook으로 결과 받기를 참조하세요.
  3. PASS이면 반환된 sessionId로 /session/get을 호출해 세션을 가져오고, 결과를 자체적으로 저장하세요. k-ID는 위젯 상태를 일정 기간만 보관하며, 그 이후에는 NOT_FOUND를 반환합니다.
상태의미조치
PENDING플레이어가 아직 연령 게이트를 완료하지 않았습니다. expiresAt은 URL이 만료되는 시점입니다.QR 코드를 계속 표시하고 몇 초마다 폴링하세요.
PASS시도가 통과했으며 sessionId가 설정되어 있습니다. 위젯이 한 번 PASS를 보고하면 이후에도 계속 PASS를 보고합니다.세션을 가져오고 플레이어를 들여보내세요.
IN_CHALLENGE보호자 동의 같은 챌린지가 아직 완료되지 않았습니다. challengeType과 challengeStatus를 확인하세요.누가 조치해야 하는지 플레이어에게 알려주세요. 보호자는 며칠이 걸릴 수 있으므로 폴링 간격을 늘리거나 webhook을 활용하세요.
FAILED보호자가 거부하는 등의 이유로 챌린지가 실패했습니다.플레이어를 들여보내지 마세요.
PROHIBITED플레이어가 진행할 수 없습니다. prohibitedReason은 BELOW_MINIMUM_AGE 또는 JURISDICTION_UNAVAILABLE입니다.플레이어를 들여보내지 마세요.
EXPIRED시도하기 전에 URL이 만료되었거나, 더 이상 챌린지를 진행할 수 없습니다.폴링을 중단하세요.

흐름이 종료되면(통과, 금지, 실패, 만료) 어떤 기기에서든 같은 QR 코드로 흐름을 다시 시작할 수 없습니다. 연령을 잘못 입력한 경우처럼 플레이어가 다시 시도하게 하려면 새 위젯을 생성하세요. widgetId는 shortUrl의 일부이므로 비밀 값은 아니지만, 그 상태는 생성한 프로덕트만 읽을 수 있습니다. 다른 프로덕트에는 NOT_FOUND가 반환됩니다.

webhook으로 결과 받기

폴링 대신 챌린지 결과를 푸시로 받으려면 Challenge.StateChange를 구독하세요. 위젯에서 시작된 챌린지에는 widgetId가 포함됩니다. 가장 흔한 결과인 챌린지 없는 통과와 금지 결과에는 webhook이 전송되지 않으므로, 이 둘은 계속 폴링으로 확인하세요.

위젯 결과의 기준 정보는 /widget/get-status입니다. webhook도 같은 결과를 보고하지만 최소 1회 전송 방식이라 누락될 수 있으므로, webhook을 받지 못했다면 폴링을 대체 수단으로 사용하세요.

Safe Start로 통과한 후에는 보호자가 나중에 동의를 거부하더라도 위젯은 계속 PASS를 보고합니다. 동의 챌린지에 대해서는 FAIL인 Challenge.StateChange를 받지만 Safe Start 세션은 활성 상태로 유지되며, 동의 상태는 challengeStatus로 확인할 수 있습니다.

플랫폼 연령 신호

연령 입력 화면을 건너뛸 수 있는 것은 검증된 Apple iOS 또는 Google Play 신호뿐입니다. Xbox와 Meta Horizon 신호는 오류 없이 접수되지만, 플레이어는 휴대폰에서 연령을 입력합니다.

위젯이 처리하는 항목

위젯은 자동으로 다음을 처리합니다:

  • 연령 수집: 관할권에 적절한 연령 수집 방법
  • 데이터 고지: Compliance Studio의 제품 구성에 따라 수락할 데이터 고지
  • 권한: Compliance Studio의 제품 구성에 따라 관리할 권한
  • 부모 동의 Challenge: 사용자가 미성년자로 판단되는 경우 신뢰할 수 있는 성인 승인을 위한 Challenge가 생성됩니다
  • Automatic age assurance: 제품이 해당 관할권에 Automatic age assurance가 활성화된 경우, 신고된 연령이 부모 동의를 건너뛸 만큼 높은 플레이어는 세션이 발급되기 전에 위젯 내에서 신고한 연령을 증명하도록 요청받습니다(얼굴 연령 추정 또는 ID 문서).

특정 흐름은 관할권과 Compliance Studio의 제품 구성에 따라 다릅니다.

Automatic age assurance를 사용한 세션 생성 타이밍

Automatic age assurance가 엔드 투 엔드 위젯 내에서 트리거되면, 세션은 연령 게이트 직후가 아니라 플레이어가 검증을 통과한 후에 생성됩니다. 흐름이 완료되면 Widget.AgeGate.Result 이벤트가 여전히 발생하며 PASS인 경우 sessionId가 포함됩니다. 이벤트가 도착할 때까지 흐름을 진행 중으로 간주하세요.

VPC 구현에 대한 자세한 내용은 빠른 시작 가이드를 참조하세요.

On this page