모바일 가상키보드 탐지하기
1. 서론
모바일 기기에서 사용자가 텍스트를 입력해야 하는 경우, 모바일에서는 기본적으로 가상 키보드(On-Screen Keyboard)가 등장하게 됩니다.
일반적인 상황에서는 큰 문제가 되지 않지만, 레이아웃을 고정하거나, 스크롤 위치를 제어하거나, 리사이징 이벤트에 반응하는 경우에는 가상 키보드가 큰 변수로 작용하곤 합니다.
가장 큰 문제는 iOS와 Android가 가상 키보드 동작 방식에서 차이를 보인다는 점입니다.
또한 일부 API가 플랫폼별로 지원되지 않거나, 지원하더라도 동작 방식이 다르기 때문에 호환성 이슈를 경험하게 됩니다.
이번 글에서는 모바일에서 가상 키보드가 등장했을 때 iOS와 안드로이드에서 레이아웃이 어떻게 변화하는지, 가상 키보드가 켜졌는지를 탐지하는 방법은 어떻게 다른지를 정리했습니다.
2. 플랫폼별 가상 키보드 등장 시 레이아웃 비교
가상 키보드가 화면에 등장하면 브라우저는 사용자가 입력할 수 있도록 공간을 확보해야 합니다.
이 과정에서 화면을 구성하는 뷰포트(viewport) 에 변화가 발생하게 됩니다.
iOS와 Android는 이 과정을 처리하는 방식에 차이를 보입니다.
2.1 iOS Safari (iPhone, iPad)
-
Layout Viewport: 변하지 않습니다.
→window.innerHeight
값은 가상 키보드가 열려도 그대로 유지됩니다. -
Visual Viewport: 줄어듭니다.
→window.visualViewport.height
가 가상 키보드 만큼 줄어듭니다. -
스크롤 위치 (offset):
VisualViewport가 줄어들면서, 화면 상에서 입력창이 키보드 위로 노출되기 위해offsetTop
등이 변하는 경우가 있습니다.
iOS에서는 레이아웃 뷰포트 자체는 고정되지만, 사용자가 실제로 보는 화면 영역(visual viewport)만 줄어들어 가상 키보드가 오버레이됩니다.
이러한 동작 때문에 fixed를 이용해 UI를 그리게 되면 가상키보드가 켜져있을 때 UI가 화면에서 보이지 않게 되는 이슈도 발생하곤 합니다.
fixed는 레이아웃 뷰포트를 기반으로 그려져 기기를 기준으로 특정 위치에 고정되는데 비주얼 뷰포트는 키보드로 인해 작아져 화면에서 보이지 않게 됩니다.
2.2 Android Chrome (대부분 Android 브라우저)
-
Layout Viewport: 줄어듭니다.
→window.innerHeight
값이 키보드 높이만큼 작아집니다. -
Visual Viewport: 함께 줄어듭니다.
→window.visualViewport.height
도 작아집니다.
Android에서는 레이아웃 뷰포트와 비주얼 뷰포트가 동시에 축소되며, 줄어든 화면 안에 키보드가 포함되지 않습니다.
3. 가상 키보드 탐지하기
최근에는 모바일 웹에서 가상 키보드를 감지하고 제어할 수 있는 VirtualKeyboard API 가 등장하면서 표준화가 진행되고 있지만, 아직 호환성 이슈가 존재합니다.
그래서 현재 시점에서는 기존 방식(뷰포트 크기 측정)을 함께 사용해야 하는 경우가 많습니다.
우선 화면 사이즈를 이용한 기존 탐지 방식을 설명하고 이어서 VirtualKeyboard API를 다루겠습니다.
3.1 iOS에서 가상 키보드 탐지하기
iOS에서는 가상 키보드가 열릴 때 Visual Viewport는 줄어들고 Layout Viewport는 줄어들지 않는 특성을 활용할 수 있습니다.
이 특성을 이용하여 visualViewport의 크기가 바뀔 때 마다 레이아웃 뷰포트와 크기 차이를 비교하여, 다음과 같은 방식으로 가상 키보드가 열렸는지 감지할 수 있습니다.
if ('visualViewport' in window) {
window.visualViewport.addEventListener('resize', (event: Event) => {
const innerHeight = window.innerHeight;
const visualHeight = (event.target as VisualViewport).height;
// `MIN_RATIO`는 '어느정도 비율이 줄어들어야 가상 키보드가 열렸는지 판별할지'를 결정하는 임계치
const isKeyboardVisible = (innerHeight - visualHeight) / innerHeight > MIN_RATIO;
// ...
});
}
3.1.1 iOS User Agent 판별하기
iOS의 경우는 Android와 달리 단순히 userAgent
문자열에 특정 텍스트가 있는지만 검사하는 것은 불완전합니다.
특히 iOS 13부터는 iPad가 userAgent에 “Macintosh”를 포함하여 보내기 때문에 단순히 “iPad”라는 문자열로 체크하는 것은 안전하지 않습니다. 이를 해결하기 위해 다음과 같은 복합 로직을 사용합니다.
const isIOS =
['iPhone', 'iPad', 'iPod'].includes(navigator.platform) ||
(navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);
자세한 토론은 이 StackOverflow 질문 에서도 참고할 수 있습니다.
3.2 Android에서 가상 키보드 탐지하기
Android는 위에서 설명한 것 처럼 iOS와는 다르게 Visual Viewport와 Layout Viewport가 모두 함께 줄어듭니다. 따라서 iOS에서 사용했던 비율 기반 방식은 그대로 사용할 수 없습니다.
3.2.1 진입시 innerHeight
저장하는 방식
조금 hacky한 방법이지만, 간단하게 구현할 수 있습니다.
const initialHeight = window.innerHeight;
window.addEventListener('resize', () => {
const currentHeight = window.innerHeight;
const heightDifferenceRatio = (initialHeight - currentHeight) / initialHeight;
const isKeyboardVisible = heightDifferenceRatio > MIN_RATIO;
// ...
});
- 최초 접속 시점에
window.innerHeight
값을 저장합니다. - 이후
resize
이벤트가 발생할 때마다 현재 높이와 비교합니다. - 줄어들었다면 가상 키보드가 열렸다고 판단합니다.
다만 단순히 화면 사이즈만 가지고 판단하는 방식이기 때문에 100% 정확하지는 않습니다.
사이트 초기 진입 시 가상 키보드가 닫혀있음을 전제로 하고, 갤럭시 flip 등 화면 사이즈가 가변적으로 변할 경우 오탐이 발생할 수 있어 가능한 경우 표준 API를 사용하는게 더 바람직해 보입니다.
3.2.2 VirtualKeyboard API 사용
최신 Android (Chromium 기반) 브라우저에서는 VirtualKeyboard API 를 통해 보다 정확하고 신뢰성 있게 키보드 상태를 감지할 수 있습니다.
이에 대해서는 “4. VirtualKeyboard API”에서 설명합니다.
4. VirtualKeyboard API
최근에는 가상 키보드를 제어할 수 있도록 공식 표준 API인 VirtualKeyboard API가 등장했습니다.
이를 통해 키보드의 표시 여부, 키보드 크기, 키보드 위치 등을 보다 정확하게 알 수 있습니다.
다만 주의해야 할 점은 현재 실험중인 상태의 API이고, iOS Safari에서는 지원되지 않습니다. 주요 지원 브라우저는 Android Chrome 94+, Edge 94+ 등 Chromium 계열입니다.
4.1 VirtualKeyboard API의 히스토리
비교적 최근에 만들어진 API 입니다. 찾아보니 2020년 후반부터 본격적으로 진행된 것으로 보입니다.
-
2020년 10월: W3C TPAC 2020 에서 최초로 Virtual Keyboard Control 관련 발표가 진행되었습니다.
-
2021년 8월 24일: First Public Working Draft (FPWD) 로 최초 공개되었습니다.
-
2022년 5월 5일: Third Working Draft 가 발표되면서 사양이 보다 구체화되었습니다.
-
2024년 4분기: Recommendation (최종 권고안) 예정
아직 IOS webkit과 firefox에서 언제 지원이 될지는 공식적인 지원 계획은 찾지 못했습니다.
Webkit Bugzilla 토론 은 담당자에게 전달이 되었다는 정도, Webkit Github Issue 는 최근에는 크게 진행되고 있지는 않은것으로 보입니다.
4.2 VirtualKeyboard API 사용 방법
VirtualKeyboard API를 사용하는 기본적인 방법은 다음과 같습니다.
if ('virtualKeyboard' in navigator) {
const virtualKeyboard = navigator.virtualKeyboard
virtualKeyboard.overlaysContent = true
virtualKeyboard.addEventListener('geometrychange', () => {
const { x, y, width, height } = virtualKeyboard.boundingRect
console.log(`키보드 위치: (${x}, ${y}), 크기: ${width}x${height}`)
})
}
-
navigator.virtualKeyboard.overlaysContent = true
를 설정하면, 브라우저가 키보드가 뜰 때 자동으로 레이아웃을 줄이는 것을 막고, 키보드가 화면 위에 “오버레이”되게 만듭니다. -
geometrychange
이벤트를 통해 키보드의 위치와 크기를 실시간으로 감지할 수 있습니다. -
height
속성을 통해 가상 키보드의 높이 값을 구할 수 있습니다.
4.2.1 overlaysContent 설정
설정 값 | 동작 방식 |
---|---|
true | 키보드가 화면 위에 오버레이됩니다. 레이아웃(viewport)은 줄어들지 않습니다. |
false | 기본적인 안드로이드 방식. 키보드가 열릴 때 브라우저가 Layout Viewport를 줄여서 키보드 공간을 확보합니다. |
4.3 테스트
예시 html 파일을 만들어서 안드로이드 기기로 한번 테스트 해봤습니다.
2025년 4월 기준으로 Virtual Keyboard API를 지원하는 크롬 브라우저와 삼성 인터넷 브라우저를 사용했습니다.
- 안드로이드 버전: 13
- 삼성 인터넷 브라우저 버전: 28.0.0.57
- 크롬 버전: 135.0.7.049.111
예제에서 “fixed bottom-0”은 padding-bottom: env(keyboard-inset-height, 0px)
을 준 요소로, 오버레이시에도 가상 키보드 위에 위치하도록 만들었습니다
4.3.1 크롬 브라우저에서 오버레이를 켜고 VirtualKeyboard API를 사용하는 경우
- 뷰포트의 높이는 그대로 유지되면서 키보드의 높이 값 측정

4.3.2 크롬 브라우저에서 오버레이를 끈 경우
- 일반적인 안드로이드 브라우저 동작
- 뷰포트의 높이가 줄어들었고, VirtualKeyboard API로 높이 값 측정이 불가능함.

4.3.3 삼성 인터넷 브라우저에서 오버레이를 켜고 VirtualKeyboard API를 사용하는 경우
- 뷰포트의 높이가 늘어나면서 키보드의 높이 값이 측정됨

4.3.4 삼성 인터넷 브라우저에서 오버레이를 끈 경우
- 일반적인 안드로이드 브라우저 동작
- 뷰포트의 높이가 줄어들었고, VirtualKeyboard API로 높이 값 측정이 불가능함.

그리고 화면을 작게 분할 하는 경우도 한번 확인해봤습니다.
4.3.5 삼성 인터넷 브라우저 오버레이를 켜고 VirtualKeyboard API를 사용하는 경우 + 화면을 작게 분할한 경우
- 가상 키보드가 화면을 가리지 않아
height
는0

4.3.6 삼성 인터넷 브라우저 오버레이를 켜고 VirtualKeyboard API를 사용하는 경우 + 화면을 작게 크할한 경우
- 가상 키보드가 화면을 가린 부분만큼
height
를 계산

4.4 정리
아마 모바일에서 가상키보드 이슈를 겪는다면 아래 2가지의 경우가 많을 것 같은데요,
- 가상 키보드로 인해 일부 UI가 가려지는 경우
- 스크롤 관련 처리
안드로이드에서도 가상 키보드를 띄울 때 뷰포트를 줄이는게 아니라 레이아웃 뷰포트의 크기를 유지하면서 오버레이 형태로 띄울 수도 있고, 키보드의 위치와 높이 등을 제공해줘서 더 좋은 DX로 해결이 가능합니다.
다만 아직 실험적인 기능이다보니 테스트에서 크롬에서는 뷰포트 높이가 변하지 않았고, 삼성 인터넷 브라우저는 뷰포트가 늘어나는 경우도 있었고 IOS와 파이어폭스가 지원되지 않아서 현재는 기존 방법을 사용해야겠지만 추후 호환성 문제만 해결된다면 개발할 때 적극적으로 활용이 가능해보입니다.