dev.to

Stop Writing JavaScript To Fix `100vh` On Mobile

모바일 `100vh` 문제를 JavaScript로 고치지 마세요

모바일 브라우저에서 `100vh`는 현재 보이는 화면이 아니라 브라우저 UI가 접힌 상태의 높이를 기준으로 삼을 수 있습니다. 글은 `dvh`, `svh`, `lvh`의 차이를 설명하고, 목적에 맞는 CSS 뷰포트 단위를 쓰면 JavaScript 우회 코드를 없앨 수 있다고 안내합니다.

AI 요약

모바일 페이지의 히어로 영역에 height: 100vh를 적용하면 주소창이나 도구 모음이 화면을 가린 상태에서 버튼이 잘릴 수 있습니다. 글은 이 현상이 내부 여백이나 버튼 크기보다 뷰포트 단위의 기준에서 비롯한다고 설명합니다.

`100vh`가 화면과 맞지 않는 이유

vh는 사용자가 지금 보는 화면 높이를 재지 않습니다. iOS Safari에서는 주소창과 도구 모음이 완전히 접힌 상태의 큰 뷰포트 높이를 기준으로 삼습니다. 페이지를 처음 열어 브라우저 UI가 표시될 때는 히어로 영역이 실제 화면보다 커져 아래쪽 콘텐츠가 가려집니다. 과거 Chrome on Android는 브라우저 UI가 펼쳐진 작은 뷰포트에 맞춰 vh를 계산하는 등 브라우저마다 동작도 달랐습니다.

이 문제를 보정하려고 window.innerHeight를 읽어 CSS 사용자 정의 속성에 저장하고, resize 이벤트마다 갱신하는 JavaScript 코드를 흔히 사용합니다. 하지만 스크립트 실행 전 한 프레임 동안 잘못된 높이가 보이고, 전체 화면 요소마다 코드를 연결해야 합니다. 일부 Android 키보드에서는 resize 이벤트가 발생하며 스크롤 중 화면이 튀기도 합니다.

화면 상황에 맞춰 단위 선택

2022년 이후 주요 브라우저 엔진은 작은 뷰포트 높이인 svh, 큰 뷰포트 높이인 lvh, 현재 보이는 높이를 따라가는 동적 뷰포트 높이인 dvh를 지원합니다. svh는 브라우저 UI가 모두 펼쳐진 상태를 기준으로 삼아 콘텐츠가 화면 밖으로 잘리지 않도록 합니다. lvh는 UI가 접힌 상태의 높이를 나타내며, iOS Safari의 기존 vh 동작과 같습니다. dvh는 주소창이 나타나고 사라질 때 실제 표시 영역을 따라 높이를 다시 계산합니다.

히어로처럼 보이는 화면에 맞춰야 하는 영역에는 다음처럼 작성하면 됩니다.

``css .hero { height: 100vh; height: 100dvh; } ``

브라우저가 dvh를 지원하지 않으면 해당 선언을 무시하고 앞선 100vh를 사용합니다. 따라서 별도의 @supports 검사 없이도 대체값을 둘 수 있습니다.

동적 크기 변화도 고려

dvh는 주소창이 애니메이션으로 움직이는 동안에도 높이를 갱신합니다. 전체 화면 히어로에는 적절하지만, 크기가 고정돼야 하는 모달이나 하단 시트에서는 화면이 흔들리는 듯 보일 수 있습니다. 글은 화면을 온전히 채우려는 영역에는 dvh, 잘림을 막아야 하는 모달의 최대 높이나 온보딩 화면에는 svh, 브라우저 UI가 접힌 상태를 의도적으로 유지하려는 경우에는 lvh를 권합니다.

글에 따르면 Safari는 15.4부터, Chrome과 Edge는 108부터, Firefox는 101부터 이 단위를 지원합니다. svw·lvw·dvw는 너비에, svi·lvi·dvisvb·lvb·dvb는 쓰기 방향을 고려하는 논리적 크기에 대응합니다.

원문: dev.to / 번역·요약: Trawling