--- url: /docs/webview/device-detection.md description: 게임웹뷰에서 iOS · Android · 태블릿을 구분하는 방법과 판정 규칙 --- # 디바이스 구분 방법 게임웹뷰는 iOS · Android 두 플랫폼과 폰 · 태블릿 두 폼팩터에서 동작합니다. 플랫폼별 처리나 태블릿 레이아웃이 필요할 때 아래 코드로 기기를 구분할 수 있습니다. * **의존성이 없습니다.** 파일 하나를 복사해 그대로 사용하세요. * 구분 기준은 **User-Agent 문자열**이며, iPadOS 예외 한 건만 `navigator.maxTouchPoints` 를 함께 사용합니다. * 카카오톡 인앱 웹뷰 여부(`isKakaoTalk`)도 함께 구분합니다. ## 구분이 필요한 대표적인 경우 | 상황 | 이유 | | --- | --- | | 웹뷰 접기(`keepBrowser`) UI 노출 | 접기는 **카카오톡 안의 iPhone 에서만** 제공합니다. 태블릿에서는 웹뷰가 접히지 않고 그대로 종료됩니다. [게임웹뷰 SDK: keepBrowser](/api-sdk/sdk/kakaotalk-gameplay#keepbrowser) 참고 | | 태블릿 레이아웃 | 큰 화면에 맞춘 배치가 필요한 경우. [게임 공통 제작 가이드](/docs/design/common-guide#태블릿-대응) 참고 | | 플랫폼별 처리 | Android 하드웨어 백 버튼 처리 등 | ## 코드 ```ts export type DeviceInfo = { /** 'unknown' 은 iOS·Android 가 아닌 환경 (데스크톱 브라우저 등). */ platform: 'ios' | 'android' | 'unknown'; isIOS: boolean; isAndroid: boolean; /** iPad 및 Android 태블릿. 폴더블 폰의 펼침 상태는 태블릿이 아닙니다. */ isTablet: boolean; /** 카카오톡 인앱 웹뷰 여부. 게임웹뷰를 포함합니다. */ isKakaoTalk: boolean; }; const UNKNOWN_DEVICE: DeviceInfo = { platform: 'unknown', isIOS: false, isAndroid: false, isTablet: false, isKakaoTalk: false, }; /** * 터치 스크린 보유 여부. * * iPadOS 13+ 는 Safari '데스크톱용 사이트' 모드에서 User-Agent 를 `Macintosh` 로 보고합니다. * 이 경우 User-Agent 만으로는 데스크톱 macOS 와 구분되지 않으므로 터치 지원 여부로 구분합니다. * (데스크톱 macOS 는 두 값 모두 거짓, iPadOS 는 참) */ function hasTouchScreen(win: Window): boolean { const maxTouchPoints = win.navigator?.maxTouchPoints ?? 0; if (maxTouchPoints > 1) { return true; } return win.document !== undefined && 'ontouchend' in win.document; } function readUserAgent(win: Window): string { return win.navigator?.userAgent ?? ''; } export function getDeviceInfo(win: Window = window): DeviceInfo { const userAgent = readUserAgent(win); if (!userAgent) { return UNKNOWN_DEVICE; } // 카카오톡 인앱 웹뷰는 iPad 토큰을 유지하므로 이 분기를 타지 않습니다. // 카카오톡 외부(일반 Safari)에서 페이지가 열리는 경우를 위한 안전망입니다. const isDesktopModeIPad = /Macintosh/i.test(userAgent) && hasTouchScreen(win); const isIOS = /iPhone|iPad|iPod/i.test(userAgent) || isDesktopModeIPad; // 레거시 Windows Phone User-Agent 가 'Android 4.0' 을 포함해 오탐됩니다. const isAndroid = /Android/i.test(userAgent) && !/Windows Phone/i.test(userAgent); // Android 태블릿은 User-Agent 에서 'Mobile' 토큰이 빠집니다. // iOS 는 iPad 토큰으로 구분합니다 — iPad 에도 'Mobile' 토큰이 있어 위 규칙을 쓸 수 없습니다. const isTablet = /^(?=.*android)(?!.*mobile).*/i.test(userAgent) || /iPad/i.test(userAgent) || isDesktopModeIPad; // 카카오톡 인앱 웹뷰는 iOS·Android 모두 User-Agent 에 KAKAOTALK 토큰을 넣습니다. const isKakaoTalk = /KAKAOTALK/i.test(userAgent); const platform = isIOS ? 'ios' : isAndroid ? 'android' : 'unknown'; return {platform, isIOS, isAndroid, isTablet, isKakaoTalk}; } ``` ## 사용 방법 ```ts declare function applyTabletLayout(): void; declare function registerAndroidBackHandler(): void; declare function showCollapseButton(): void; const {isIOS, isAndroid, isTablet, isKakaoTalk} = getDeviceInfo(); if (isTablet) { applyTabletLayout(); } if (isAndroid) { registerAndroidBackHandler(); } // 접기는 카카오톡 안의 iPhone 에서만 제공합니다. Android 와 태블릿은 대상이 아닙니다. if (isKakaoTalk && isIOS && !isTablet) { showCollapseButton(); } ``` User-Agent 는 페이지 수명 동안 바뀌지 않습니다. 모듈 로드 시 한 번 호출해 결과를 재사용해도 됩니다. `platform` 값은 `isIOS` · `isAndroid` 와 항상 일치합니다. 편의를 위한 중복이므로 어느 쪽을 써도 됩니다. ## 판정 규칙 | 항목 | 규칙 | 근거 | | --- | --- | --- | | `isIOS` | User-Agent 에 `iPhone` · `iPad` · `iPod` 중 하나. 또는 `Macintosh` + 터치 보유 | 카카오톡 인앱 웹뷰는 기기 토큰을 유지합니다. `Macintosh` 조건은 카카오톡 외부 iPadOS 대응용입니다 | | `isAndroid` | User-Agent 에 `Android`, 단 `Windows Phone` 제외 | 레거시 Windows Phone User-Agent 가 `Android 4.0` 을 포함해 오탐됩니다 | | `isTablet` | 셋 중 하나: ① `android` 가 있고 `mobile` 이 **없음** ② User-Agent 에 `iPad` ③ `Macintosh` + 터치 보유 | ① Android 는 폰에만 `Mobile` 토큰을 넣어 태블릿에서는 생략됩니다. ② iPad **에도 `Mobile` 토큰이 있어** ①번 규칙을 iOS 에 쓸 수 없습니다. ③ User-Agent 에 `iPad` 토큰이 없는 유일한 iPad 케이스입니다 | | `isKakaoTalk` | User-Agent 에 `KAKAOTALK` | 카카오톡 인앱 웹뷰는 iOS · Android 모두 이 토큰을 넣습니다. 게임웹뷰도 인앱 웹뷰이므로 `true` 입니다 | ### User-Agent 예시 카카오톡 26.7.0 기준입니다. 끝부분에는 카카오톡 버전과 웹뷰 구분값이 붙습니다. ```text iPhone Mozilla/5.0 (iPhone; CPU iPhone OS 18_6 like Mac OS X) AppleWebKit/605.1.15 Mobile/15E148 KAKAOTALK/26.7.0 ... iPad Mozilla/5.0 (iPad; CPU OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 KAKAOTALK/26.7.0 ... Android Mozilla/5.0 (Linux; Android 14; SM-S928N; wv) AppleWebKit/537.36 Mobile Safari/537.36; ... KAKAOTALK 2426700 ``` iPad 예시의 `Mobile/15E148` 을 주의해서 보세요. **iPad 에도 `Mobile` 토큰이 있습니다.** 이 때문에 Android 의 `Mobile` 토큰 규칙을 iOS 에 적용할 수 없습니다. ## 환경별 판정 결과 구현을 검증할 때 참고하세요. `O` = true, `-` = false 입니다. | 환경 | `isIOS` | `isAndroid` | `isTablet` | `isKakaoTalk` | | --- | --- | --- | --- | --- | | iPhone: 게임웹뷰 | O | - | - | O | | iPad: 게임웹뷰 | O | - | O | O | | Android 폰: 게임웹뷰 | - | O | - | O | | Android 태블릿: 게임웹뷰 | - | O | O | O | | iPhone: Safari | O | - | - | - | | iPad: Safari | O | - | O | - | | iPad: Safari 데스크톱용 사이트 모드 | O | - | O | - | | iPod touch: Safari | O | - | - | - | | Android 폰: Chrome | - | O | - | - | | Android 태블릿: Chrome | - | O | O | - | | 폴더블 폰 펼침: Chrome | - | O | - | - | | 데스크톱: macOS Safari · Chrome | - | - | - | - | | 데스크톱: Windows Chrome (터치 포함) | - | - | - | - | 데스크톱 환경에서는 `platform` 이 `'unknown'` 이 됩니다. User-Agent 를 읽을 수 없는 경우에도 예외를 던지지 않고 `'unknown'` 을 반환합니다. 주목할 두 가지가 있습니다. * **iPad 데스크톱용 사이트 모드와 데스크톱 macOS Safari 는 User-Agent 문자열이 완전히 같습니다.** `maxTouchPoints` 없이는 구분할 수 없습니다. * **폴더블 폰 펼침 상태는 화면이 태블릿만큼 넓지만 태블릿이 아닙니다.** 화면 크기 기반 판정이 실패하는 대표적인 경우입니다. ## 사용하면 안 되는 방식 아래 방식은 위 표의 환경 중 일부에서 오판을 일으킵니다. | 방식 | 문제 | | --- | --- | | `screen.width` · `window.innerWidth` 로 태블릿 판정 | **폴더블 폰 펼침 상태(짧은 변 약 674px)를 태블릿으로 오판합니다.** iPad 에서 카카오톡이 split view 로 표시되면 `innerWidth` 가 기기 크기와 무관해지는 문제도 있습니다 | | iOS 에 "`Mobile` 토큰이 없으면 태블릿" 규칙 적용 | iPad 에도 `Mobile` 토큰이 있어 **iPad 를 폰으로 오판합니다.** Android 전용 규칙입니다 | | `navigator.platform` 사용 | Deprecated 된 API 이며, iPadOS 는 `MacIntel` 을 반환해 데스크톱과 구분되지 않습니다 | | `navigator.userAgentData` 단독 사용 | Chromium 계열 전용입니다. **iOS 에는 존재하지 않아** iPhone · iPad 판정이 전부 실패합니다 | | 카카오톡 버전으로 기능 지원 여부 분기 | 패치 · 백포트 빌드에서 버전 비교가 어긋납니다. 기능 지원 여부는 해당 메서드의 실제 존재를 확인하는 방식으로 판단하세요 | 기기 구분과 레이아웃 판단은 분리하세요. **기기 종류는 User-Agent, 레이아웃은 viewport 또는 media query** 를 사용합니다. iPad split view 처럼 기기 크기와 표시 영역이 일치하지 않는 상황이 있습니다. ## 적용 범위 이 코드는 **기기 종류(플랫폼 · 폼팩터)와 카카오톡 인앱 웹뷰 여부**를 구분합니다. `isKakaoTalk` 은 카카오톡 안에서 열렸는지만 알려줍니다. 게임웹뷰인지 일반 인앱브라우저인지까지 구분하지는 않습니다. 게임은 언제나 게임웹뷰에서 실행되므로 이 구분이 필요한 경우는 드뭅니다. 동작이 예상과 다른 기기를 발견하면 해당 기기의 User-Agent 문자열과 함께 [카카오디벨로퍼스 데브톡](https://devtalk.kakao.com/c/game-play/353)으로 알려주세요. 판정 규칙에 반영하겠습니다. ## 참고 문서 * [게임웹뷰](/docs/webview/overview): 게임웹뷰의 역할과 전체 개발 순서 * [게임웹뷰 SDK: keepBrowser](/api-sdk/sdk/kakaotalk-gameplay#keepbrowser): 접기를 iPhone 에서만 노출해야 하는 이유 * [게임 공통 제작 가이드](/docs/design/common-guide#태블릿-대응): 태블릿 레이아웃 기준 * [심사 체크리스트](/docs/checklist/review-checklist): 단말기 확인 항목