Skip to content
This page has been auto-translated and may contain errors.View in English

크기와 단위

docs.scrimba.com

픽셀 단위로 폰트 크기를 정해서 페이지를 배포했는데, 눈이 편하도록 브라우저 글자 크기를 키워둔 독자에게는 아무 변화가 없습니다. 카드에 고정 너비를 줬는데 폰에서는 화면 밖으로 넘칩니다. 두 문제 모두 결국 같은 질문으로 이어집니다. CSS에 숫자를 적었을 때, 그 숫자는 무엇을 기준으로 재는 걸까요? 어떤 단위는 고정되어 있고, 어떤 단위는 폰트에 맞춰 커지고, 어떤 단위는 부모 요소에 맞춰 커지고, 어떤 단위는 화면에 맞춰 커집니다. 어떤 단위가 무엇을 기준으로 하는지 알면, 더 이상 감으로 찍지 않고 목적에 맞는 단위를 골라 쓸 수 있습니다.

절대 단위 vs 상대 단위

CSS 단위는 두 부류로 나뉩니다. **절대 단위**는 절대 변하지 않는 고정된 크기입니다. px(픽셀)가 어디서나 마주치게 될 대표적인 절대 단위입니다. 상대 단위는 독자의 폰트 크기나 화면 너비처럼 다른 무언가에 맞춰 커지는 크기입니다.

방 크기를 재는 상황을 떠올려 보세요. 센티미터는 절대적입니다. 키가 크든 작든 벽의 길이는 400cm입니다. "내 걸음으로 세 걸음"은 상대적입니다. 누구의 걸음이냐에 따라 달라지니까요. 절대 단위는 정확한 통제를 주고, 상대 단위는 페이지가 보는 사람에 맞춰 적응하게 해 줍니다.

이 적응이 중요합니다. 어떤 독자는 눈이 편하도록 기본 글자 크기를 더 크게 설정해 둡니다. 텍스트 크기를 상대 단위로 정하면 그 설정이 그대로 반영되어 페이지가 그 사람에 맞게 커집니다. 픽셀로 고정하면 그 자리에 그대로 머물러 그 설정을 무시합니다.

CSS의 모든 길이는 절대 단위이거나 상대 단위이며, 이 차이가 레이아웃이 적응할 수 있는지를 결정합니다. **절대 단위**는 맥락과 무관하게 항상 고정된 크기로 결정됩니다. 실무에서 쓰는 절대 단위는 사실상 px 하나뿐이며, cm, pt 등은 인쇄용 단위입니다. 상대 단위는 어떤 기준을 놓고 그에 맞춰 크기가 정해지는데, 그 기준은 단위마다 다릅니다. remem은 폰트 크기에 맞춰 커지고, %는 부모 요소에 맞춰 커지고, vwvh는 뷰포트에 맞춰 커집니다.

대부분의 크기 지정에서 상대 단위를 선호하는 이유는 접근성과 반응성입니다. 독자가 브라우저의 기본 글자 크기를 키우면 rem으로 지정한 요소는 함께 커지지만, px로 고정한 요소는 그대로 남아 주변 레이아웃을 깨뜨릴 수 있습니다. 화면 크기가 다를 때도 마찬가지입니다. 상대 단위를 쓰면 하나의 스타일시트로 휴대폰과 모니터에 모두 맞출 수 있고, 이것이 반응형 디자인의 기초가 됩니다.

어떤 단위를 고를지는 결국 그 숫자가 무엇에 고정되어 있는지를 정하는 결정이며, 그 기준이 레이아웃이 독자에 반응하는지 무시하는지를 결정짓습니다. **절대 단위**는 어떤 맥락에서도 항상 같은 계산된 길이로 결정됩니다. 화면용 CSS에서는 사실상 px만이 여기에 해당합니다. 물리적 단위(cm, mm, in, pt, pc)는 어차피 픽셀을 기준으로 정의되어 있고(1in은 정의상 96px) 인쇄용 스타일시트에 속합니다. 상대 단위는 변할 수 있는 기준을 놓고 그에 맞춰 크기가 정해지며, 바로 이 점이 적응력을 만들어 줍니다.

기준이 곧 핵심이므로, 상대 단위 각각이 무엇을 가리키는지 짚어볼 필요가 있습니다. remem폰트 상대적입니다. 루트 요소나 해당 요소의 폰트 크기를 기준으로 정해집니다. %는 대부분의 속성에서 부모 상대적입니다. 퍼센트 너비는 포함 블록 너비의 일부분입니다. vwvh뷰포트 상대적입니다. 브라우저 뷰포트(페이지에서 보이는 영역)의 일부분입니다. 여기서 실무 규칙이 자연스럽게 나옵니다. 타입과 레이아웃은 상대 단위로 지정하고, 크기가 변하면 안 되는 부분에만 px를 남겨두세요. 독자가 기본 글자 크기를 더 크게 설정하거나 페이지를 확대하면, 상대 단위가 가리키는 기준 자체가 움직이므로 페이지가 그에 맞춰 커집니다. 반면 px 값은 움직일 기준이 없어 그대로 남아 있고, 이것이 바로 여러분이 피하고자 하는 접근성 문제입니다.

Juno절대 단위 vs 상대 단위px와 같은 절대 단위는 절대 변하지 않는 고정된 크기입니다. 상대 단위는 독자의 폰트 크기나 화면 너비처럼 다른 무언가에 맞춰 커집니다. 상대 단위는 페이지가 보는 사람에 맞춰 적응하게 해 주기 때문에, 글자 크기를 더 크게 설정한 독자에게 중요합니다.
Juno절대 단위 vs 상대 단위px는 실제로 쓰게 될 유일한 절대 단위이고, 나머지는 모두 어떤 기준에 맞춰 크기가 정해집니다. remem은 폰트에 맞춰지고, %는 부모에 맞춰지고, vwvh는 뷰포트에 맞춰집니다. 독자가 기본 글자 크기를 키웠을 때 그 설정을 무시하지 않고 페이지가 함께 커지도록 상대 단위를 쓰세요.
Juno절대 단위 vs 상대 단위 모든 단위는 숫자와 기준의 조합이며, 그 기준이 레이아웃이 반응할 수 있는지를 결정합니다. px는 움직일 기준이 없어 절대 적응하지 않습니다. remem은 폰트를 기준으로, %는 부모를 기준으로, vwvh는 뷰포트를 기준으로 정해집니다. 타입과 레이아웃은 상대 단위로 지정하고, 고정되어야 하는 부분만 px로 남겨두세요.

픽셀과 그것이 적절한 경우

픽셀(px)은 작고 고정된 길이 단위입니다. 16px은 언제나 16px이라서 가장 직관적으로 다룰 수 있는 단위입니다. 그래서 무엇이 되든 크기가 그대로 유지되어야 하는 작은 요소, 예를 들어 테두리 두께, 작은 모서리 둥글기, 고정된 아이콘 같은 것에 잘 맞습니다.

css
.card {
  border: 1px solid #ccc;      /* 언제나 1px인 얇은 테두리선 */
  border-radius: 8px;          /* 고정된 모서리 둥글기 */
}

문제는 픽셀이 독자를 신경 쓰지 않는다는 점입니다. 누군가 브라우저에서 더 큰 글자를 표시하도록 설정해 놓았다면, 24px로 고정한 제목은 그 설정을 무시하고 그대로 24px에 머뭅니다. 그래서 px는 테두리나 작은 디테일에는 괜찮은 선택이지만, 텍스트에 쓸 단위는 아닙니다.

px는 크기가 그대로 유지되어야 할 때, 즉 테두리, 얇은 구분선, 작은 반경, 그리고 크기가 변하면 안 되는 아이콘 등에 쓰는 단위입니다. 이런 경우에는 고정되어 있다는 점 자체가 목적입니다. 1px 테두리가 독자의 글자 크기에 맞춰 커진다면 오히려 이상하게 보일 것이므로, 고정해 두는 것이 맞습니다.

css
.card {
  border: 1px solid #ccc;      /* 글자 크기와 무관하게 항상 얇은 테두리로 유지 */
  border-radius: 8px;
  padding: 1rem;               /* 하지만 내부 여백은 타입에 맞춰 커짐 */
}

단점은 텍스트 크기를 이 단위로 지정하지 말아야 하는 이유이기도 합니다. 독자는 편안함이나 접근성을 위해 브라우저에서 기본 글자 크기를 더 크게 설정할 수 있는데, px는 그 설정을 무시합니다. font-size: 18px인 제목은 독자의 기본 크기가 16이든 24이든 항상 18픽셀로 렌더링됩니다. 그러니 고정된 디테일에는 px를 쓰고, 독자의 설정을 반영해야 하는 부분에는 폰트 상대 단위로 바꿔 쓰세요. 이 내용은 다음 절에서 다룹니다.

CSS의 px는 물리적인 장치 픽셀이 아니라 기준 픽셀(reference pixel), 즉 화면 밀도가 다르더라도 1px이 일관된 시각적 크기로 보이도록 브라우저가 스케일링하는 각도 단위입니다. 고밀도 디스플레이에서는 하나의 CSS 픽셀이 여러 개의 실제 장치 픽셀에 대응하기 때문에, 1px 테두리가 사라지지 않고 또렷한 얇은 선으로 유지됩니다. 이 고정성 때문에 px는 독자의 글자 크기 설정을 따라가면 안 되는 것들, 즉 테두리, 1px 구분선, 작은 반경, box-shadow 오프셋, 얇은 선 디테일에 적합합니다.

css
.card {
  border: 1px solid #ccc;      /* 고정된 얇은 선. 크기가 변하면 이상해 보임 */
  border-radius: 8px;
  padding: 1rem;               /* 내부 여백은 대신 타입을 따라감 */
}

실패 사례는 텍스트를 px로 지정하는 경우입니다. font-size를 픽셀로 설정하면 독자의 기본 글자 크기 설정, 즉 브라우저 환경설정에서 (흔히 저시력을 위해) 조절할 수 있는 기본 크기를 무시하게 됩니다. px 값에는 그 설정에 맞춰 커질 기준이 아예 없기 때문입니다. 페이지 확대(zoom)와는 구분해야 합니다. 최신 브라우저는 전체 페이지 확대 시 px도 함께 스케일링하므로 "px는 확대를 무시한다"는 오래된 통설은 이제 정확하지 않지만, px 폰트 크기는 여전히 기본 글자 크기 설정만큼은 무시하며, 많은 독자가 바로 이 설정에 의존해 접근성을 확보합니다. 결국 지켜야 할 규칙은, 고정된 디테일에는 px를, 독자가 확대할 수 있어야 하는 부분에는 폰트 상대 단위를 쓰는 것입니다. 특히 본문 텍스트와 타이포그래피가 그렇습니다.

Juno픽셀과 그것이 적절한 경우 픽셀(px)은 고정된 크기라서 16px은 언제나 16px입니다. 그래서 1px 테두리나 작은 모서리 둥글기처럼 크기가 그대로 유지되어야 하는 작은 요소에 잘 맞습니다. 대신 px는 글자를 크게 설정한 독자를 무시하므로, 페이지의 글자에 쓸 단위는 아닙니다.
Juno픽셀과 그것이 적절한 경우 길이가 변하면 안 될 때, 즉 테두리, 얇은 구분선, 작은 반경 등에 px를 쓰세요. 이때는 고정되어 있다는 점이 장점입니다. 단점은 텍스트에서 드러나는데, px 폰트 크기는 독자의 더 큰 기본 설정을 무시하므로, 고정된 디테일에는 px를 쓰고 텍스트 크기는 폰트 상대 단위로 지정하세요.
Juno픽셀과 그것이 적절한 경우 CSS의 px는 화면 밀도가 다르더라도 시각적으로 일관되게 유지되는 기준 픽셀이며, 그래서 1px 테두리가 또렷한 얇은 선으로 유지됩니다. 이 때문에 테두리나 그림자 같은 고정 디테일에는 적합합니다. 텍스트에는 적합하지 않은데, px 폰트 크기는 독자의 기본 글자 크기 설정을 무시하기 때문입니다. 대신 타입은 rem으로 지정하세요.

em과 rem

폰트 크기에 맞춰 커지는 상대 단위 두 가지가 있습니다. emrem입니다. 이 중 먼저 써야 할 단위는 **rem**입니다. 이 단위는 페이지의 루트 폰트 크기, 즉 문서 전체에 설정된 기본 텍스트 크기(기본값 16px)를 기준으로 잡습니다. 그래서 1rem16px, 1.5rem24px, 2rem32px입니다.

css
h1 {
  font-size: 2rem;             /* 2 x 16px = 32px */
}
p {
  font-size: 1rem;             /* 16px, 기본 크기 */
  margin-bottom: 1.5rem;       /* 아래쪽 여백 24px */
}

rem은 이 단 하나의 루트 크기에 묶여 있기 때문에 예측하기 쉽습니다. 1.5rem은 페이지 어디서나 같은 값을 뜻합니다. 그리고 독자가 기본 텍스트 크기를 키우면 rem으로 지정한 모든 요소가 함께 커집니다. 반면 em은 해당 요소 자신의 폰트 크기를 기준으로 커지기 때문에 복잡하게 꼬일 수 있습니다. 그러니 rem부터 시작하면 실수할 일이 거의 없습니다.

emrem은 둘 다 폰트에 상대적인 단위이며, 차이는 어떤 폰트 크기를 기준으로 하느냐입니다. **rem**은 루트 폰트 크기, 즉 <html> 요소에 설정된 크기(기본값 16px)를 기준으로 하므로, 1.5rem은 페이지 어디에 쓰든 같은 길이로 계산됩니다. em해당 요소 자신의 폰트 크기를 기준으로 하므로, 어디에 쓰느냐에 따라 값이 달라집니다.

css
h1 {
  font-size: 2rem;             /* 항상 루트의 2배 = 32px */
}
.card {
  font-size: 1.125rem;         /* 18px */
  padding: 1em;                /* 이 요소의 18px x 1 = 18px */
}

권장 사항은 기본적으로 rem으로 크기를 정하고, em은 요소 자체의 폰트 크기에 맞춰 커져야 할 때만 사용하는 것입니다. em은 버튼 내부 여백처럼 자기 자신의 텍스트를 따라가야 하는 경우에 잘 맞습니다. 버튼의 폰트 크기가 커지면 여백도 함께 커지는 식입니다. 하지만 em은 요소가 중첩될 때마다 누적되어 사람들을 자주 놀라게 하므로, 이런 지역적인 경우에만 em을 남겨두고 전반적인 크기 지정과 타이포그래피rem에 맡기세요.

emrem은 둘 다 폰트 상대적입니다. 즉 고정된 길이가 아니라 폰트 크기를 기준으로 계산되며, 차이는 그 기준이 무엇이냐에 있습니다. rem(root em)은 루트 폰트 크기, 즉 <html> 요소에서 계산된 font-size를 기준으로 하므로 전역적으로 단 하나의 기준을 가집니다. 1.25rem은 제목이든, 하단 영역이든, 6단계 깊이로 중첩된 곳이든 항상 같은 계산 길이입니다. em현재 요소font-size를 기준으로 하므로, 그 의미는 지역적이며 맥락에 따라 달라집니다.

em이 누적되는 지점은 바로 이 지역적 기준 때문입니다. 자식 요소의 em은 자신의 font-size를 기준으로 계산되고, font-size 자체도 상속되므로, 중첩된 요소들에 em으로 font-size를 설정하면 단계마다 값이 곱해집니다.

css
/* 각 목록 단계마다 font-size를 em으로 설정하므로 크기가 곱해져 누적됨 */
ul { font-size: 0.9em; }       /* 부모의 0.9배 */

/* 16px 루트가 이렇게 변함:
   깊이 1 -> 14.4px
   깊이 2 -> 12.96px  (0.9 x 0.9)
   깊이 3 -> 11.66px  (0.9 x 0.9 x 0.9)  */

중첩될수록 글자가 점점 작아지는 것을 원하는 사람은 없습니다. 이것이 바로 em이 누적되는 문제이고, 기본적으로 rem을 쓰라고 하는 주된 이유입니다. 반대로 컴포넌트 지역적 크기 조정에서는 이야기가 다릅니다. 어떤 값이 하나의 요소 자신의 타입을 따라가야 할 때, 예를 들어 패딩이 0.75em이고 border-radius가 0.5em인 버튼은 font-size를 키우면 전체가 하나의 단위로 함께 다시 스케일링됩니다. rem은 지역 크기를 무시하기 때문에 이런 동작을 할 수 없습니다. 그래서 실무 구분은, 루트를 기준으로 안정적으로 유지되어야 하는 것에는 rem을, 자신의 요소 폰트 크기에 맞춰 커져야 하는 여백이나 반경에는 em을 쓰는 것입니다.

유동적인 크기 조정에는 min(), max(), clamp()를 쓰면 길이를 정해진 범위 안에서 유연하게 조절할 수 있습니다. min(a, b)는 현재 뷰포트에서 둘 중 더 작은 값을, max(a, b)는 더 큰 값을 선택하며, clamp(min, preferred, max)는 선호값을 하한과 상한 사이로 유지합니다. 제목에 흔히 쓰는 패턴은 font-size: clamp(1.75rem, 4vw, 3rem)입니다. 뷰포트(4vw)에 맞춰 커지지만 1.75rem 아래로 내려가거나 3rem을 넘어서지 않으므로, 브레이크포인트를 여러 개 쌓지 않고도 타입을 유동적으로 유지할 수 있습니다. 타입 크기를 rem으로(또는 rem으로 경계를 준 clamp()로) 지정하면 사용자의 확대나 독자의 기본 글자 크기 설정도 존중하게 되는데, 이는 이런 설정이 rem이 기준으로 삼는 루트 값 자체를 바꾸기 때문입니다. 반면 순수한 vw 값은 뷰포트만 따라가고 독자의 폰트 설정은 무시합니다. 그래서 clamp() 안의 rem 하한과 상한이 중요합니다. 이것이 표현식 안에 접근 가능한 기준을 남겨두는 방법입니다.

Junoem과 remremem은 둘 다 폰트 크기에 맞춰 커지는데, 먼저 써야 할 것은 rem입니다. 페이지의 루트 크기, 기본값 16px를 기준으로 잡으므로 1.5rem24px이고 어디서나 같은 값을 뜻합니다. em은 대신 해당 요소 자신의 폰트 크기를 기준으로 하기 때문에 복잡해질 수 있으니, rem을 먼저 쓰세요.
Junoem과 rem 둘 다 폰트 상대 단위입니다. rem은 루트 폰트 크기를 기준으로 하므로 어디서나 안정적이고, em은 해당 요소 자신의 폰트 크기를 기준으로 하므로 맥락에 따라 값이 달라지고 중첩되면 누적됩니다. 기본적으로 rem으로 크기를 정하고, 버튼 내부 여백처럼 자신의 타입을 따라가야 할 여백에만 em을 쓰세요.
Junoem과 remrem은 루트 폰트 크기를 기준으로 삼아 하나의 안정된 기준을 제공하고, em은 해당 요소 자신의 크기를 기준으로 삼아 중첩될수록 값이 곱해집니다. 그래서 깊게 중첩된 em 폰트 크기는 점점 어긋납니다. 일반적인 크기 지정에는 rem을, 자기 자신의 타입을 따라가야 하는 컴포넌트 지역 크기 조정에는 em을 쓰세요. 유동적인 타입에는 clamp(1.75rem, 4vw, 3rem)이 뷰포트에 맞춰 유연하게 움직이면서도, rem 하한과 상한이 사용자의 확대와 폰트 설정을 계속 존중하도록 해 줍니다.

퍼센트

퍼센트(%)는 부모 요소를 기준으로 크기를 정합니다. 너비의 경우 width: 50%는 그 요소가 담긴 상자의 너비가 얼마든 그 절반을 뜻합니다.

css
.sidebar {
  width: 25%;                  /* 부모 너비의 4분의 1 */
}
.content {
  width: 75%;                  /* 나머지 4분의 3 */
}

이것이 레이아웃을 유동적으로 만들어 주는 이유입니다. 너비가 부모의 일부분이기 때문에, 부모가 늘어나거나 줄어들면 함께 늘어나거나 줄어듭니다. 창 크기를 조절해도 50% 칼럼은 여전히 절반의 너비를 유지하며, 고정 500px 칼럼이었다면 화면 밖으로 넘칠 상황을 피하게 됩니다. 이런 유연함 때문에 퍼센트는 반응형 디자인에서 자주 등장합니다.

**퍼센트**는 부모의 어떤 기준값을 놓고 계산되며, width의 경우 그 기준은 부모의 콘텐츠 너비입니다. 그래서 width: 50%는 현재 포함 상자가 무엇이든 그 절반을 줍니다. 이것이 퍼센트 기반 칼럼을 유동적으로 만들어 주는 원리입니다. 부모가 크기를 바꾸면 함께 따라갑니다.

css
.container {
  width: 90%;                  /* 부모의 90%. 아래에서 상한을 둠 */
  max-width: 60rem;            /* 넓은 화면에서는 60rem을 넘지 않도록 제한 */
  margin: 0 auto;              /* 가운데 정렬 */
}

이 기준이 항상 너비인 것은 아니며, 여기서 사람들이 자주 걸려 넘어집니다. 퍼센트 padding이나 margin은, 심지어 수직 방향의 값이라도 부모의 너비를 기준으로 계산되고, 부모의 높이와는 무관합니다. 퍼센트 height는 부모의 높이를 기준으로 계산되지만, 부모가 계산 가능한 명확한 높이를 가지고 있을 때만 그렇습니다. 그렇지 않으면 기준으로 삼을 값이 없어서 무시됩니다. 위 예시처럼 퍼센트 너비와 rem 단위의 max-width를 함께 쓰는 것이 흔한 방법입니다. 일정 지점까지는 유동적이되, 그 이후로는 상한을 두어 줄이 읽기 힘들 만큼 넓게 늘어나지 않게 막는 것입니다.

**퍼센트**는 상대 단위이지만, rem이나 em과 달리 기준이 폰트 크기가 아니라 *포함 블록(containing block)*의 어떤 치수입니다. 포함 블록은 위치 지정 요소나 흐름 내 요소가 기준을 잡는 상자로, 보통 부모의 콘텐츠 박스입니다. 여기서 발목을 잡는 함정은, 기준이 되는 치수가 속성마다 다르며 예상과 다를 수 있다는 점입니다.

css
.hero {
  width: 80%;                  /* 포함 블록의 WIDTH의 80% */
  padding-top: 25%;            /* 이것도 높이가 아니라 WIDTH의 25% */
  margin-bottom: 10%;          /* 수직 방향인데도 역시 WIDTH 기준 */
}

width, padding, margin의 퍼센트는 모두 포함 블록의 너비를 기준으로 계산되며, 수직 방향의 padding과 margin도 마찬가지입니다. 너비를 기준으로 한 이 수직 패딩 계산은 피해야 할 특이 동작이 아니라, aspect-ratio가 나오기 전에 고정 종횡비 상자를 만드는 대표적인 트릭입니다. padding-top: 56.25%는 너비에 맞춰 커지는 16:9 영역을 만들어 줍니다. height 퍼센트는 포함 블록의 높이를 기준으로 계산되지만, 그 높이가 명확한 값(명시적으로 설정되었거나 계산 가능한 값)일 때만 그렇습니다. auto 높이를 기준으로 계산하려 하면 결과값이 auto가 되어 아무 효과도 없는 것처럼 보이는데, 이것이 퍼센트 높이가 조용히 실패하는 흔한 원인입니다. 최신 레이아웃에서는 flex-basis와 그리드 트랙의 퍼센트가 더 예측 가능하게 동작하므로, 퍼센트는 유동적인 너비에 여전히 유용하고, 퍼센트가 다루기 어려운 경우는 fr 단위와 min()/max()/clamp()가 대신 처리합니다.

Juno퍼센트 퍼센트는 부모를 기준으로 크기가 정해지므로, width: 50%는 그 요소가 담긴 상자 너비의 절반입니다. 이 덕분에 칼럼이 유동적으로 동작하여, 창 크기를 조절해도 넘치지 않고 함께 늘어나고 줄어듭니다. 어떤 화면에도 맞는 레이아웃을 만드는 핵심 도구 중 하나입니다.
Juno퍼센트 퍼센트 너비는 부모의 일부분이며, 이것이 컨테이너 크기가 바뀌어도 칼럼이 유동적으로 유지되는 이유입니다. 기준에 주의하세요. 퍼센트 padding과 margin은 수직 방향이라도 부모의 너비를 기준으로 계산되고, 퍼센트 height는 부모가 명확한 높이를 가지고 있어야 합니다. 퍼센트 너비와 rem 단위의 max-width를 함께 쓰면 유동적이면서도 상한을 둘 수 있습니다.
Juno퍼센트 퍼센트는 포함 블록을 기준으로 계산되며, 그 기준은 속성마다 다릅니다. width, padding, margin은 모두 그 너비를 기준으로 하며 수직 방향 값도 포함됩니다. 이것이 너비 기반 종횡비 상자의 트릭입니다. 퍼센트 height는 부모의 명확한 높이가 필요하며, 그렇지 않으면 auto로 계산되어 아무 효과도 없습니다. 유동적인 너비에는 퍼센트를 쓰고, 다루기 어려운 경우는 fr 단위와 clamp()에 맡기세요.

뷰포트 단위

뷰포트 단위는 브라우저 창 자체를 기준으로 크기를 정합니다. **vw**는 뷰포트 너비의 퍼센트이고 vh는 뷰포트 높이의 퍼센트입니다. 여기서 뷰포트는 페이지에서 보이는 영역을 뜻합니다. 그래서 100vw는 창의 전체 너비이고 100vh는 창의 전체 높이입니다.

css
.hero {
  height: 100vh;               /* 화면 전체 높이를 채움 */
}

랜딩 배너처럼 창의 크기가 어떻든 화면을 가득 채워야 하는 전체 화면 섹션에 이 단위가 딱 맞습니다. 이 단위가 창을 기준으로 하기 때문에, 누군가 창 크기를 조절하거나 기기를 회전시켜도 이 섹션은 전체 화면을 유지합니다. 특정 화면에만 맞는 고정 픽셀 높이를 지정할 필요가 없습니다.

뷰포트 단위는 뷰포트, 즉 브라우저 창에서 보이는 영역을 기준으로 계산됩니다. **vw**는 뷰포트 너비의 1퍼센트, vh는 뷰포트 높이의 1퍼센트이므로, 100vh는 창 전체 높이이고 50vw는 창 너비의 절반입니다. 어떤 크기의 창에서도 화면을 가득 채워야 하는 전체 화면 섹션이나 히어로 배너에 자연스럽게 잘 맞습니다.

css
.hero {
  min-height: 100vh;           /* 최소한 화면 전체 높이 */
  padding: 2rem;
}

모바일에서 알아둘 점이 하나 있습니다. 100vh는 예전에 브라우저 주소 표시줄 뒤에 숨겨진 공간까지 포함했기 때문에, 전체 높이 섹션이 실제로 보이는 영역보다 더 커져서 아래쪽이 잘려 나갈 수 있었습니다. 이 문제를 해결한 것이 *동적 뷰포트 높이(dynamic viewport height)*인 dvh로, 브라우저 UI가 나타나고 사라짐에 따라 실제로 보이는 높이를 그대로 따라갑니다. 모바일 전체 화면 섹션에서는 min-height: 100dvh를 쓰세요. 그리고 폰트 크기에 순수한 vw를 쓰면 독자의 폰트 설정을 무시하게 되므로, 뷰포트 단위는 타입이 아니라 레이아웃에 쓰세요.

뷰포트 단위는 뷰포트, 즉 브라우저 창에서 실제로 렌더링되어 보이는 영역을 기준으로 계산됩니다. **vw**와 vh는 각각 그 너비와 높이의 1퍼센트이고, vminvmax는 그 둘 중 더 작은 값과 더 큰 값의 1퍼센트로, 방향에 반응해야 하는 값에 편리합니다. 이 단위들의 기준은 창이며 어떤 부모도 아니기 때문에, 주변 레이아웃과 무관하게 뷰포트 전체를 차지하는 섹션에 적합한 도구입니다.

css
.hero {
  min-height: 100dvh;          /* 실제로 보이는 전체 높이, 모바일 안전 */
  padding: 2rem;
}

가장 까다로운 부분은 모바일입니다. 뷰포트 높이가 하나의 값으로 고정되지 않기 때문입니다. 브라우저의 주소 표시줄과 툴바는 사용자가 스크롤할 때마다 펼쳐지고 접히는데, 100vh는 항상 가능한 최대 뷰포트 값에 고정되어 있어서, 전체 높이 섹션이 브라우저 UI 뒤로 넘쳐버릴 수 있었습니다. CSS는 이제 세 가지를 제공합니다. svh(작은 뷰포트 높이, UI가 펼쳐진 상태), lvh(큰 뷰포트 높이, UI가 접힌 상태), dvh(동적 값으로, 현재 상태를 실시간으로 따라감). 보이는 영역에 딱 맞춰야 하는 전체 화면 섹션에는 dvh를 쓰고, 대신 UI 바가 움직일 때마다 다시 흐름이 조정된다는 점을 감안하세요. 또 다른 함정은 font-size에 순수한 vw를 쓰는 경우입니다. 뷰포트 단위의 기준은 창이고 루트 폰트 크기가 아니기 때문에, font-size: 4vw는 독자의 기본 글자 크기 설정과 뷰포트를 바꾸지 않는 페이지 확대 모두를 무시하게 되어, px 폰트 크기와 같은 접근성 문제를 일으킵니다. 해법은 이를 경계 짓는 것입니다. clamp(1rem, 4vw + 0.5rem, 2rem)은 유동적인 동작을 유지하면서도 rem 항이 독자의 설정으로 움직일 수 있는 기준을 되살려 줍니다.

Juno뷰포트 단위vwvh는 브라우저 창을 기준으로 크기를 정합니다. 100vw는 창의 전체 너비, 100vh는 창의 전체 높이입니다. 랜딩 배너 같은 전체 화면 섹션에 딱 맞는 단위인데, 특정 픽셀 높이에 고정되지 않고 창 크기가 바뀌어도 전체 화면을 유지하기 때문입니다.
Juno뷰포트 단위vwvh는 창 너비와 높이의 1퍼센트라서, 어떤 크기의 화면에서도 가득 채워야 하는 히어로 섹션에 딱 맞습니다. 모바일에서는 100vh가 주소 표시줄 뒤 영역까지 포함해 실제로 보이는 영역보다 커질 수 있으므로, 그럴 때는 dvh를 쓰세요. 뷰포트 단위는 레이아웃에 쓰고 폰트 크기에는 쓰지 마세요. 순수한 vw를 타입에 쓰면 독자의 설정을 무시합니다.
Juno뷰포트 단위 뷰포트 단위는 부모가 아니라 창을 기준으로 하므로 뷰포트 전체를 차지하는 섹션에 적합하며, vminvmax는 방향에 반응합니다. 모바일에서는 높이가 하나의 값이 아니므로, 브라우저 UI가 움직일 때 보이는 영역을 따라가는 dvh를 쓰세요. 순수한 vw로 타입 크기를 지정하지 마세요. 독자의 폰트 설정을 무시하기 때문입니다. rem 항이 들어간 clamp()로 경계를 지어, 표현식 안에 접근 가능한 기준을 남겨두세요.