CSS 강좌 / 유동적인 크기로 반응형 레이아웃 시작하기
반응형 레이아웃은 화면과 콘텐츠가 사용할 수 있는 공간에 맞춰 크기와 배치를 조절하는 구성입니다. 먼저 고정 폭을 줄이고 부모 폭을 넘지 않는 유동적인 크기를 만든 뒤, 배치 자체를 바꿔야 할 때 미디어 쿼리를 추가하면 됩니다. 이 강의에서는 본문·글자·카드·이미지가 좁은 공간에 적응하도록 만드는 기본값을 다룹니다.
화면 폭과 요소가 사용할 수 있는 폭 구분하기
브라우저에서 웹페이지가 표시되는 영역을 뷰포트(viewport)라고 합니다. 그러나 본문 안 카드가 사용할 수 있는 폭은 뷰포트보다 작을 수 있습니다. 부모의 여백, 다른 열과의 간격, 최대 폭 제한을 제외하고 남은 공간에 카드가 들어가야 하기 때문입니다.
width: 100%의 백분율은 일반적인 블록 배치에서 요소를 감싸는 기준 영역의 폭을 사용합니다. 100vw는 뷰포트 폭을 기준으로 하므로 작은 카드 안에 넣어도 화면 전체만큼 커질 수 있습니다. 세로 스크롤바 처리와 바깥 여백까지 더해지면 가로 넘침을 만들기도 합니다. 본문 내부 요소의 폭에는 먼저 백분율이나 자동 크기를 검토하세요.
| 지정 | 좁은 부모 | 넓은 부모 | 사용 기준 |
|---|---|---|---|
| width: 48rem | 부모보다 클 수 있음 | 48rem 유지 | 줄어들어도 되는 본문의 기본값으로는 부적합 |
| width: 100% | 부모에 맞춰 줄어듦 | 부모 폭까지 계속 늘어남 | 부모를 채워야 하는 요소 |
| width: 100%; max-width: 48rem | 48rem보다 작게 줄어듦 | 최대 48rem | 읽는 폭에 제한이 필요한 본문 |
| min-width: 48rem | 48rem 미만으로 줄지 않음 | 다른 크기 규칙에 따라 결정 | 필수 최소 크기가 있을 때만 사용 |
본문에 최대 폭과 여백 지정하기
*, *::before, *::after { box-sizing: border-box; }
.content {
width: 100%;
max-width: 48rem;
margin-inline: auto;
padding-inline: 1rem;
}
box-sizing: border-box이면 지정한 폭 안에 테두리와 안쪽 여백이 포함됩니다. 루트 글꼴 크기가 16px이고 부모 폭이 390px일 때 이 본문의 바깥 폭은 390px, 좌우 안쪽 여백을 뺀 콘텐츠 폭은 358px입니다. 부모가 1000px이면 바깥 폭은 768px에서 멈추고 남는 바깥 공간을 좌우 자동 마진으로 나눕니다.
같은 요소에 content-box를 적용하면 width: 100%에 안쪽 여백이 추가되어 부모보다 넓어질 수 있습니다. 반면 마진은 border-box에도 포함되지 않습니다. width: 100%와 양쪽의 고정 마진을 함께 지정할 때는 전체 합계를 확인해야 합니다. 최소 폭과 최대 폭이 충돌하면 최소 폭 때문에 더 커질 수 있으므로 min-width도 점검하세요.
min()과 clamp()로 값의 범위 정하기
min()은 후보 중 작은 값을 선택합니다. width: min(100%, 48rem)은 부모 폭과 48rem 중 작은 폭을 사용하므로, 앞서 지정한 100%와 최대 폭 제한을 하나의 선언으로 나타낼 수 있습니다.
.content { width: min(100%, 48rem); }
h1 { font-size: clamp(1.5rem, 1rem + 2vw, 2.5rem); }
clamp(최솟값, 선호값, 최댓값)는 가운데 값을 계산하되 양끝 범위를 벗어나지 않게 합니다. 위 제목에서 rem은 루트 글꼴 크기를, vw는 뷰포트 폭의 1%를 기준으로 합니다. 루트 크기가 16px이면 최솟값은 24px, 최댓값은 40px이고 선호값은 16px에 화면 폭의 2%를 더한 값입니다.
| 뷰포트 폭 | 가운데 값 계산 | 실제 제목 크기 |
|---|---|---|
| 320px | 16 + 6.4 = 22.4px | 최솟값 24px |
| 600px | 16 + 12 = 28px | 28px |
| 1200px | 16 + 24 = 40px | 40px |
| 1600px | 16 + 32 = 48px | 최댓값 40px |
본문 글자까지 화면 폭에만 의존하게 만들면 좁은 화면이나 확대 상황에서 너무 작아질 수 있습니다. 제목에 유동 크기를 쓸 때도 읽을 수 있는 하한을 두고, 본문은 rem 기반 크기로 유지하는 것이 출발점으로 알맞습니다. rem을 섞었다는 이유만으로 확대 접근성이 자동으로 확보되지는 않으므로 실제 브라우저 확대와 기본 글꼴 설정에서도 확인해야 합니다. MDN의 clamp() 정의와 접근성 설명에서 각 인수의 역할을 확인할 수 있습니다.
전체 예제: 카드·그림·긴 문자열 함께 줄이기
다음 코드를 UTF-8의 fluid-size.html에 저장하고 브라우저로 여세요. 그림은 파일에 포함된 SVG 데이터이므로 따로 내려받을 이미지나 외부 라이브러리가 필요하지 않습니다. 넓은 화면과 390px 정도의 좁은 화면에서 본문의 폭, 카드 열 수와 제목 크기를 비교합니다.
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>fluid-size.html</title>
<style>
*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; font: 1rem/1.6 sans-serif; color: #243447; }
h1, h2, p { margin-top: 0; } a { color: #145f8f; }
a:focus-visible, button:focus-visible, input:focus-visible { outline: 3px solid #ac4100; outline-offset: 3px; }
.content { width: min(100%, 48rem); margin-inline: auto; padding: 1rem; }
h1 { font-size: clamp(1.5rem, 1rem + 2vw, 2.5rem); line-height: 1.3; }
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr)); gap: 1rem; }
.card { min-width: 0; padding: 1rem; background: #e8f3fa; overflow-wrap: anywhere; }
.card img { display: block; max-width: 100%; height: auto; margin-bottom: 1rem; }
.token { font-family: monospace; }
</style>
</head>
<body>
<main class="content">
<h1>화면과 내용에 맞춰 크기 조절하기</h1>
<p>본문은 최대 48rem이며, 화면이 작으면 부모 폭에 맞춰 줄어듭니다.</p>
<div class="cards">
<article class="card"><img src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22400%22%20height%3D%22300%22%20viewBox%3D%220%200%20400%20300%22%3E%3Crect%20width%3D%22400%22%20height%3D%22300%22%20fill%3D%22%23287699%22%2F%3E%3Crect%20width%3D%22400%22%20height%3D%2235%22%20fill%3D%22%23205c32%22%2F%3E%3Crect%20y%3D%22265%22%20width%3D%22400%22%20height%3D%2235%22%20fill%3D%22%239e3131%22%2F%3E%3Ccircle%20cx%3D%22200%22%20cy%3D%22150%22%20r%3D%2270%22%20fill%3D%22%23f6ae4c%22%2F%3E%3Crect%20x%3D%2212%22%20y%3D%22120%22%20width%3D%2230%22%20height%3D%2260%22%20fill%3D%22white%22%2F%3E%3Crect%20x%3D%22358%22%20y%3D%22120%22%20width%3D%2230%22%20height%3D%2260%22%20fill%3D%22white%22%2F%3E%3C%2Fsvg%3E" width="400" height="300" alt="위아래 띠와 가운데 원이 있는 실습 그림"><h2>그림이 있는 카드</h2><p>이미지는 카드보다 넓어지지 않고 원래 비율을 유지합니다.</p></article>
<article class="card"><h2>여러 줄로 이어지는 제목과 설명</h2><p>카드의 높이를 고정하지 않으면 글이 길어져도 아래로 늘어납니다.</p></article>
<article class="card"><h2>긴 식별자</h2><p class="token">ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789</p></article>
</div></main>
</body>
</html>
카드의 minmax(min(100%, 14rem), 1fr)는 기본 최소 폭 14rem을 쓰되 부모가 더 좁으면 100%까지 줄이게 합니다. auto-fit은 들어갈 수 있는 열을 만들고, 남은 폭을 1fr 열이 나눠 가집니다. 카드 안쪽 여백 때문에 실제 텍스트와 그림의 폭은 카드 바깥 폭보다 작습니다. 열 수를 기기 이름으로 판단하지 말고 부모의 이용 가능한 폭으로 확인하세요.
min-width: 0은 카드가 콘텐츠의 최소 폭 때문에 줄어들지 못하는 상황을 막고, overflow-wrap: anywhere는 긴 URL이나 식별자가 필요하면 중간에서 줄바꿈되게 합니다. 두 속성은 역할이 다릅니다. 최소 폭을 풀어도 문자열이 실제로 줄바꿈되지 않으면 여전히 밖으로 그려질 수 있습니다. 줄이 나뉘어도 복사한 문자열의 값이 바뀌는 것은 아닙니다.
한 가지씩 바꿔서 확인하는 실습
- 화면을 390px로 줄입니다. 본문은 화면 안에 들어가고 카드가 세로로 이어지며, 그림 위아래의 띠와 가운데 원을 모두 볼 수 있어야 합니다.
.content의 폭만48rem으로 바꿉니다. 좁은 화면에서 본문이 왜 넘치는지 확인한 뒤min(100%, 48rem)으로 되돌립니다.- 제목의 최댓값만
3rem으로 바꿔 넓은 화면에서 차이를 봅니다. 좁은 화면의 크기가 바뀌지 않는 이유를 가운데 값 계산으로 설명하고 2.5rem으로 복구합니다. .card에서overflow-wrap: anywhere만 제거합니다. 긴 식별자가 카드 바깥으로 나오는지 확인하고 복구합니다. 이후 루트 크기를 바꾸는 실험과 브라우저 확대는 별개의 조건으로 기록합니다.
넘침을 숨기는 overflow-x: hidden을 페이지 전체에 넣기 전에 폭·여백·최소 폭·문자열을 확인하세요. 화면에서 안 보이게 가리면 원인을 해결하지 못한 채 콘텐츠나 조작 요소가 잘릴 수 있습니다. 반응형 구성의 기본 원리는 MDN의 반응형 디자인 안내도 참고할 수 있습니다.










