반응형 웹사이트를 작업하다 보면 화면 전체 높이를 사용하는 레이아웃을 구현할 일이 종종 있다. 풀스크린 메인 비주얼이나 모바일 랜딩 페이지, 전체 화면 모달 등이 대표적이다. 이럴 때 가장 먼저 떠올리는 CSS 단위가 vh(Viewport Height)다.
그런데 height: 100vh를 적용했는데도 모바일에서 화면 하단이 잘리거나 예상하지 못한 스크롤이 생기는 경우가 있다.
모바일 브라우저는 스크롤에 따라 주소창이나 하단 툴바가 나타나거나 사라지는데, 이 과정에서 실제로 보이는 화면의 높이가 달라지기 때문이다. 이런 문제를 해결하기 위해 CSS에는 기존 vh 외에도 svh, lvh, dvh라는 새로운 뷰포트 단위가 추가되었다.
이번 글에서는 각 단위가 어떤 차이를 갖고 있는지, 실제 작업에서는 어떤 상황에 사용하는 것이 좋은지 정리해 본다.
Viewport와 vh 단위
먼저 뷰포트(Viewport)란, 브라우저에서 웹페이지 화면이 표시되는 영역을 말한다. CSS에서는 이 뷰포트의 크기를 기준으로 요소의 너비와 높이를 지정할 수 있는 단위가 있는데 바로 vw와 vh다.
- 1vw : 뷰포트 너비의 1%
- 1vh : 뷰포트 높이의 1%
- 100vw : 뷰포트 너비의 100%
- 100vh : 뷰포트 높이의 100%
예를 들어 뷰포트 높이가 900px이라면 100vh는 900px, 50vh는 450px이 된다.
.hero {
height: 100vh;
}위 코드는 hero 요소의 높이를 뷰포트 높이의 100%로 지정한다. 데스크톱에서는 대부분 의도한 대로 동작하지만, 실제 모바일 환경에서는 다른 결과가 나타날 수 있다.
모바일에서 100vh가 문제가 되는 이유
모바일 브라우저에는 데스크톱과 달리 스크롤에 따라 나타나거나 사라지는 주소창과 하단 툴바가 있다.
예를 들어 모바일 브라우저의 화면 높이가 다음과 같다고 가정해 보자.
- 브라우저 UI가 표시된 상태 : 700px
- 브라우저 UI가 숨겨진 상태 : 800px
일반적인 모바일 브라우저에서 100vh는 주소창과 툴바가 숨겨진 상태인 800px을 기준으로 계산된다. 문제는 주소창이 표시되어 실제로 보이는 영역이 700px로 줄어들더라도, 요소의 높이는 여전히 800px이라는 점이다. 결국 화면 하단의 콘텐츠가 가려지거나 의도하지 않은 스크롤이 발생하게 된다.
즉, 100vh가 항상 현재 눈에 보이는 화면의 높이를 의미하는 것은 아니다.
이 문제를 보완하기 위해 CSS에서는 브라우저 UI의 표시 상태를 고려한 새로운 높이 단위를 제공한다.
svh, lvh, dvh는 무엇이 다를까?
세 가지 단위의 가장 큰 차이는 브라우저 UI가 표시되거나 숨겨졌을 때, 어떤 높이를 기준으로 계산하느냐에 있다.

svh (Small Viewport Height)
.hero {
height: 100svh;
}svh는 브라우저 UI가 표시된 상태의 최소 뷰포트 높이를 기준으로 하는 단위이다.
100svh를 적용하면 브라우저 UI가 표시된 상태에서도 요소를 화면 높이에 맞춰 배치할 수 있다. 모바일 브라우저의 주소창과 툴바가 펼쳐진 상태를 기준으로 하기 때문에 콘텐츠가 브라우저 UI에 가려지는 문제를 줄일 수 있다.
다만 스크롤하여 주소창이 사라지더라도 요소의 높이는 그대로 유지되기 때문에 브라우저 UI가 숨겨진 상태에서는 화면 하단에 여유 공간이 생길 수 있다.
· 주소창이 표시(확장)된 상태를 기준으로 계산
· 주소창의 변화와 관계없이 고정된 높이를 유지
· 콘텐츠가 주소창에 가려지는 문제를 줄일 수 있음
· 첫 화면에서 주요 콘텐츠와 CTA 버튼이 잘리지 않고 보여야 하는 모바일 랜딩 페이지에 적합
lvh (Large Viewport Height)
.hero {
height: 100lvh;
}lvh는 브라우저 UI가 숨겨진 상태의 최대 뷰포트 높이를 기준으로 하는 단위이다.
100lvh를 적용하면 브라우저 UI가 사라졌을 때 확보할 수 있는 최대 높이를 기준으로 한다. svh와 마찬가지로 고정값을 유지하기 때문에 브라우저 UI가 표시된 모바일 환경에서는 의도치 않은 스크롤이 발생할 수 있다. 한마디로, 100vh와 동일하게 동작한다.
· 주소창이 숨겨진(축소된) 상태를 기준으로 계산
· 주소창의 변화와 관계없이 고정된 높이를 유지
· 주소창이 표시(확장)되면 콘텐츠 일부가 화면 밖으로 벗어나 스크롤이 발생할 수 있음
dvh (Dynamic Viewport Height)
.hero {
height: 100dvh;
}dvh는 이름에서 알 수 있듯이 브라우저 UI의 표시 여부에 따라 동적으로 변하는 뷰포트 높이를 기준으로 하는 단위이다.
예를 들어 주소창이 표시된 상태에서 화면 높이가 700px이라면 100dvh는 700px이 되며, 이후 스크롤하여 주소창이 사라지고 표시 영역이 800px로 늘어나면 100dvh 역시 800px로 변경된다.
· 주소창의 변화에 따라 유동적으로 계산
· 주소창의 변화에 따라 요소의 높이도 함께 변하므로 스크롤 시 레이아웃이 부자연스럽게 움직일 수 있다는 점을 주의
· 온보딩이나 스플래시 화면 같은 스크롤이 없는 풀사이즈 페이지에 적합
그럼 실무에서는 어떤 단위를 사용하면 좋을까?
| 단위 | 높이 기준 | 동적 변화 |
| vh | 전체 뷰포트 | ✕ |
| svh | 축소된 뷰포트 | ✕ |
| lvh | 확장된 뷰포트 (= 전체 뷰포트) | ✕ |
| dvh | 현재 뷰포트 | ○ |
단위별 차이를 이해했다면 이제 실제 작업에서 어떻게 활용할 수 있는지 살펴보자.
Case 01.
모바일 랜딩 페이지의 메인 비주얼
추천 단위 : svh

모바일 랜딩 페이지에서 메인 비주얼을 화면 전체 높이로 구현하는 상황이다. 이때 중요한 것은 사용자가 처음 페이지에 진입했을 때 주요 콘텐츠와 버튼이 잘리지 않고 보이는 것이다.
.hero {
display: flex;
flex-direction: column;
justify-content: center;
min-height: 100svh;
}100svh를 사용하면 브라우저 주소창이 표시된 상태를 기준으로 레이아웃을 구성할 수 있다. 또한 height 대신 min-height를 사용하면 콘텐츠가 많아졌을 때 영역이 자연스럽게 늘어나기 때문에 작은 화면에도 대응하기 좋다.
Case 02.
모바일 전체화면 메뉴
추천 단위 : dvh 또는 고정위치 기반 레이아웃

햄버거 버튼을 클릭했을 때 화면 전체를 덮는 내비게이션 메뉴를 구현하는 경우다. 이런 UI는 브라우저 주소창의 표시 상태에 관계없이 현재 보이는 화면에 맞춰 높이를 조정하는 것이 좋다.
.mobile-menu {
position: fixed;
inset: 0;
height: 100dvh;
overflow-y: auto;
}100dvh를 사용하면 브라우저 UI의 표시 상태에 따라 메뉴 영역의 높이가 변경된다. 다만 실제 구현에서는 position: fixed와 inset: 0만으로도 필요한 영역을 채울 수 있으므로, 반드시 dvh를 지정해야 하는 것은 아니다.
Case 03.
모바일 전체화면 모달
추천 단위 : dvh

모바일에서 화면 전체를 사용하는 모달을 구현하는 경우다. 특히 모달 내부에 콘텐츠가 많아 스크롤이 필요한 상황이라면 높이뿐 아니라 스크롤 영역도 함께 고려해야 한다.
.modal {
display: flex;
flex-direction: column;
position: fixed;
inset: 0;
height: 100dvh;
}
.modal__content {
flex: 1;
min-height: 0;
overflow-y: auto;
}모달의 높이는 현재 뷰포트에 맞추고, 내부 콘텐츠에는 별도의 스크롤 영역을 지정하는 방식이다. 이렇게 하면 화면이 작은 모바일 환경에서도 모달 내부의 콘텐츠를 스크롤하여 확인할 수 있다.
다만 입력 필드에 포커스가 생겨 가상 키보드가 표시되는 경우에는 별도의 대응이 필요할 수 있다. dvh가 항상 가상 키보드의 높이까지 반영하는 것은 아니기 때문이다.
Case 04.
화면을 가득 채우는 배경 비주얼
추천 단위 : vh 또는 lvh

배경 이미지나 비디오를 화면 전체 사이즈로 표시하고 싶은 경우다.
.visual {
height: 100lvh;
overflow: hidden;
}
.visual video {
width: 100%;
height: 100%;
object-fit: cover;
}100vh 또는 100lvh를 사용하면 브라우저 UI가 숨겨진 상태의 최대 높이를 기준으로 비주얼 영역을 구성할 수 있다. 고정 높이값을 가지므로 스크롤에 따라 레이아웃이 움직이는 현상도 방지할 수 있다.
다만 주소창이 표시된 상태에서는 하단 영역이 화면 밖으로 벗어날 수 있으므로, 중요한 텍스트나 버튼보다는 배경 이미지와 영상 중심의 레이아웃에 활용하는 편이 좋다.
브라우저 호환성과 Fallback 처리
svh, lvh, dvh는 최신 브라우저에서 폭넓게 지원되지만, 오래된 브라우저까지 고려해야 한다면 대체 스타일(Fallback)을 함께 작성하는 것이 좋다.
가장 간단한 방법은 기존 vh를 먼저 선언하고 새로운 단위를 이어서 작성하는 것이다.
.hero {
min-height: 100vh;
min-height: 100svh;
}CSS는 동일한 우선순위의 선언에서 나중에 작성된 값을 적용한다. 따라서 svh를 지원하는 브라우저에서는 100svh가 적용되고, 지원하지 않는 브라우저에서는 기존 100vh가 유지된다.
@supports를 활용해 명시적으로 구분하는 방법도 있다.
.hero {
min-height: 100vh;
}
@supports (height: 100svh) {
.hero {
min-height: 100svh;
}
}두 방법 모두 사용할 수 있지만, 단순히 단위 지원 여부만 확인하는 경우라면 첫 번째 방법이 더 간결하다. 실제 프로젝트에서는 지원 대상 브라우저와 기기 환경을 확인한 후 적용하는 것이 좋다.
사용하기 전에 알아둘 점
새로운 뷰포트 단위가 기존 vh의 문제를 보완해 준다고 해서 모든 상황에서 dvh를 사용하는 것이 정답은 아니다.
몇 가지 주의할 부분도 있다.
1. dvh 사용 시 레이아웃 변경에 주의
dvh는 브라우저 UI의 상태에 따라 높이가 유동적으로 달라진다. 스크롤 도중 주소창이 숨겨지면 요소의 높이가 변경되고, 내부 콘텐츠의 위치도 함께 움직일 수 있다.
특히 스크롤이 발생하는 일반적인 페이지에서는 브라우저 UI의 변화에 따라 dvh가 적용된 요소의 높이가 동적으로 변하면서 레이아웃이 불안정해질 수 있으므로, 가급적 사용을 자제하는 것이 좋다.
2. height와 min-height의 차이
높이를 반드시 고정해야 하는 상황이 아니라면 height보다 min-height를 사용하는 편이 유연하다. height는 요소의 높이를 지정한 값으로 설정하지만, min-height는 콘텐츠가 많아지면 요소의 높이가 늘어날 수 있다.
특히 반응형 웹에서는 화면 크기뿐 아니라 텍스트 길이, 콘텐츠 수, 사용자의 글자 크기 설정 등도 함께 고려해야 한다.
3. 마무리하며
vh, svh, lvh, dvh는 모두 뷰포트의 높이를 기준으로 하는 CSS 단위지만, 브라우저 UI를 반영하는 방식에는 차이가 있다.
간단히 정리하면 다음과 같다.
- svh : 브라우저 UI가 표시된 상태를 기준으로 일정한 높이가 필요할 때
- lvh : 브라우저 UI가 숨겨진 상태의 최대 높이를 기준으로 할 때
- dvh : 현재 보이는 화면에 맞춰 높이가 변경되어야 할 때
- vh : 기존 방식의 뷰포트 높이가 필요할 때
모바일에서는 주소창과 툴바의 표시 상태가 레이아웃에 직접적인 영향을 줄 수 있다. 따라서 무조건 100vh나 100dvh를 적용하기보다는 해당 UI가 어떤 상황에서 어떻게 보이는지 실제 모바일 기기에서 테스트 해보는 것이 좋다.
작은 단위의 차이지만, 용도에 맞게 사용하면 모바일에서 발생하는 불필요한 스크롤이나 콘텐츠 잘림 문제를 줄이는 데 도움이 된다.