Skip to content

디바이스 구분 방법 ​

게임웹뷰는 iOS · Android 두 플랫폼과 폰 · 태블릿 두 폼팩터에서 동작합니다.
플랫폼별 처리나 태블릿 레이아웃이 필요할 때 아래 코드로 기기를 구분할 수 있습니다.

  • 의존성이 없습니다.
    파일 하나를 복사해 그대로 사용하세요.
  • 구분 기준은 User-Agent 문자열이며, iPadOS 예외 한 건만 navigator.maxTouchPoints 를 함께 사용합니다.
  • 카카오톡 인앱 웹뷰 여부(isKakaoTalk)도 함께 구분합니다.

구분이 필요한 대표적인 경우 ​

상황이유
웹뷰 접기(keepBrowser) UI 노출접기는 카카오톡 안의 iPhone 에서만 제공합니다.
태블릿에서는 웹뷰가 접히지 않고 그대로 종료됩니다.
게임웹뷰 SDK: keepBrowser 참고
태블릿 레이아웃큰 화면에 맞춘 배치가 필요한 경우.
게임 공통 제작 가이드 참고
플랫폼별 처리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 와 항상 일치합니다.
편의를 위한 중복이므로 어느 쪽을 써도 됩니다.

판정 규칙 ​

항목규칙근거
isIOSUser-Agent 에 iPhone · iPad · iPod 중 하나.
또는 Macintosh + 터치 보유
카카오톡 인앱 웹뷰는 기기 토큰을 유지합니다.
Macintosh 조건은 카카오톡 외부 iPadOS 대응용입니다
isAndroidUser-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 케이스입니다
isKakaoTalkUser-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 입니다.

환경isIOSisAndroidisTabletisKakaoTalk
iPhone: 게임웹뷰O--O
iPad: 게임웹뷰O-OO
Android 폰: 게임웹뷰-O-O
Android 태블릿: 게임웹뷰-OOO
iPhone: SafariO---
iPad: SafariO-O-
iPad: Safari 데스크톱용 사이트 모드O-O-
iPod touch: SafariO---
Android 폰: Chrome-O--
Android 태블릿: Chrome-OO-
폴더블 폰 펼침: 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 문자열과 함께 카카오디벨로퍼스 데브톡으로 알려주세요.
판정 규칙에 반영하겠습니다.

참고 문서 ​