CSS · 단위와 반응형
CSS? 로 크기를 적을 때는 "몇 px" 처럼 딱 못 박는 방법도 있고, "부모의 절반(50%)" 처럼 상황에 따라 늘었다 줄었다 하는 방법도 있습니다. 화면이 휴대폰부터 큰 모니터까지 제각각인 요즘은, 이 유연한 단위와 반응형 기법으로 한 벌의 HTML? 이 어디서나 보기 좋게 맞춰지도록 만듭니다.
이 문서의 단위·함수·미디어쿼리 동작은 모두 MDN CSS 레퍼런스 기준입니다. clamp(MIN, 선호, MAX) 는 정의상 max(MIN, min(선호, MAX)) 와 같고,
1rem 은 문서 루트(<html>)의 글자 크기(기본 16px)를 가리킵니다. 라이브 데모는 폭에 자연히 반응하는 것 위주로 두었어요 —
데모 화면(iframe)이 좁아 미디어쿼리의 폭 변화는 잘 안 보이므로, 미디어쿼리는 코드로 설명합니다.
1. 절대 단위 vs 상대 단위
무엇: 크기를 적는 단위는 크게 두 종류입니다. 화면이나 부모와 무관하게 고정인 절대 단위(대표: px)와,
무언가에 비례해서 변하는 상대 단위(%, em, rem, vw/vh, ch, fr)입니다.
| 단위 | 기준(무엇에 비례하나) | 주로 쓰는 곳 |
|---|---|---|
px | 고정(절대). 1px = 화면의 1픽셀(논리 단위) | 테두리 두께, 1px 선처럼 늘면 곤란한 값 |
% | 부모 요소의 같은 속성 값(폭은 부모 폭의 %) | 폭·여백을 부모에 맞춰 늘리기 |
em | 그 요소의 글자 크기(font-size). font-size 자체에 쓰면 부모의 글자 크기 | 버튼 안쪽 여백처럼 글자 크기에 비례시킬 때 |
rem | 루트(<html>)의 글자 크기. 기본 16px | 글자·간격의 전역 기준. 중첩돼도 안 흔들림 |
vw / vh | 뷰포트(보이는 화면) 폭/높이의 1% | 화면 가득 채우는 영역, 유동 글자 |
ch | 그 글꼴 0(영) 한 글자의 폭 | 본문 한 줄 글자 수 제한(max-width: 60ch) |
fr | 그리드 컨테이너의 남은 공간의 한 몫(fraction) | Grid 칸 비율 나누기(1fr 2fr) |
- 언제 px: 테두리·구분선처럼 "절대 1px이어야" 하는 값. 늘어나면 안 되는 곳에만.
- 언제 % / vw: 컨테이너나 화면 폭에 맞춰 같이 늘었다 줄어야 하는 폭·영역.
- 언제 rem: 글자 크기와 간격의 전역 기준. 사용자가 브라우저 기본 글꼴을 키우면 같이 커져 접근성에 좋아요.
- 언제 ch: 읽기 좋은 줄 길이(보통 45~75자) 제한.
fr은 Grid 전용 단위입니다.
em vs rem — 부모냐 루트냐
핵심 차이: em 은 가장 가까운 부모의 글자 크기를 기준 삼아 중첩되면 누적됩니다(부모가 1.5em, 자식도 1.5em이면 2.25배). 반면 rem 은 언제나 루트 기준이라 아무리 깊이 중첩돼도 흔들리지 않아요. 아래 데모에서 직접 확인해 보세요 — 바깥 상자의 글자 크기를 바꾸면 em 글자만 따라 변합니다.
위 코드에서 .box 의 font-size: 24px 를 40px 으로 바꾸고 실행해 보세요. 1em 줄만 커지고 1rem 줄은 그대로입니다.
2. 함수형 크기 — min() · max() · clamp()
무엇: 여러 값 중에서 상황에 맞는 하나를 계산해서 고르는 CSS 함수입니다. 글자·폭을 화면에 따라 부드럽게(유동적으로) 변하게 할 때 핵심이에요.
| 함수 | 고르는 값 | 쓰임 |
|---|---|---|
min(a, b, …) | 가장 작은 값 | "이 폭을 넘기지 마라" 상한. width: min(100%, 600px) |
max(a, b, …) | 가장 큰 값 | "이 아래로는 줄이지 마라" 하한. font-size: max(1rem, 2vw) |
clamp(MIN, 선호, MAX) | 선호 값을 MIN~MAX 사이로 가둠 | 유동 글자·폭. 너무 작지도 크지도 않게 |
/* clamp 는 정의상 다음과 같습니다 (MDN) */
clamp(MIN, 선호, MAX) ≡ max(MIN, min(선호, MAX))
/* 예: 최소 16px, 선호는 화면 폭의 4%, 최대 32px */
h1 { font-size: clamp(16px, 4vw, 32px); }
/* 예: 본문 폭은 100% 까지 늘되 600px 을 넘기지 않음 */
.wrap { width: min(100%, 600px); }아래 제목과 카드는 clamp() 로 만들었습니다. 데모 화면(또는 "크게 보기")의 폭을 좁혔다 넓히면 글자와 카드 폭이 구간 안에서 부드럽게 변합니다 — 미디어쿼리의 "딱딱 끊김"과 달리 연속적으로 변하는 게 특징이에요.
3. 미디어쿼리 — @media
무엇: "조건이 맞을 때만 이 스타일을 적용해라"라고 적는 규칙입니다. 화면 폭·방향·다크모드 같은 환경에 따라 다른 모습을 줄 수 있어요.
/* 폭이 768px 이상일 때만 */
@media (min-width: 768px) { .list { grid-template-columns: repeat(3, 1fr); } }
/* 폭이 480px 이하일 때만 */
@media (max-width: 480px) { .nav { display: none; } }
/* 화면 방향: 가로(landscape) / 세로(portrait) */
@media (orientation: landscape) { .banner { height: 40vh; } }
/* 사용자가 다크 테마를 선호할 때 */
@media (prefers-color-scheme: dark) { body { background: #0f141b; color: #e6eaf0; } }min-width는 "그 폭 이상",max-width는 "그 폭 이하"에서 켜집니다.orientation은portrait(세로가 긺) 또는landscape(가로가 긺) 두 값.prefers-color-scheme는light/dark— 사용자의 OS·브라우저 테마 설정을 읽어옵니다.
기본 스타일은 가장 좁은 화면(모바일) 기준으로 작게 짜 두고, @media (min-width: …) 로 폭이 넓어질 때 칸을 늘리는 식으로 더해 갑니다.
이렇게 하면 기본값이 단순해지고, 작은 화면에서 불필요한 무효화(override)가 줄어요.
/* 1) 기본(모바일): 한 줄에 1칸 */
.list { display: grid; grid-template-columns: 1fr; gap: 12px; }
/* 2) 넓어지면 키운다 */
@media (min-width: 600px) { .list { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 960px) { .list { grid-template-columns: repeat(3, 1fr); } }4. 반응형 레이아웃 — 유연한 Flex / Grid
무엇: 미디어쿼리로 단계를 일일이 나누지 않고도, Grid의 auto-fit + minmax() 조합만으로 폭에 맞춰 칸 수가 자동으로 바뀌게 만들 수 있습니다.
.gallery {
display: grid;
/* 각 칸은 최소 180px, 남으면 똑같이 1fr 씩 나눠 채움.
들어갈 만큼 칸을 자동으로 만들고(auto-fit), 폭이 좁아지면 줄을 바꿈 */
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 14px;
}minmax(180px, 1fr)— 칸은 최소 180px, 자리가 남으면1fr로 같이 늘어남.auto-fit— 한 줄에 들어갈 만큼 칸을 채우고, 빈 칸은 접어(collapse) 남은 칸을 늘림. (auto-fill은 빈 칸을 그대로 둠.)- 그래서 화면을 좁히면 칸이 스스로 줄바꿈되고, 넓히면 한 줄에 더 많이 들어갑니다 — 미디어쿼리 없이도요.
아래 카드 갤러리의 폭을 바꾸면 한 줄에 들어가는 칸 수가 자동으로 달라집니다. ("크게 보기"로 열어 창을 넓혀 보세요.)
Flex로도 비슷하게 할 수 있어요: display:flex; flex-wrap:wrap; 에 각 아이템을 flex: 1 1 180px(최소 180px, 남으면 늘고, 넘치면 줄바꿈)로 두면 자동으로 줄이 바뀝니다.
5. 반응형 이미지와 뷰포트 meta
무엇: 이미지가 부모 폭을 넘어 삐져나오지 않게 하고, 휴대폰에서 화면이 축소돼 보이지 않도록 하는 두 가지 기본기입니다.
/* 이미지가 부모보다 커지지 않게, 비율은 유지 */
img {
max-width: 100%; /* 부모 폭을 넘지 않음 */
height: auto; /* 가로에 맞춰 세로를 비율대로 */
}이 한 줄(max-width: 100%; height: auto;)이 반응형 이미지의 기본입니다. 아래 데모에서 박스 폭을 좁혀도 그림(여기선 색 블록)이 비율을 지키며 같이 줄어듭니다.
그리고 모든 반응형의 시작은 문서 <head> 안의 뷰포트 meta 한 줄입니다. 이게 없으면 휴대폰이 데스크톱 폭(보통 980px)인 척하며 화면을 통째로 축소해 버려, 미디어쿼리가 의도대로 작동하지 않아요.
<meta name="viewport" content="width=device-width, initial-scale=1" />
width=device-width— 페이지 폭을 기기 실제 화면 폭에 맞춤.initial-scale=1— 처음 확대 배율을 1배(축소 없음)로.
6. 다크모드 — prefers-color-scheme
무엇: 사용자가 OS나 브라우저를 어두운 테마로 쓰면, 그 설정을 읽어 색을 바꿔 주는 것입니다. 별도 버튼 없이 시스템 설정을 따라가는 가장 간단한 다크모드예요.
/* 1) 색을 변수로 빼 두고 */
:root { --bg: #ffffff; --fg: #1f2733; }
/* 2) 다크 선호일 때 변수만 바꿔 끼움 */
@media (prefers-color-scheme: dark) {
:root { --bg: #0f141b; --fg: #e6eaf0; }
}
body { background: var(--bg); color: var(--fg); }색을 CSS 변수로 모아 두면, 다크모드에서는 변수 값만 갈아 끼우면 되므로 규칙이 깔끔해집니다. (사용자가 직접 토글하는 버튼식 다크모드는 보통 <html data-theme="dark"> 같은 속성을 두고 그에 맞춰 변수를 바꾸는데, 이 사이트의 테마 버튼이 그 방식이에요.)
한눈에 — 단위 / 미디어쿼리 카탈로그
| 항목 | 뜻 / 기준 | 대표 예 |
|---|---|---|
px | 절대 단위(고정) | border: 1px solid |
% | 부모의 같은 속성에 비례 | width: 50% |
em | 요소(또는 부모)의 글자 크기 | padding: 0.5em |
rem | 루트 글자 크기(기본 16px) | margin: 1rem |
vw / vh | 뷰포트 폭/높이의 1% | height: 100vh |
ch | 글꼴 "0" 한 글자 폭 | max-width: 60ch |
fr | Grid 남은 공간의 한 몫 | 1fr 2fr |
min(a, b) | 가장 작은 값(상한 만들기) | min(100%, 600px) |
max(a, b) | 가장 큰 값(하한 만들기) | max(1rem, 2vw) |
clamp(MIN, 선호, MAX) | max(MIN, min(선호, MAX)) | clamp(16px, 4vw, 32px) |
@media (min-width: n) | 폭 n 이상에서 적용 | 모바일 퍼스트로 키울 때 |
@media (max-width: n) | 폭 n 이하에서 적용 | 좁은 화면만 손볼 때 |
@media (orientation: …) | portrait / landscape | 가로/세로 분기 |
@media (prefers-color-scheme: dark) | 다크 테마 선호 | 시스템 따라 다크모드 |
repeat(auto-fit, minmax(n, 1fr)) | 폭 따라 칸 수 자동 | 카드 갤러리 |
① 기본 스타일은 모바일(좁은 화면)부터 짜고 min-width 로 키워 가세요. 작은 화면이 가장 까다로우니 그것부터 단단하게.
② 글자·간격은 rem 을 기준으로 통일하면 전역 글꼴 크기 한 번으로 전체 스케일이 맞고, 사용자가 글자를 키워도 깨지지 않습니다.
③ 폭은 width: min(100%, 최대치), 글자는 clamp() 로 두면 미디어쿼리 없이도 대부분 유연하게 대응돼요.
④ 단계가 꼭 필요한 레이아웃 전환(1칸→3칸 등)에만 미디어쿼리를 쓰는 게 깔끔합니다.
다음 단계
- 이 단위·반응형 개념을 유틸리티 클래스로 빠르게 쓰기 → Tailwind CSS
- 반응형 컴포넌트를 미리 만들어진 UI로 → Shadcn UI