mobile-web

1 개의 포스트

웹뷰 엔지니어를 위한 iOS Webview Input 경험 개선기 (새 탭에서 열림)

당근 커뮤니티실의 웹뷰 엔지니어가 iOS 환경에서 키보드가 올라올 때 화면 전체가 위로 밀리는 고질적인 문제를 해결하기 위해 시도한 네 가지 단계의 여정을 담고 있습니다. iOS 웹뷰의 레이아웃(Layout) 및 시각적(Visual) 뷰포트 특성을 분석하고, 최종적으로 `opacity` 속성을 이용해 OS의 자동 스크롤 동작을 선제적으로 차단하는 최적의 사용자 경험(UX) 솔루션을 도출했습니다. 이는 기술적 완결성보다 유저의 입력 흐름을 깨지 않는 실전적인 해결책에 집중한 사례입니다. ### iOS 웹뷰의 뷰포트 구조와 문제의 원인 iOS 웹뷰에서 입력창(Input)에 포커스가 되면 키보드 공간을 확보하기 위해 페이지가 위로 밀려 올라가는 현상이 발생합니다. * **두 가지 뷰포트의 차이**: 키보드가 올라와도 크기가 변하지 않는 'Layout Viewport'와 사용자 눈에 보이는 영역인 'Visual Viewport' 간의 불일치가 원인입니다. * **OS의 강제 동작**: iOS는 포커스된 입력창을 시각적 영역 안에 두기 위해 페이지 전체를 밀어 올리며, 이 과정에서 상단 콘텐츠가 화면 밖으로 사라지는 등 웹 개발자가 제어하기 어려운 동작이 수반됩니다. ### 초기 시도: 리사이징과 오프셋 보정의 한계 문제를 인지한 후 화면을 원래 위치로 되돌리거나 밀린 만큼 따라가는 방식을 시도했으나 기술적 한계가 있었습니다. * **Resize 방식**: `visualViewport`의 변화를 감지해 래퍼 요소를 축소하고 `scrollTo(0, 0)`를 호출했으나, OS가 화면을 먼저 밀어버린 후 복구되기 때문에 화면 떨림과 깜빡임이 발생했습니다. * **OffsetTop 방식**: `visualViewport.offsetTop` 값을 계산해 화면이 밀린 만큼 레이아웃의 `top` 위치를 조정했습니다. 첫 번째 시도보다 나았지만, 키보드가 올라오는 과정에서 실시간 보정 딜레이로 인한 미세한 진동 피드백이 남았습니다. ### 전환점: Fake Input을 활용한 입력창 스왑 iOS가 화면을 밀어 올리는 원인 자체를 제거하기 위해 입력창을 두 개로 분리하는 전략을 세웠습니다. * **동작 원리**: 평소에는 화면에 보이는 'Fake Input(ReadOnly)'을 노출하고, 터치 시 화면 밖에 숨겨둔 'Real Input'에 포커스를 줍니다. * **효과**: iOS는 화면 밖에 있는 요소를 위해 스크롤을 발생시키지 않으므로 화면 밀림이 완벽히 해결되었습니다. * **문제점**: 두 입력창 사이의 값(Value), 선택 영역, 자동 높이 조절 등을 동기화해야 하는 로직이 매우 복잡해져 유지보수 부담이 컸습니다. ### 최종 해결책: Opacity를 이용한 OS 속이기 입력창을 하나만 유지하면서도 OS의 자동 스크롤을 막기 위해 브라우저의 특성을 활용한 'Opacity Trick'을 적용했습니다. * **핵심 아이디어**: iOS는 `opacity: 0`인 요소에 대해서는 'scroll-into-view'(포커스된 요소를 화면 안으로 가져오는 동작)를 수행하지 않는다는 점을 발견했습니다. * **구현 로직**: `onTouchStart` 시점에 입력창의 `opacity`를 0으로 만들어 자동 스크롤을 차단한 뒤, `focus`가 완료되고 키보드가 올라온 시점에 다시 `opacity`를 1로 복원합니다. * **결과**: 입력창 상태 관리의 복잡성 없이 단일 요소를 사용하면서도 화면 밀림 현상을 선제적으로 차단하여 가장 매끄러운 UX를 구현했습니다. 정석적인 API가 없는 환경에서 유저 경험을 위해 선택한 이 방식은 다소 'Hacky'할 수 있으나, 프로덕트의 핵심인 '글쓰기 경험'을 보호하기 위한 엔지니어링적 결단이었습니다. 기술적인 완벽함보다는 유저가 느끼는 불편함을 제거하는 것이 우선이며, OS 업데이트 등 변화하는 환경에 맞춰 지속적으로 해결책을 고도화해 나가는 자세가 중요합니다.