안녕하세요! 프론트엔드 개발 강사입니다. 이번에 가져오신 문서는 반응형 웹 디자인과 모바일 웹 개발의 핵심인 뷰포트(Viewport)에 대한 내용이네요!
처음 웹 개발을 할 때 "화면 크기 100%로 맞췄는데 모바일에서 왜 글씨가 깨알같이 보이지?" 하거나 "가상 키보드가 올라오니까 화면이 이상해지네?" 같은 경험을 해보셨을 거예요. 이 모든 게 바로 이 '뷰포트' 개념과 연결되어 있습니다. 영문 문서라 조금 딱딱하게 느껴지셨을 텐데, 제가 이해하기 쉬운 구어체로 싹 번역해 드리고 실무에서 알아야 할 꿀팁도 팍팍 얹어드릴게요! 😊
이 문서는 뷰포트(viewport)의 개념 — 뷰포트가 무엇인지, 그리고 CSS, SVG, 모바일 기기 측면에서 어떤 영향을 미치는지 설명합니다. 이 문서에서는 초기 뷰포트(initial viewport)와 실제 뷰포트(actual viewport)를 정의하고, 시각적 뷰포트(visual viewport)와 레이아웃 뷰포트(layout viewport)의 차이점을 구분하여 살펴봅니다.
뷰포트(viewport)는 연속적인 미디어(continuous media, 스크롤이 가능한 웹페이지 등)를 위한 '초기 컨테이닝 블록(initial containing block)'을 설정하는 데 사용되는 사용자 에이전트(브라우저)의 기능입니다.
💡 강사의 부연 설명: "컨테이닝 블록이 뭔가요?"
쉽게 말해, 웹 요소들의 크기나 위치를 결정할 때 기준이 되는 '부모 영역'이라고 생각하시면 됩니다. 우리가width: 100%를 줄 때 "대체 무엇의 100%인가?"를 결정하는 최상위 기준점이 바로 뷰포트가 만들어내는 초기 컨테이닝 블록입니다.
컴퓨터 그래픽스에서 뷰포트라는 일반적인 용어는 주로 '현재 화면에 보여지고 있는 영역'을 뜻합니다. 웹 브라우저의 관점에서는 일반적으로 브라우저의 UI, 메뉴 바 등을 제외한 브라우저 창(window)의 내부 영역과 같습니다. 즉, 여러분이 현재 눈으로 보고 있는 문서의 그 부분이 바로 뷰포트입니다.
문서가 로드될 때, 뷰포트는 두 단계를 거칩니다:
초기 뷰포트 (Initial viewport)초기 뷰포트는 브라우저의 기본 스타일(user agent styles), HTML <meta> 태그, 또는 CSS 스타일이 뷰포트의 크기를 덮어쓰기(override) 전의 브라우저 창이나 보기 영역을 의미합니다. 초기 뷰포트의 크기는 내용물(content)의 크기가 아니라 창이나 보기 영역의 크기를 기준으로 합니다. 전체 화면 브라우저의 초기 뷰포트 크기는 기기와 가로/세로 방향(orientation)에 따라 다르지만, 같은 기기에서 같은 방향일 때는 항상 동일합니다.
실제 뷰포트는 뷰포트 <meta> 태그를 처리한 후에 얻게 되는 뷰포트입니다. 넓은 뷰포트용으로 디자인된 콘텐츠를 좁은 뷰포트에서 보면 의도치 않은 줄바꿈, 잘린(clipped) 콘텐츠, 크기가 잘못 계산된 스크롤 컨테이너 등 다양한 버그가 나타날 수 있습니다. 뷰포트 메타 태그는 이 뷰포트의 초기 크기에 대한 힌트를 브라우저에 제공합니다. 실제 뷰포트의 크기는 메타 태그의 content 속성에 의해 정의됩니다. 만약 이 태그를 생략하면, 일부 모바일 브라우저는 일반적으로 980px이라는 고정된 너비로 초기 컨테이닝 블록을 설정하여 콘텐츠를 렌더링해 버립니다. 즉, 실제 뷰포트의 너비를 980px로 맞춘 뒤 좁은 모바일 화면에 들어가도록 화면 전체를 축소(scale down)시켜 버립니다. 그 결과 CSS의 1픽셀이 실제 화면의 1픽셀보다 훨씬 작아지게 되죠 (글씨가 깨알처럼 작아지는 현상입니다).
이 문서와 같은 웹 문서는 길이가 아주 길 수 있습니다. 여러분의 뷰포트는 "현재 화면에 보이는 모든 것"입니다. 예를 들어 "뷰포트란 무엇인가요" 섹션과 네비게이션 메뉴 일부가 보일 수 있죠. 뷰포트의 크기는 화면의 크기, 브라우저가 전체 화면(fullscreen) 모드인지 아닌지, 그리고 브라우저 화면을 확대(zoom in)했는지 여부에 따라 달라집니다. 이 문서의 같이 보기(See Also) 섹션처럼 뷰포트 바깥에 있는 콘텐츠는 스크롤해서 시야에 들어오기 전까지는 화면에 보이지 않습니다.
페이지 기반 미디어(paged media)(예: 인쇄물)의 경우, 초기 컨테이닝 블록은 페이지 영역(page area)을 기준으로 합니다. 이 페이지 영역은 @page 규칙을 통해 설정할 수 있습니다.
요약하자면, 뷰포트는 기본적으로 현재 눈에 보이는 문서의 부분입니다.
뷰포트의 너비가 항상 브라우저 창의 너비와 일치하는 것은 아닙니다. Chrome이나 Firefox에서 창(window)과 문서(document)의 너비나 높이를 조회해 보면 다음과 같은 결과를 얻을 수 있습니다:
document.documentElement.clientWidth; /* 1200 */
window.innerWidth; /* 1200 */
window.outerWidth; /* 1200 */
document.documentElement.clientHeight; /* 800 */
window.innerHeight; /* 800 */
window.outerHeight; /* 900 */
뷰포트 크기 및 이와 유사한 길이들을 조회하는 데 도움이 되는 몇 가지 DOM 속성들이 있습니다:
Element.clientWidth는 CSS 픽셀 단위로 계산된 문서의 내부 너비입니다. 여기에는 패딩(padding)은 포함되지만, 테두리(borders), 여백(margins), 그리고 (만약 렌더링되었다면) 세로 스크롤바의 너비는 제외됩니다. 이것이 바로 뷰포트 너비입니다.Window.innerWidth는 브라우저 창 뷰포트의 너비를 CSS 픽셀 단위로 나타냅니다. 여기에는 세로 스크롤바가 화면에 나타나 있다면 스크롤바의 너비까지 포함됩니다.Window.outerWidth는 모든 창 크롬(chrome)(브라우저의 UI 부분)을 포함한 브라우저 창 외부의 전체 너비입니다.💡 강사의 실무 팁! "스크롤바가 골칫거리!"
실무에서 요소의 너비를100vw로 주었을 때, 가로 스크롤바가 갑자기 생기면서 화면이 약간 좌우로 흔들리는 버그를 겪어본 적 있나요? 이는100vw가 스크롤바 영역까지 포함한window.innerWidth를 기준으로 계산되기 때문입니다! 반면100%는 스크롤바를 제외한clientWidth를 따릅니다. 이 차이를 아는 것이 중요해요!
이것들을 가지고 실험해 보았을 때, innerWidth와 outerWidth는 같게 나타났지만, outerHeight는 innerHeight보다 100px 더 크게 나타났습니다. 이는 outerHeight에 브라우저 크롬(UI)이 포함되기 때문입니다: 측정 당시 브라우저 상단에는 주소 표시줄과 북마크 바가 합쳐서 100px 높이로 있었지만, 창 좌우에는 크롬 영역이 없었기 때문이죠.
innerHeight와 innerWidth 내부의 영역을 일반적으로 레이아웃 뷰포트(layout viewport)라고 부릅니다. 브라우저 크롬은 뷰포트의 일부로 간주되지 않습니다.
화면을 확대(zoom in)했을 때, Firefox와 Chrome 모두 innerWidth와 clientWidth에 대해 변경된 CSS 픽셀 크기를 보고합니다. 하지만 outerWidth와 outerHeight에 반환되는 값은 브라우저마다 다릅니다: Firefox는 새로운 CSS 픽셀 값으로 반환하지만, Chrome은 기본 픽셀 크기 그대로의 길이를 반환합니다. 화면을 확대하면 다음과 같은 결과를 얻을 수 있습니다:
document.documentElement.clientWidth; /* 800 */
window.innerWidth; /* 800 */
window.outerWidth; /* Firefox에서는 800, Chrome에서는 1200 */
document.documentElement.clientHeight; /* 533 */
window.innerHeight; /* 533 */
window.outerHeight; /* Firefox에서는 596, Chrome에서는 900 */
원래 뷰포트는 1200 x 800 픽셀이었습니다. 키보드로 화면을 확대한 후, 뷰포트는 800 x 533 픽셀이 되었습니다. 이것이 바로 레이아웃 뷰포트입니다. 다음 스타일을 사용한 고정(sticky/fixed) 헤더나 푸터는 각각 레이아웃 뷰포트의 상단과 하단에 고정됩니다.
body > header {
position: fixed;
top: 0;
}
body > footer {
position: fixed;
bottom: 0;
}
키보드를 사용해 확대했을 때 우리는 800 x 533이라는 측정값을 얻었고, 헤더와 푸터는 창의 위아래에 착 붙어 유지되었습니다. 하지만 태블릿에서 손가락으로 꼬집어서 확대(pinch-zoom)를 했다면 어떨까요? 또는 휴대폰에서 글자를 입력하려고 가상 키보드가 튀어나왔다면요?
웹에는 두 가지 뷰포트가 존재합니다. 바로 레이아웃 뷰포트(layout viewport)와 시각적 뷰포트(visual viewport)입니다. 시각적 뷰포트는 브라우저에서 현재 실제로 눈에 보이는 웹 페이지의 영역을 말하며, 상황에 따라 유동적으로 변할 수 있습니다. 사용자가 화면을 꼬집어 확대(pinch-zoom)하거나, 화면 하단에서 동적 키보드가 올라오거나, 숨겨져 있던 주소 표시줄이 다시 나타날 때, 시각적 뷰포트는 줄어들지만 레이아웃 뷰포트는 변하지 않습니다.
위에서 언급한 고정된(fixed) 헤더나 푸터는 레이아웃 뷰포트의 상하단에 달라붙습니다. 따라서 키보드(단축키)를 이용해 브라우저 줌을 할 때는 여전히 시야에 머물러 있습니다. 하지만 핀치 줌(pinch-zoom)을 하게 되면 레이아웃 뷰포트의 전체가 화면에 보이지 않을 수 있습니다. 레이아웃 뷰포트의 한가운데를 확대하면 콘텐츠가 사방으로 밀려나며 커집니다. 이때 고정 헤더나 푸터는 여전히 레이아웃 뷰포트의 위아래 끝에 붙어있긴 하지만, 기기 화면(즉, 시각적 뷰포트)의 상하단에는 보이지 않게 될 수 있습니다. 시각적 뷰포트는 레이아웃 뷰포트 안에서 현재 눈에 보이는 일부 영역일 뿐이기 때문이죠. 스크롤을 내려 시각적 뷰포트의 내용을 바꾸다 보면 레이아웃 뷰포트의 하단이 화면에 들어오게 되고, 그때서야 아래에 고정되어 있던 푸터가 보이게 됩니다.
시각적 뷰포트는 화면에 떠오른 가상 키보드, 핀치 줌 영역 밖의 공간, 또는 페이지의 전체 크기와 함께 조절되지 않는 다른 요소들을 제외한 순수한 시각적 화면 영역입니다. 시각적 뷰포트는 레이아웃 뷰포트와 크기가 같거나 더 작습니다.
iframe, object, 또는 외부 SVG를 포함하는 페이지의 경우, 부모 페이지와 각각의 포함된 파일들은 모두 고유한 창(window) 객체를 갖습니다. 이 중 최상위 창(top-level window)만이 레이아웃 뷰포트와 구별되는 시각적 뷰포트를 가질 수 있습니다. 내부로 포함된(included) 문서들의 경우, 시각적 뷰포트와 레이아웃 뷰포트는 동일합니다.
💡 강사의 실무 팁!
아이폰(iOS Safari)에서 하단에 고정된 메뉴바(position: fixed; bottom: 0;)를 만들었는데, 스크롤할 때 브라우저의 하단 주소창이 튀어나오면서 메뉴바를 덮어버리거나 위로 밀려 올라가는 현상 겪어보셨죠? 이게 바로 레이아웃 뷰포트와 시각적 뷰포트의 괴리 때문에 생기는 유명한 모바일 UI 버그입니다. 최근에는 이를 해결하기 위해dvh(Dynamic Viewport Height) 같은 새로운 CSS 단위가 등장했어요!
위에서 설명한 레이아웃 뷰포트와 시각적 뷰포트가 여러분이 마주칠 뷰포트의 전부는 아닙니다. 레이아웃 뷰포트 내에 전체 또는 일부가 표시되는 모든 하위 뷰포트(sub-viewport)도 일종의 시각적 뷰포트로 간주됩니다.
우리는 일반적으로 미디어 쿼리의 width와 height가 브라우저 창의 너비와 높이를 기준으로 한다고 생각합니다. 엄밀히 말하자면 이는 뷰포트를 기준으로 합니다. 메인 문서에서는 뷰포트가 브라우저 창을 의미하지만, object, iframe, SVG처럼 중첩된 브라우징 컨텍스트(nested browsing context)에서는 요소 부모의 고유한 크기(intrinsic size)가 기준이 됩니다. CSS에는 뷰포트 크기를 기반으로 하는 길이 단위도 있습니다. vh 단위는 레이아웃 뷰포트 높이의 1%를 나타내고, 마찬가지로 vw 단위는 레이아웃 뷰포트 너비의 1%를 나타냅니다.
<iframe><iframe> 내부에서, 시각적 뷰포트는 부모 문서의 크기가 아니라 iframe 자체의 내부 너비와 높이 사이즈가 됩니다. iframe에 너비와 높이를 설정할 수는 있지만, 내부 문서 전체가 다 보이지는 않을 수 있습니다.
iframe 문서 내부의 CSS에서 뷰포트 길이 단위를 사용한다면, 1vh는 iframe 높이의 1%가 되고, 1vw는 문서 너비의 1%가 됩니다.
iframe {
width: 50vw;
}
만약 우리 예제에서 부모 문서의 너비가 1200px이고 iframe에 50vw를 설정했다면, 부모 문서 너비의 50%인 600px이 될 것이며 내부에서 1vw는 6px이 됩니다. 화면을 확대(zoom in)하여 뷰포트가 줄어들면 iframe도 400px로 줄어들고 1vw는 4px이 됩니다.
iframe 문서 내부에서 작성된 너비 기반의 미디어 쿼리는 iframe 자체의 뷰포트를 기준으로 동작합니다.
@media screen and (width <= 500px) {
p {
color: red;
}
}
위 CSS가 iframe 내부에 포함되어 있다면, 사용자가 화면을 확대해서 iframe의 뷰포트 너비가 500px 이하로 줄어들었을 때 <p> 태그들이 빨간색으로 변할 것입니다. 하지만 확대하지 않은 기본 상태에서는 이 스타일이 적용되지 않겠죠.
SVG 문서에서 뷰포트는 SVG 이미지의 가시 영역(visible area)입니다. <svg> 요소에 어떤 너비나 높이든 지정할 수 있지만, 전체 이미지가 다 보이지 않을 수도 있습니다. 눈에 보이는 그 영역을 바로 뷰포트라고 부릅니다. 뷰포트의 크기는 <svg> 요소의 width와 height 속성을 이용해 정의할 수 있습니다.
<svg height="300" width="400"></svg>
이 예제에서 뷰포트는 3:4의 종횡비(aspect ratio)를 가지며, 기본적으로 400 x 300 유닛 크기입니다. (일반적으로 여기서 1유닛은 CSS 1픽셀과 같습니다.)
SVG는 이 뷰포트 논의와는 별개로 viewBox 속성을 통해 정의되는 내부 좌표계(coordinate system)도 별도로 가지고 있습니다.
만약 HTML 문서 안에 SVG 파일을 포함시킨다면, SVG의 뷰포트는 초기 컨테이닝 블록(SVG 컨테이너의 너비와 높이)이 됩니다. SVG의 CSS 내부에서 @media 쿼리를 사용하면 브라우저 창이 아니라 그 컨테이너를 기준으로 평가됩니다.
@media screen and (400px <= width <= 500px) {
/* CSS 코드가 여기에 들어갑니다 */
}
일반적으로 메인 HTML에서 위와 같은 미디어 쿼리를 작성하면, 뷰포트(주로 브라우저 창)가 400px에서 500px 사이일 때 스타일이 적용됩니다. 하지만 SVG 내부에 있는 너비 미디어 쿼리는 브라우저 뷰포트가 아니라 SVG를 감싸고 있는 컨테이너 요소에 상대적으로 작동합니다. 소스가 SVG 파일인 경우 <img> 요소가 기준이 되고, SVG가 HTML에 직접 삽입된(인라인) 경우 SVG 자체가 기준이 됩니다. 따라서 위의 미디어 쿼리가 SVG 파일 내에 있다면, 이 CSS는 SVG 컨테이너의 너비가 400px에서 500px 사이일 때만 적용됩니다.
자바스크립트의 VisualViewport 인터페이스는 시각적 뷰포트의 속성들을 조회하고 조작할 수 있는 메커니즘을 제공합니다.
마찬가지로 Viewport 인터페이스는 뷰포트의 속성들을 쿼리하고 수정할 수 있는 방법을 제공합니다.
모바일 기기는 크기와 모양이 매우 다양하며, 디바이스 픽셀(device pixel) 비율도 각기 다릅니다. 모바일 브라우저의 뷰포트는 웹 콘텐츠가 보여지는 창의 영역을 뜻하는데, 렌더링된 전체 페이지의 크기와 반드시 일치하는 것은 아닙니다.
모바일 브라우저는 가상의 창 또는 뷰포트(일반적으로 화면보다 훨씬 넓은 980px)에서 페이지를 먼저 렌더링한 다음, 사용자가 한눈에 화면을 다 볼 수 있도록 렌더링된 결과물을 화면 크기에 맞춰 축소(shrink down)시킵니다. 그러면 사용자가 직접 화면을 이리저리 문지르고(pan) 줌인해서 페이지의 특정 부분을 읽어야 하죠.
예를 들어, 스마트폰 화면 너비가 실제로는 320px인데, 웹사이트가 980px의 가상 뷰포트로 렌더링된 후 320px 공간에 맞게 확 축소된다면, 디자인에 따라 다르겠지만 대부분의 사람들은 글씨가 너무 작아 도저히 읽을 수 없게 됩니다.
모바일 브라우저에게 "기본값인 980px 대신 기기의 실제 화면 너비를 뷰포트 너비로 사용해라!"라고 알려주기 위해, 개발자들은 HTML 문서의 <head> 태그 안에 다음과 같은 뷰포트 메타 태그를 반드시 포함해야 합니다:
<meta name="viewport" content="width=device-width" />
💡 강사의 실무 팁! "반응형 웹의 시작이자 끝!"
width=device-width는 반응형 웹사이트를 만들 때 가장 먼저 적어 넣어야 하는 마법의 주문입니다. 이걸 넣지 않으면 기껏 작성한 미디어 쿼리가 모바일에서 전혀 작동하지 않고 PC 화면이 작게 축소되어 보일 거예요.
width 속성은 뷰포트의 크기를 제어합니다. 이 값은 가급적 100% 비율에서의 기기 화면 너비(CSS 픽셀 기준)를 뜻하는 device-width로 설정하는 것이 좋습니다. 이 외에도 사용자가 화면을 확대/축소할 수 있는지 제어하는 maximum-scale, minimum-scale, user-scalable 등의 다른 속성들도 있습니다. 하지만 접근성(accessibility)과 사용자 경험 측면에서는 기본적으로 확대를 허용하는 것이 가장 좋으므로, 이런 확대 제한 속성들은 생략하는 것이 권장됩니다.
<meta>, 특히 <meta name="viewport">이 페이지가 도움이 되셨나요? [네 (Yes)] / [아니요 (No)]
기여하는 방법 알아보기 (Learn how to contribute)
이 페이지는 2026년 3월 9일에 MDN 기여자들 (MDN contributors)에 의해 마지막으로 수정되었습니다.