--- url: /api-sdk/sdk/kakaotalk-gameplay.md description: 카카오톡 게임웹뷰 환경의 공통 스펙과 window.kakaotalkGamePlay JavaScript 인터페이스 --- # 게임웹뷰 SDK 카카오톡 안에서 실행되는 **게임웹뷰**의 개발 스펙과 `window.kakaotalkGamePlay` JavaScript 인터페이스를 정리합니다. ::: info 이 문서를 읽는 두 경우 이 문서는 `window.kakaotalkGamePlay` 를 **직접 호출하는** 개별 SDK 연동의 기준 문서입니다. [게임플레이 JS SDK](/api-sdk/sdk/gameplay-js/)를 쓰는 경우에도 기능별 카카오톡 최소 지원 버전은 이 문서가 기준이므로 함께 봅니다. 다만 두 방식을 **동시에 구성하는 것은 권장하지 않습니다.** 같은 객체가 두 번 올라가 초기화 순서에 따라 설정이 덮이거나 콜백이 중복 호출될 수 있습니다. 자세한 내용은 [SDK 연동 방식 선택](/api-sdk/sdk/)을 참고하세요. ::: ## 용어 정의 문서에서 사용하는 두 웹뷰 개념을 구분합니다. | 용어 | 정의 | | --- | --- | | **게임웹뷰** | 게임플레이 전용 커스텀 웹뷰.파트너사 게임이 실제 실행되는 곳입니다.주소창·헤더가 없고, `window.kakaotalkGamePlay` 객체가 주입됩니다.**싱글턴**으로 여러 개 동시 실행이나 게임웹뷰 내에서 다시 게임웹뷰를 여는 것은 불가능합니다. | | **인앱브라우저** | 주소창·헤더가 있는 일반 카카오톡 인앱브라우저입니다.게임웹뷰 안에서 별도 외부 URL을 잠시 띄웠다가 다시 게임으로 돌아와야 할 때 사용합니다.예: [게임 중 추가 동의](/api-sdk/kakaosync/rest-api#게임-중-추가-동의). | ## 실행 조건 게임플레이 플랫폼에 파트너사별 게임 도메인이 등록되어 있어야 해당 도메인이 게임웹뷰로 실행됩니다. 게임 URL은 게임플레이 파트너센터의 게임 정보에 입력합니다. 게임웹뷰 실행에 필요한 샌드박스·운영·테스트 도메인(화이트리스트)은 카카오 담당자에게 등록을 요청하고 등록 상태를 확인합니다. > **관련 가이드**: [게임웹뷰](/docs/webview/overview), [게임 실행 흐름](/docs/webview/launch-flow), [웹뷰 컴포넌트 가이드](/docs/design/webview-component-guide), [게임웹뷰 테스트 방법](/docs/webview/test-guide) ## 공통 스펙 ### User-Agent 식별값 게임웹뷰 진입 시 User-Agent에 `PFCUSTOM` 구분값이 포함됩니다. * **Android**: `Mozilla/5.0 (Linux; Android 11; ...; wv) AppleWebKit/537.36 ... Mobile Safari/537.36;PFCUSTOM;KAKAOTALK 2410350` * **iOS**: `Mozilla/5.0 (iPhone; CPU iPhone OS 18_6 like Mac OS X) AppleWebKit/... Mobile/15E148 KAKAOTALK/26.2.0 (PFCUSTOM)` `PFCUSTOM`과 `KAKAOTALK` 두 문자열이 함께 포함되어 있는지 확인해 게임웹뷰 여부를 판단합니다. ### 지원 스펙 | 구분 | 내용 | | --- | --- | | 화면 구성 | 상단 내비게이션 없음. 네트워크 에러나 미허용 도메인 접근 시에만 클라이언트 내비게이션이 노출됩니다 | | 호출 방식 | 링크(URL) 또는 Custom 스킴: `kakaotalk://pfCustomWebView/open?url={인코딩된 URL}` | | 닫기 스킴 | `kakaotalk://web/close` | | 인앱브라우저 열기 | `kakaotalk://inappbrowser?url={인코딩된 URL}` | | 다크모드 | 파트너사가 웹에서 직접 구현 (네이티브는 OS 설정을 따름) | | 로딩바 | 파트너사가 웹에서 직접 구현 | | 최소 지원 버전 | 카카오톡 v26.3.0 이상. 파트너사별 입점 시점에 따라 상이할 수 있음 | ### 인앱브라우저 스킴 게임웹뷰 진입, 웹뷰 종료, 인앱브라우저 호출에 사용하는 기본 스킴입니다. | 목적 | 스킴 | | --- | --- | | 게임웹뷰 열기 | `kakaotalk://pfCustomWebView/open?url={인코딩된 URL}` | | 게임웹뷰 닫기 | `kakaotalk://web/close` | | 인앱브라우저 열기 | `kakaotalk://inappbrowser?url={인코딩된 URL}` | `kakaotalk://pfCustomWebView/open` 은 **게임웹뷰 진입 전용**입니다. 게임 실행 도메인 외의 URL에 대해서는 동작을 보장하지 않습니다. 이 표에 적힌 스킴 외의 사용은 지원하지 않습니다. `kakaotalk://web` 에는 여기에 적지 않은 다른 경로도 있지만, 지원 범위는 이 문서에 기재된 스킴으로 한정합니다. ::: warning 게임 실행 도메인은 인앱브라우저로 열리지 않습니다 카카오톡은 게임웹뷰를 **게임 실행 도메인당 하나만** 유지합니다. 그래서 게임웹뷰가 떠 있는 상태에서 같은 도메인의 주소를 `kakaotalk://inappbrowser` 로 열면 인앱브라우저가 새로 뜨지 않고 이미 떠 있는 게임웹뷰가 그 주소로 이동합니다. **경로를 나눠도 해소되지 않습니다.** 판정 기준이 경로가 아니라 도메인이라 `game.partner.com/support` 처럼 하위 경로로 두면 같은 결과가 됩니다. 고객센터·이용약관처럼 인앱브라우저로 띄울 페이지는 게임 실행 도메인과 **다른 도메인**에 두세요. 이 제약은 스킴을 직접 호출할 때뿐 아니라 [게임플레이 JS SDK](/api-sdk/sdk/gameplay-js/)의 [더보기 메뉴](/api-sdk/sdk/gameplay-js/ui#더보기-메뉴)에서 외부 링크를 여는 경우에도 같습니다. ::: 게임 중 추가 OAuth 동의 등을 위해 별도 웹뷰를 여닫는 상세 흐름은 [카카오싱크 API: 게임 중 추가 동의](/api-sdk/kakaosync/rest-api#게임-중-추가-동의)를 참고하세요. ### 다크모드 대응 최초 로드 페이지의 ``에 `color-scheme`을 정의합니다. ```html ``` ### 에러 처리 네트워크 오류나 미허용 도메인에 접근하면 클라이언트가 상단 내비게이션 바를 자동 노출합니다. * 상단 타이틀: "불러올 수 없는 페이지" * 닫기 컨펌 팝업: "현재 페이지를 종료하시겠어요?" (취소 / 확인) ## JavaScript 인터페이스 게임웹뷰에서 동작하는 SDK 인터페이스입니다. `window.kakaotalkGamePlay` 객체를 통해 호출하며, 모든 메서드는 Promise(async/await) 방식으로 동작합니다. ### API 목록 지원 버전은 카카오톡 앱 기준입니다. `26.3.0` 은 게임웹뷰가 최초 배포된 버전이며, 이후 도입된 API는 도입 시점의 카카오톡 버전을 표기합니다. | 메서드 | 설명 | 반환값 | 플랫폼 | 지원 버전 | | --- | --- | --- | --- | --- | | `close()` | 웹뷰 닫기 | `Promise` | Android / iOS | 26.3.0+ | | `haptic(options)` | 햅틱(진동) 피드백 | `Promise` | Android / iOS | 26.3.0+ | | `getSoundState()` | 디바이스 볼륨 조회 | `Promise<{volume: number}>` | Android / iOS | 26.3.0+ | | `setOrientation(options)` | 화면 방향 제어 | `Promise<{value: string}>` | Android / iOS | 26.3.0+ | | `setBackSwipeEnabled(enabled)` | 뒤로가기 스와이프 제어. 26.8.0 부터 게임웹뷰 기본값은 비활성화 | `Promise` | iOS | 26.3.0+ | | `window.onBackClick = () => boolean` | Android Back 버튼 override | `boolean` | Android | 26.3.0+ | | `registerNativeHandler(name)` | UPDATE 이벤트 콜백 등록 | `Promise` | Android / iOS | 26.3.0+ | | `resetWebViewHistory(url)` | URL 이동 + 히스토리 초기화 | `Promise` | Android / iOS | 26.5.0+ | | `setStatusBarOverlay(options)` | 상태바 영역까지 확장 | `Promise` | Android / iOS | 26.5.0+ | | `getNetworkState()` | 네트워크 상태 조회 | `Promise<{isWifi, networkType}>` | Android / iOS | 26.5.0+ | | `getSafeArea()` | Safe Area Insets 조회 | `Promise<{top, bottom, left, right}>` | Android / iOS | 26.5.0+ | | `startAccelerometer(options)` | 가속도 센서 수집 시작 | `Promise` | Android / iOS | 26.5.0+ | | `stopAccelerometer()` | 가속도 센서 수집 종료 | `Promise` | Android / iOS | 26.5.0+ | | `changeStatusBarColor(color)` | 상태바 색상 변경 | `Promise` | Android / iOS | 26.6.0+ | | `keepBrowser()` | 웹뷰를 닫지 않고 플로팅으로 최소화 | `Promise` | iOS | 26.6.0+ | | `setScrollEnabled(enabled)` | 웹뷰 스크롤/바운스 제어 | `Promise` | Android / iOS | 26.7.0+ | | `setBouncesEnabled(enabled)` | 오버스크롤 바운스만 개별 제어 | `Promise` | iOS | 26.7.0+ | | `setPullToRefreshEnabled(enabled)` | 당겨서 새로고침 제어 | `Promise` | iOS | 26.7.0+ | | `setLinkPreviewEnabled(enabled)` | 링크 롱프레스 미리보기 제어 | `Promise` | iOS | 26.7.0+ | ## 주요 메서드 상세 ### close() 게임웹뷰를 닫습니다. 가로 모드에서 호출하면 자동으로 세로 모드로 복원한 뒤 닫힙니다. ```ts await window.kakaotalkGamePlay.close(); ``` ### haptic(options) 디바이스 햅틱(진동) 피드백을 실행합니다. 내장 모드 6종과 커스텀 패턴을 지원합니다. | mode | 설명 | | --- | --- | | `impact_light` | 가벼운 임팩트 | | `impact_medium` | 보통 임팩트 | | `impact_heavy` | 강한 임팩트 | | `success` | 성공 알림 | | `warning` | 경고 알림 | | `error` | 에러 알림 | | `custom` | 커스텀 패턴 (`duration`, `intensity` 배열 필요) | ```ts type HapticMode = 'impact_light' | 'impact_medium' | 'impact_heavy' | 'success' | 'warning' | 'error' | 'custom'; interface HapticOptions { mode: HapticMode; duration?: number[]; // ms, custom 전용 intensity?: number[]; // 0~255, custom 전용 } // 내장 모드 await window.kakaotalkGamePlay.haptic({mode: 'impact_heavy'}); await window.kakaotalkGamePlay.haptic({mode: 'success'}); // 커스텀 패턴 await window.kakaotalkGamePlay.haptic({ mode: 'custom', duration: [100, 50, 200], intensity: [128, 0, 255], }); ``` ### getSoundState() 현재 디바이스의 볼륨 상태(0.0 ~ 1.0)를 조회합니다. ```ts const {volume}: {volume: number} = await window.kakaotalkGamePlay.getSoundState(); ``` ### setOrientation(options) 화면 방향을 제어합니다. | mode | 설명 | | --- | --- | | `portrait` | 세로 모드로 전환 | | `landscape` | 가로 모드로 전환 | | `current` | 현재 방향 조회 | ```ts type OrientationMode = 'portrait' | 'landscape' | 'current'; interface OrientationResult { value: 'portrait' | 'landscape'; } await window.kakaotalkGamePlay.setOrientation({mode: 'landscape'}); await window.kakaotalkGamePlay.setOrientation({mode: 'portrait'}); const {value}: OrientationResult = await window.kakaotalkGamePlay.setOrientation({mode: 'current'}); ``` ::: warning 주의사항 카카오톡 Android의 `targetSdk 36` (Android 16) 상향에 따라, **대화면 기기(폴더블·태블릿 등 smallest width 600dp 이상) + Android 16 이상**에서는 OS가 앱의 화면 방향 고정 요청을 무시합니다. 이 환경에서는 다음이 보장되지 않습니다. * `setOrientation({mode: 'portrait'})` / `{mode: 'landscape'}` 를 호출해도 실제 화면 방향이 고정되지 않으며, 기기 회전에 따라 자유롭게 전환될 수 있습니다. * 반환값(`value`)은 요청한 mode를 반영하지만, 실제 적용 여부는 보장하지 않습니다. * `close()` 호출 시 가로→세로 자동 복원 동작도 동일하게 무시될 수 있습니다. **영향 범위**: 대화면 기기(폴더블·태블릿) + Android 16 이상. 일반 스마트폰과 Android 15 이하 기기에서는 기존대로 정상 동작합니다. **권장 대응**: 특정 방향에 의존하는 게임은 세로·가로 양쪽 모두에서 레이아웃이 깨지지 않도록 반응형으로 구현하고, 방향 전환 시 진행 중인 게임 상태·데이터가 유실되지 않도록 처리하세요. ::: ### setBackSwipeEnabled(enabled) iOS 뒤로가기 스와이프 제스처를 활성화·비활성화합니다. 게임 중 실수로 뒤로가기 되는 것을 방지할 때 사용합니다. **카카오톡 26.8.0 부터 게임웹뷰는 이 제스처가 꺼진 상태로 생성됩니다.** 게임 진입 시 비활성화를 위해 이 메서드를 호출할 필요가 없고, 뒤로가기 스와이프가 필요한 게임만 `setBackSwipeEnabled(true)` 로 명시적으로 켜면 됩니다. ```ts // 뒤로가기 스와이프가 필요한 경우: 명시적으로 활성화 await window.kakaotalkGamePlay.setBackSwipeEnabled(true); // 다시 비활성화 await window.kakaotalkGamePlay.setBackSwipeEnabled(false); // 시그니처 declare function setBackSwipeEnabled(enabled: boolean): Promise; ``` ::: warning 주의사항 카카오톡 26.8.0 미만에서는 게임웹뷰가 이 제스처를 켠 상태로 생성됩니다. iOS 는 제스처가 켜진 웹뷰에 내부 제스처 인식기를 설치하고, 이후 `setBackSwipeEnabled(false)` 로 값을 꺼도 그 인식기를 제거하지 않습니다. 그래서 뒤로가기 동작 자체는 막히지만, 화면 가장자리 근처의 터치가 인식기에 선점되어 게임까지 전달되지 않는 현상이 남습니다. 이 현상은 웹에서 우회할 수 없습니다. 가장자리 터치가 조작에 중요한 게임이라면 26.8.0 미만 기기를 고려해 조작 영역을 가장자리에서 띄워 배치하세요. 26.8.0 이상에서는 제스처 인식기 자체가 설치되지 않으므로 별도 대응 없이 해소됩니다. Android 에서는 재현되지 않는 iOS 한정 현상입니다. ::: ### window.onBackClick (Android Back 처리) Android에서는 시스템 Back 입력이 발생하면 네이티브가 `window.onBackClick && window.onBackClick()`을 평가합니다. 반환값에 따라 처리 방식이 달라집니다. | 반환값 | 동작 | | --- | --- | | `true` | 웹에서 Back 이벤트를 처리한 것으로 간주. 앱의 기본 `goBack()`을 실행하지 않음 | | `false` | 웹이 처리하지 않은 것으로 간주. 앱의 기본 `goBack()` 실행 | ```ts declare global { interface Window { onBackClick?: () => boolean; } } window.onBackClick = (): boolean => { if (shouldCloseGameLayer()) { closeGameLayer(); return true; } return false; }; ``` 콜백이 없거나 boolean이 아닌 값을 반환하면 `false`로 처리됩니다. iOS 뒤로가기 스와이프는 `setBackSwipeEnabled()`를 사용하세요. ### setScrollEnabled(enabled) 웹뷰 자체의 스크롤과 바운스(오버스크롤) 제스처를 활성화·비활성화합니다. 연타형 게임처럼 웹뷰 스크롤 제스처가 터치 입력을 선점하거나, 의도치 않은 스크롤·바운스가 발생하는 것을 막을 때 사용합니다. | 값 | 동작 | | --- | --- | | `false` | 웹뷰 스크롤과 오버스크롤을 비활성화합니다. `touchstart`, `touchmove`, `touchend` 이벤트는 웹으로 전달되므로 인게임 드래그·스와이프에는 영향이 없습니다. | | `true` | 기본 동작인 스크롤 허용 상태로 복원합니다. | 플랫폼별 동작: | 플랫폼 | 동작 | | --- | --- | | iOS | `WKWebView`의 `UIScrollView scrollEnabled` / `bounces`를 비활성화합니다. | | Android | 오버스크롤(바운스/글로우)을 제거하고 웹뷰 스크롤 이동을 억제합니다. 터치 이벤트는 웹으로 그대로 전달됩니다. | ```ts // 게임 진입: 스크롤/바운스 비활성화 await window.kakaotalkGamePlay.setScrollEnabled(false); // 게임 종료: 스크롤 다시 활성화 await window.kakaotalkGamePlay.setScrollEnabled(true); // 시그니처 declare function setScrollEnabled(enabled: boolean): Promise; ``` ### setBouncesEnabled(enabled) 스크롤은 유지한 채, 스크롤 끝에서 튕기는 바운스(오버스크롤) 효과만 개별적으로 제어합니다. 스크롤은 필요하지만 바운스만 없애고 싶을 때 사용합니다. | 값 | 동작 | | --- | --- | | `false` | 바운스(오버스크롤) 효과를 비활성화합니다. | | `true` | 기본 동작인 바운스 허용 상태로 복원합니다. | ```ts // 바운스만 비활성화 (스크롤은 유지) await window.kakaotalkGamePlay.setBouncesEnabled(false); // 바운스 다시 활성화 await window.kakaotalkGamePlay.setBouncesEnabled(true); // 시그니처 declare function setBouncesEnabled(enabled: boolean): Promise; ``` `setScrollEnabled(false)`로 스크롤 자체를 끄면 바운스도 함께 사라집니다. 스크롤은 살리고 바운스만 끄려는 경우 `setBouncesEnabled()`를 사용하세요. ### setPullToRefreshEnabled(enabled) 당겨서 새로고침(Pull-to-Refresh) 제스처를 활성화·비활성화합니다. 게임 플레이 중 아래로 당겨 새로고침이 트리거되는 것을 방지할 때 사용합니다. | 값 | 동작 | | --- | --- | | `false` | 당겨서 새로고침을 비활성화합니다. 진행 중이던 새로고침은 즉시 종료됩니다. | | `true` | 당겨서 새로고침을 활성화합니다. | ```ts // 게임 진입: 당겨서 새로고침 비활성화 await window.kakaotalkGamePlay.setPullToRefreshEnabled(false); // 게임 종료: 당겨서 새로고침 다시 활성화 await window.kakaotalkGamePlay.setPullToRefreshEnabled(true); // 시그니처 declare function setPullToRefreshEnabled(enabled: boolean): Promise; ``` ### setLinkPreviewEnabled(enabled) 링크를 길게 눌렀을 때 나타나는 미리보기(Haptic Touch / 3D Touch link preview)를 활성화·비활성화합니다. 게임 중 링크 롱프레스로 미리보기 팝업이 뜨는 것을 막을 때 사용합니다. | 값 | 동작 | | --- | --- | | `false` | 링크 롱프레스 미리보기를 비활성화합니다. | | `true` | 링크 롱프레스 미리보기를 활성화합니다. | ```ts // 링크 롱프레스 미리보기 비활성화 await window.kakaotalkGamePlay.setLinkPreviewEnabled(false); // 링크 롱프레스 미리보기 다시 활성화 await window.kakaotalkGamePlay.setLinkPreviewEnabled(true); // 시그니처 declare function setLinkPreviewEnabled(enabled: boolean): Promise; ``` ### resetWebViewHistory(url) 전달한 URL로 이동하고 기존 back/forward 히스토리를 초기화합니다. 게임 결과 페이지로 전환하면서 뒤로가기로 게임 페이지에 돌아가지 않도록 할 때 사용합니다. ```ts await window.kakaotalkGamePlay.resetWebViewHistory('https://gameplay.kakao.com/game/result'); // 시그니처 declare function resetWebViewHistory(url: string): Promise; ``` 결과 페이지를 먼저 로드한 뒤 다시 호출할 필요 없이 이 호출 하나로 URL 이동과 히스토리 초기화가 함께 처리됩니다. ### setStatusBarOverlay(options) 웹뷰를 상태바 영역까지 확장하거나 원래 레이아웃으로 되돌립니다. | 필드 | 타입 | 설명 | | --- | --- | --- | | `enable` | `boolean` | `true`: 상태바 영역까지 확장, `false`: 기본 레이아웃 복원 | * `enable: true`: 웹뷰 컨테이너가 상태바까지 확장되고 네이티브 내비게이션 바는 강제로 숨겨진 상태가 유지됩니다. * `enable: false`: 기본 레이아웃 복원. 내비게이션 바 자동 토글 규칙이 다시 적용됩니다. * 레이아웃 변경에 따른 새 인셋은 `SAFE_AREA` UPDATE 이벤트로 자동 전송됩니다. ```ts interface StatusBarOverlayOptions { enable: boolean; } // 게임 진입: 상태바까지 확장 await window.kakaotalkGamePlay.setStatusBarOverlay({enable: true}); // 게임 종료: 원래 레이아웃 복원 await window.kakaotalkGamePlay.setStatusBarOverlay({enable: false}); ``` 에러 케이스: * `enable` 파라미터 누락 → `"enable parameter is required"` * 미지원 환경 → `"setStatusBarOverlay is not supported"` 오버레이는 네이티브 웹뷰가 소유하는 레이아웃 속성이라 **페이지를 이동해도 유지됩니다.** 게임이 외부 URL 로 리디렉션하는 흐름이 있다면 [상태바 오버레이 설정 방법](/docs/webview/status-bar-overlay)을 함께 확인하세요. ### changeStatusBarColor(color?) 상태바(status bar) 색상을 변경합니다. 게임 화면 테마에 맞춰 배경색을 바꾸거나 화면 전환 시 원래 색상으로 되돌릴 때 사용합니다. * `color` 에 색상 문자열(예: `"#RRGGBB"`)을 전달하면 해당 색상으로 상태바가 변경됩니다. * `color` 를 비우거나(`""`) 생략하면 호스트(카카오톡)의 **기본 상태바 색상으로 복원**됩니다. * 색상 파싱·폴백 및 상태바 아이콘 명암(밝은/어두운 아이콘) 조정은 네이티브가 자동으로 처리합니다. | 필드 | 타입 | 설명 | | --- | --- | --- | | `color` | `string` | 적용할 색상 문자열(예: `"#RRGGBB"`). 비우거나 생략 시 기본 색상 복원 | ```ts // 시그니처 declare function changeStatusBarColor(color?: string): Promise; // 지정한 색상으로 변경 await window.kakaotalkGamePlay.changeStatusBarColor('#FF6B00'); // 기본 상태바 색상으로 복원 await window.kakaotalkGamePlay.changeStatusBarColor(); ``` 에러 케이스: * 미지원 환경 → `"changeStatusBarColor not supported in this context"` ### registerNativeHandler(handlerName)와 UPDATE 이벤트 네이티브가 웹으로 전달하는 UPDATE 이벤트를 수신할 콜백을 등록합니다. **UPDATE 이벤트 종류** | type | payload | 발생 시점 | | --- | --- | --- | | `LIFECYCLE` | `{state: 'FOREGROUND' \| 'BACKGROUND'}` | 앱이 포그라운드·백그라운드로 전환될 때 | | `SAFE_AREA` | `{top, bottom, left, right}` (px) | 화면 레이아웃 변경 시 (방향 전환 등) | | `ACCELEROMETER` | `{x, y, z}` (m/s²) | `startAccelerometer()` 호출 후 지정 주기로 반복 수신 | 볼륨과 네트워크 상태는 UPDATE 이벤트로 전달되지 않습니다. 필요할 때 `getSoundState()`, `getNetworkState()`를 직접 호출하세요. ```ts type UpdateMessage = | {type: 'LIFECYCLE'; payload: {state: 'FOREGROUND' | 'BACKGROUND'}} | { type: 'SAFE_AREA'; payload: {top: number; bottom: number; left: number; right: number}; } | {type: 'ACCELEROMETER'; payload: {x: number; y: number; z: number}}; declare global { interface Window { onNativeUpdate?: (message: UpdateMessage) => void; } } // 1. 핸들러 정의 window.onNativeUpdate = (message: UpdateMessage): void => { switch (message.type) { case 'LIFECYCLE': message.payload.state === 'BACKGROUND' ? pauseGame() : resumeGame(); break; case 'SAFE_AREA': adjustLayout(message.payload); break; case 'ACCELEROMETER': handleMotion(message.payload); break; } }; // 2. 핸들러 등록 (이 시점부터 UPDATE 이벤트 수신 시작) await window.kakaotalkGamePlay.registerNativeHandler('onNativeUpdate'); ``` 핸들러 이름은 자유롭게 정의할 수 있습니다. 네이티브는 등록한 이름의 함수를 호출해 UPDATE 메시지를 전달합니다. ### getNetworkState() 현재 네트워크 연결 상태를 조회합니다. ```ts type NetworkType = 'wifi' | 'cellular' | 'none' | 'unknown'; interface NetworkState { isWifi: boolean; networkType: NetworkType; } const {isWifi, networkType}: NetworkState = await window.kakaotalkGamePlay.getNetworkState(); if (!isWifi && networkType === 'cellular') { showDataUsageWarning(); } ``` ### getSafeArea() 현재 웹뷰의 Safe Area Insets (px) 를 조회합니다. 노치·홈 인디케이터 영역을 피해 UI를 배치할 때 사용합니다. ```ts interface SafeAreaInsets { top: number; bottom: number; left: number; right: number; } const safeArea: SafeAreaInsets = await window.kakaotalkGamePlay.getSafeArea(); // 예: {top: 47, bottom: 34, left: 0, right: 0} document.body.style.paddingTop = `${safeArea.top}px`; document.body.style.paddingBottom = `${safeArea.bottom}px`; ``` Safe Area 값 변경 시 `SAFE_AREA` UPDATE 이벤트도 수신되므로 **초기 1회는 `getSafeArea()`, 이후 변경 감지는 UPDATE 이벤트**로 처리합니다. ### startAccelerometer(options) / stopAccelerometer() 가속도 센서 수집을 시작·종료합니다. 시작 후에는 `ACCELEROMETER` UPDATE 이벤트가 지정 주기로 반복 수신됩니다. | 필드 | 타입 | 설명 | | --- | --- | --- | | `interval` | `number` (ms) | 수집 주기. 기본 50ms, 최소 33ms | payload 스펙: * 단위: m/s² (W3C `DeviceMotionEvent.accelerationIncludingGravity` 스펙과 동일) * 중력 포함 값. 디바이스를 수평으로 놓으면 z 축이 약 +9.81 * iOS CoreMotion 기본값(G 단위)을 Android/W3C 표준으로 변환해 전달하므로 양쪽 플랫폼에서 동일한 값을 사용할 수 있습니다. 선행 조건: `registerNativeHandler()`로 UPDATE 핸들러를 먼저 등록해야 합니다. ```ts interface AccelerometerOptions { interval?: number; // ms, 기본 50, 최소 33 } window.onNativeUpdate = (message: UpdateMessage): void => { if (message.type === 'ACCELEROMETER') { const {x, y, z} = message.payload; applyTilt(x, y, z); } }; await window.kakaotalkGamePlay.registerNativeHandler('onNativeUpdate'); // 수집 시작 (100ms 주기) await window.kakaotalkGamePlay.startAccelerometer({interval: 100}); // 종료 (게임 종료·백그라운드 전환 시 반드시 호출해 배터리 소모 방지) await window.kakaotalkGamePlay.stopAccelerometer(); ``` ### keepBrowser() 현재 게임웹뷰를 **닫지 않고** 화면 우측의 플로팅(둥둥이) 아이콘으로 최소화합니다. 사용자가 둥둥이 아이콘을 다시 탭하면 직전 상태로 복귀합니다. iOS 전용 기능입니다. * `close()` 는 웹뷰를 완전히 종료하지만, `keepBrowser()` 는 컨텍스트를 유지한 채 최소화합니다. * 게임 도메인(`gameplay.kakao.com` 등)에서 최소화 시 게임 전용 플로팅 아이콘이 표시됩니다. * 최소화된 웹뷰는 리로드 없이 보관되므로 JS 상태와 게임 진행 상황이 유지됩니다. * 접힘 시점에는 `LIFECYCLE` UPDATE의 `BACKGROUND`가 전송되지 않습니다. 게임이 직접 `keepBrowser()`를 호출하기 직전에 게임 루프와 사운드를 일시정지하세요. * 사용자가 플로팅 아이콘을 탭해 복귀하면 등록된 네이티브 핸들러로 `LIFECYCLE` UPDATE의 `FOREGROUND`가 전송됩니다. 이 시점을 게임 재개 트리거로 사용합니다. ::: danger 접기 UI 는 iPhone 에서만 노출하세요 접기를 제공하는 환경은 **카카오톡 안의 iPhone** 뿐입니다. 나머지 환경에서는 접기 버튼을 노출하지 마세요. | 환경 | 접기 | 호출하면 | | --- | --- | --- | | iPhone | 제공 | 정상 동작 | | 태블릿 | 미제공 | **웹뷰가 접히지 않고 그대로 종료됩니다** | | Android | 미제공 | `"keepBrowser is not supported"` 반환 | 태블릿이 특히 위험합니다. 카카오톡 인앱브라우저 자체에 접기 버튼이 없고, 호출해도 **에러가 나지 않습니다.** 코드만으로는 드러나지 않은 채 사용자만 게임이 예고 없이 종료되는 경험을 하게 됩니다. 메서드를 막을 것이 아니라 **UI 노출을 분기하세요.** 제외 목록이 아니라 허용 조건으로 쓰는 편이 안전합니다: 지원 환경이 늘어날 때까지 새 기기가 자동으로 포함되지 않습니다. 구분 방법은 [디바이스 구분 방법](/docs/webview/device-detection) 을 참고합니다. ```ts declare function showCollapseButton(): void; const {isIOS, isTablet, isKakaoTalk} = getDeviceInfo(); if (isKakaoTalk && isIOS && !isTablet) { showCollapseButton(); } ``` ::: ```ts async function collapseGame(): Promise { pauseGame(); muteGameSound(); await window.kakaotalkGamePlay.keepBrowser(); } window.onNativeUpdate = (message: UpdateMessage): void => { if (message.type === 'LIFECYCLE' && message.payload.state === 'FOREGROUND') { resumeGame(); unmuteGameSound(); } }; await window.kakaotalkGamePlay.registerNativeHandler('onNativeUpdate'); ``` 에러 케이스: * 미지원 환경 → `"keepBrowser is not supported"` ## 전체 초기화 예시 게임 진입부터 종료까지 전체 흐름 샘플입니다. ```ts window.onNativeUpdate = (message: UpdateMessage): void => { switch (message.type) { case 'LIFECYCLE': message.payload.state === 'BACKGROUND' ? pauseGame() : resumeGame(); break; case 'SAFE_AREA': adjustLayout(message.payload); break; case 'ACCELEROMETER': handleMotion(message.payload); break; } }; async function initGame(): Promise { await window.kakaotalkGamePlay.registerNativeHandler('onNativeUpdate'); const network = await window.kakaotalkGamePlay.getNetworkState(); if (network.networkType === 'none') { showNetworkError(); return; } const safeArea = await window.kakaotalkGamePlay.getSafeArea(); adjustLayout(safeArea); await window.kakaotalkGamePlay.setScrollEnabled(false); await window.kakaotalkGamePlay.setOrientation({mode: 'landscape'}); await window.kakaotalkGamePlay.startAccelerometer({interval: 50}); } async function onGameClear(): Promise { await window.kakaotalkGamePlay.stopAccelerometer(); await window.kakaotalkGamePlay.haptic({mode: 'success'}); await window.kakaotalkGamePlay.setOrientation({mode: 'portrait'}); await window.kakaotalkGamePlay.resetWebViewHistory('https://gameplay.kakao.com/result'); } async function onGameExit(): Promise { await window.kakaotalkGamePlay.stopAccelerometer(); await window.kakaotalkGamePlay.close(); } void initGame(); ``` ## 트러블슈팅: 웹뷰 헤더 잔상 파트너사와의 실제 연동 과정에서 발견되어 정리된 이슈와 해결 방법입니다. 재현되는 케이스가 나오면 이 섹션에 추가합니다. ### 현상 파트너사 게임 진입 시 다음 흐름을 탑니다. 1. gameplay splash 2. 게임 도메인 이동 3. kauth 이동 (로그인·동의) 4. 게임 도메인 재진입 1·2·4단계에서는 카카오톡 웹뷰 상단 native header가 노출되면 안 되고 3단계(kauth)에서만 노출되어야 합니다. 그런데 **3 → 4로 전환될 때, 3단계에서 노출되던 header가 4단계 진입 직후에도 잠시 유지되다가 사라지는 잔상**이 파트너사에 따라 관찰됩니다. ### 원인 카카오톡 iOS 웹뷰의 native header는 **`didFinishNavigation` 콜백 시점**에 숨김 처리됩니다. | 순서 | 단계 | 다음 단계로의 전이 | | --- | --- | --- | | 1 | kauth (header 노출 페이지) | redirect | | 2 | 게임 도메인 페이지 응답 | HTML 파싱 + 스크립트 로드: **로드가 지연되면 이 구간이 길어진다** | | 3 | `didFinishNavigation` 콜백 | — | | 4 | native header 숨김 | — | `didFinishNavigation` 은 **현재 문서의 파싱 및 blocking 리소스 로드 완료** 시점에 호출됩니다. HTML 안의 ` ``` 주의: * 브릿지 페이지는 **inline script만 있는 정적 HTML** 이어야 합니다. ` ``` 트레이드오프: * 초기 렌더까지 흰 화면 구간이 잠깐 존재 (배경 스타일로 완화) * HTML 개조 후에도 첫 진입 시 실제 JS 실행 시점은 이전과 유사하지만, **native header는 `didFinishNavigation` 기준으로 이미 사라진 뒤**이므로 잔상은 발생하지 않습니다. * 라우팅·백엔드 변경 없이 프론트 빌드 산출물만 수정하면 되는 저비용 대응입니다. ## 참고 문서 * [게임 실행 흐름](/docs/webview/launch-flow): 웹뷰 진입부터 게임 실행까지의 화면 전환 구조 * [웹뷰 컴포넌트 가이드](/docs/design/webview-component-guide): 화면 컴포넌트와 디자인 사양 * [게임웹뷰 테스트 방법](/docs/webview/test-guide): 화이트리스트 도메인 웹뷰 실행 방법 * [카카오싱크 API: 게임 중 추가 동의](/api-sdk/kakaosync/rest-api#게임-중-추가-동의): 별도 웹뷰 open/close 스킴 사용법 * 기술 문의: [카카오디벨로퍼스 데브톡](https://devtalk.kakao.com/c/game-play/353)