한 줄 요약 — 색과 길이는 둘 다 "숫자로 재서 적는" 값입니다. 색은 hex·rgb·hsl 세 가지로 적을 수 있는데, 사람이 눈으로 조절하기엔 색상(각도)·채도·명도를 그대로 쓰는 hsl이 가장 직관적입니다. 길이는 고정된 자인 px, 부모 글자 크기를 기준으로 삼는 em, 문서 전체의 기준 글자 크기를 따르는 rem, 부모 대비 비율인 **%**로 잴 수 있고, 글자 크기는 사용자의 설정을 존중하도록 px 대신 rem으로 두는 것이 보통 무난합니다.

학습 목표

  • 같은 색을 hex·rgb·hsl 세 방식으로 적을 수 있고, 언제 hsl이 편한지 설명할 수 있다.
  • px·em·rem·%가 각각 "무엇을 기준으로 재는 단위"인지 한 문장으로 말할 수 있다.
  • rem과 em의 차이를 알고, 글자 크기에 왜 rem을 주로 쓰는지 안다.
  • vw·vh가 무엇이고, 모바일에서 100vh가 왜 잘리는지 안다.

오늘의 비유 — 길이를 잴 때 무엇을 기준으로 삼을까

같은 "길이"를 말할 때도 우리는 상황마다 다른 기준을 씁니다. 책상 너비는 자를 대고 "60cm"라고 정확히 잽니다. 화장실까지 거리는 "내 걸음으로 다섯 걸음"이라고 어림합니다. 아이 키는 "작년보다 한 뼘 컸다"고 몸을 기준으로 말하고, 운동장에서 위치는 "전체의 절반쯤"이라고 비율로 가리킵니다. 같은 길이인데 무엇을 기준으로 재느냐에 따라 표현이 완전히 달라집니다.

CSS 단위도 똑같습니다. px는 자입니다 — 누가 재든 똑같은 고정된 길이. em은 내 걸음입니다 — 사람마다 보폭이 다르듯 부모 요소의 글자 크기에 따라 달라집니다. rem은 키처럼 한 사람의 고정된 기준입니다 — 문서 전체의 기준 글자 크기 하나만 봅니다. **%**는 운동장 둘레 대비 비율이고요. 색도 결국 빛을 얼마씩 섞었는지를 숫자로 재서 적는 일이라, 오늘 다루는 건 처음부터 끝까지 무엇을 기준으로 재고 적을 것인가 하나입니다.

핵심 개념

색을 적는 세 가지 표기 — hex, rgb, hsl

화면의 색은 빨강·초록·파랑(Red·Green·Blue) 세 빛을 얼마씩 섞었는지로 정해집니다. 이 "얼마씩"을 적는 방식이 세 가지입니다.

.box {
  color: #1d4ed8;            /* hex: 16진법 속기 */
  color: rgb(29 78 216);     /* rgb: 빨·초·파를 0~255로 */
  color: hsl(222 77% 48%);   /* hsl: 색상·채도·명도로 */
}

세 줄은 모두 같은 파란색입니다.

  • rgb는 빨강·초록·파랑을 각각 0~255 눈금으로 잰 값입니다. rgb(29 78 216)은 "빨강 29, 초록 78, 파랑 216만큼"이라는 뜻이에요.
  • hex는 그 rgb 값을 16진법(hexadecimal)으로 줄여 적은 속기입니다. #1d4ed81d(=29)·4e(=78)·d8(=216)을 이어 붙인 것뿐입니다. 짧아서 가장 많이 보이지만, 숫자만 봐선 무슨 색인지 감이 안 옵니다.
  • hsl은 색상(Hue, 0~360도)·채도(Saturation, %)·명도(Lightness, %)로 잽니다. 사람이 색을 떠올리는 방식과 가장 가까워서, "같은 색인데 조금 더 어둡게"가 명도 %만 내리면 끝납니다.

같은 계열에서 밝기만 다른 색을 여러 개 만들 때는 hsl이 압도적으로 편합니다.

.btn       { background: hsl(222 77% 48%); }
.btn:hover { background: hsl(222 77% 40%); } /* 명도만 8 낮춤 */

길이를 재는 단위 — px, rem, em, %

px는 화면 위의 고정된 한 점입니다. 자로 잰 길이처럼 어디서든 같은 크기라 예측이 쉽지만, 사용자가 브라우저 글자 크기를 키워도 따라 커지지 않습니다.

em과 rem은 글자 크기를 기준으로 삼는 상대 단위입니다. 둘의 차이가 오늘의 핵심입니다.

  • em자기 요소(부모로부터 물려받은)의 font-size를 1로 봅니다. 부모가 20px이면 1.5em은 30px입니다.
  • rem(root em)은 문서 뿌리인 <html>의 font-size를 1로 봅니다. 보통 16px이라 1.5rem은 24px이고, 중간에 어떤 부모를 거치든 값이 흔들리지 않습니다.
html        { font-size: 16px;    }
.card       { font-size: 1.25rem; } /* 항상 20px */
.card small { font-size: 0.875em; } /* 부모(20px) 기준 17.5px */

%는 보통 부모의 같은 속성을 기준으로 합니다. width: 50%는 부모 너비의 절반이라는 뜻이에요.

화면 크기에 맞추는 단위 — vw, vh

vw·vh는 **뷰포트(viewport, 브라우저의 화면 영역)**를 기준으로 재는 단위입니다. 1vw는 화면 너비의 1%, 1vh는 화면 높이의 1%예요. 그래서 width: 100vw는 화면 꽉 찬 너비, height: 100vh는 화면 꽉 찬 높이가 됩니다. 첫 화면을 가득 채우는 히어로 섹션에 자주 쓰는데, 100vh에는 모바일에서 함정이 하나 있습니다. 흔한 실수에서 따로 다룹니다.

함께 따라하기 — 같은 카드를 px·rem·em으로 만들고 글자 크기를 바꿔 보기

day-17 폴더에 index.htmlstyle.css를 만들고, 글자 크기 단위만 다른 카드 세 개를 나란히 둡니다.

<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="UTF-8">
    <title>색상과 단위</title>
    <link rel="stylesheet" href="style.css">
  </head>
  <body>
    <div class="card px">px로 잰 카드</div>
    <div class="card rem">rem으로 잰 카드</div>
    <div class="card em">em으로 잰 카드</div>
  </body>
</html>
html { font-size: 16px; }

.card {
  margin: 12px;
  padding: 16px;
  border-radius: 8px;
  background: hsl(222 60% 95%);
  border: 1px solid hsl(222 60% 80%);
}

.card.px  { font-size: 20px;    }
.card.rem { font-size: 1.25rem; } /* 16 × 1.25 = 20px */
.card.em  { font-size: 1.25em;  } /* 부모 body 기준 20px */

저장하고 브라우저로 열어 보면 세 카드의 글자가 모두 20px로 똑같아 보입니다. 이제 브라우저에서 글자 크기를 키워 봅니다 — 크롬이라면 설정 → 모양 → 글꼴 크기를 "매우 크게"로 바꾸면 됩니다.

그러면 px 카드만 그대로고, rem·em 카드의 글자는 함께 커집니다. px는 자로 박아 둔 고정 길이라 사용자의 설정을 무시하고, rem·em은 글자 크기를 기준으로 재기 때문에 설정을 따라가는 것입니다. 본문 글자에 px을 쓰면 안 되는 이유가 이 화면 하나에 다 들어 있습니다.

흔한 실수 3가지

1. em을 중첩해 폰트 크기가 폭주한다

em은 자기 요소의 글자 크기를 기준으로 삼습니다. 그래서 em을 준 요소 안에 또 em을 주면 값이 곱하기로 누적됩니다.

ul { font-size: 1.2em; }
li { font-size: 1.2em; } /* 중첩 리스트에서 점점 커짐 */

(이 코드는 의도와 다르게 동작합니다.) 리스트 안에 리스트가 들어가면 1.2 × 1.2 × 1.2…로 글자가 단계마다 1.2배씩 부풀어 오릅니다. 세 단만 들어가도 두 배 가까이 커져요.

ul { font-size: 1.2rem; } /* 항상 루트 기준 */
li { font-size: 1rem;   }

기준을 항상 루트로 고정하는 rem을 쓰면 몇 단을 중첩해도 크기가 흔들리지 않습니다. "부모 글자에 비례해야 하는" 곳(아이콘을 글자에 딱 맞추기 등)에만 em을 골라 쓰는 편이 안전합니다.

2. 100vh를 모바일에서 그대로 써 주소창 때문에 잘린다

화면을 꽉 채우려고 height: 100vh를 주는 건 데스크톱에선 잘 됩니다. 그런데 모바일 브라우저는 주소창이 보일 때와 숨을 때 화면 높이가 달라지는데, 100vh주소창이 숨은 상태의 큰 높이를 기준으로 잡습니다.

.hero {
  height: 100vh; /* 모바일에서 아래가 주소창에 가려질 수 있음 */
}

(모바일에서 콘텐츠 아래가 잘립니다.) 첫 진입 화면에선 주소창이 보이는 만큼 100vh가 실제 보이는 영역보다 커서, 버튼 같은 아래쪽 요소가 주소창 뒤로 숨습니다.

.hero {
  height: 100svh; /* small viewport height: 주소창 포함한 작은 높이 */
}

요즘 브라우저는 svh(작은 뷰포트 높이)·lvh(큰 높이)·dvh(변하는 높이)를 지원합니다. 가려지면 안 되는 첫 화면에는 svh가 무난합니다. 더 넓은 호환이 필요하면 min-height: 100vh로 두고 내부를 flex로 정렬하는 방법도 자주 씁니다.

3. 모든 폰트 크기에 px만 써서 접근성 설정을 무시한다

body { font-size: 14px; }
h1   { font-size: 32px; }

(사용자가 글자 크기를 키워도 안 커집니다.) 시력이 낮은 사용자는 브라우저나 운영체제에서 기본 글자 크기를 키워 둡니다. 그런데 px로 박아 두면 그 설정을 통째로 무시해서, 작게 보이도록 설정을 바꿀 수 없는 사람에게 그대로 작게 보입니다.

html { font-size: 100%; } /* 사용자 기본값(보통 16px) 존중 */
body { font-size: 1rem; }
h1   { font-size: 2rem; }

글자 크기를 rem으로 두면 사용자의 기본 설정에 비례해 함께 커집니다. 테두리 두께나 그림자처럼 사용자 설정과 무관해도 되는 값에는 px을 그대로 써도 괜찮습니다 — 글자에는 rem, 고정 디테일에는 px이 무난한 기준입니다.

오늘 배운 것 체크리스트

  • 같은 색을 hex·rgb·hsl로 적을 수 있고, 밝기 조절엔 hsl이 편한 이유를 안다.
  • px는 고정 단위, em·rem·%는 무언가를 기준으로 재는 상대 단위임을 안다.
  • em은 자기 요소의 글자 크기, rem은 루트 글자 크기를 기준으로 한다는 차이를 안다.
  • 본문 글자에 px 대신 rem을 쓰는 이유(접근성)를 설명할 수 있다.
  • 모바일에서 100vh가 잘리는 이유와 svh 같은 대안을 안다.

자주 묻는 질문

Q. css 단위 종류가 너무 많은데, 처음엔 무엇부터 쓰면 되나요?

A. 셋만 기억하면 충분합니다. 글자 크기와 여백은 rem, 고정된 테두리·그림자 두께는 px, 부모에 꽉 채우는 너비는 %. 익숙해지면 화면 비율이 필요한 곳에 vw·vh를, 부모 글자에 딱 붙여야 하는 곳에 em을 더하면 됩니다. 처음부터 모든 단위를 다 쓰려 하지 않아도 됩니다.

Q. 반응형 폰트 사이즈는 px로 주면 안 되나요?

A. 안 되는 건 아니지만 권장하지 않습니다. px은 사용자가 키운 글자 크기 설정을 무시해 접근성을 해칩니다. 본문은 rem으로 두고, 제목처럼 화면 폭에 따라 더 유연하게 줄이고 키우고 싶다면 clamp()로 최소·기본·최대를 한 번에 정하는 방법이 요즘은 무난합니다.

Q. rem과 em 중에 그냥 하나만 쓰면 안 되나요?

A. 대부분은 rem 하나로 충분합니다. 기준이 루트 하나로 고정돼 예측이 쉽기 때문입니다. em은 부모 글자에 비례해야 하는 특수한 경우 — 버튼 안 아이콘을 글자 크기에 맞추거나, 컴포넌트 전체를 한 값으로 확대·축소하고 싶을 때 — 골라 쓰면 됩니다.

다음 시간 예고

내일은 폰트와 타이포그래피 — font-family, line-height, 그리고 웹폰트를 다룹니다. 오늘 정한 글자 크기 단위 위에, 어떤 글꼴을 어떻게 불러오고 줄간격을 얼마로 둬야 읽기 편한지 — line-height에 왜 단위 없는 숫자를 쓰는지까지 정리합니다.

더 알아보기