AD JB Toolbox 01

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이나 식별자가 필요하면 중간에서 줄바꿈되게 합니다. 두 속성은 역할이 다릅니다. 최소 폭을 풀어도 문자열이 실제로 줄바꿈되지 않으면 여전히 밖으로 그려질 수 있습니다. 줄이 나뉘어도 복사한 문자열의 값이 바뀌는 것은 아닙니다.

한 가지씩 바꿔서 확인하는 실습

  1. 화면을 390px로 줄입니다. 본문은 화면 안에 들어가고 카드가 세로로 이어지며, 그림 위아래의 띠와 가운데 원을 모두 볼 수 있어야 합니다.
  2. .content의 폭만 48rem으로 바꿉니다. 좁은 화면에서 본문이 왜 넘치는지 확인한 뒤 min(100%, 48rem)으로 되돌립니다.
  3. 제목의 최댓값만 3rem으로 바꿔 넓은 화면에서 차이를 봅니다. 좁은 화면의 크기가 바뀌지 않는 이유를 가운데 값 계산으로 설명하고 2.5rem으로 복구합니다.
  4. .card에서 overflow-wrap: anywhere만 제거합니다. 긴 식별자가 카드 바깥으로 나오는지 확인하고 복구합니다. 이후 루트 크기를 바꾸는 실험과 브라우저 확대는 별개의 조건으로 기록합니다.

넘침을 숨기는 overflow-x: hidden을 페이지 전체에 넣기 전에 폭·여백·최소 폭·문자열을 확인하세요. 화면에서 안 보이게 가리면 원인을 해결하지 못한 채 콘텐츠나 조작 요소가 잘릴 수 있습니다. 반응형 구성의 기본 원리는 MDN의 반응형 디자인 안내도 참고할 수 있습니다.

같은 카테고리의 다른 글
CSS 강좌 / 박스 모델과 margin, padding, border 이해하기

CSS 강좌 / 박스 모델과 margin, padding, border 이해하기

콘텐츠·padding·border·margin의 위치와 너비 계산을 도해와 실행 예제로 설명합니다. 여백 축약 규칙과 세로 margin 합치기를 비교하고 개발자 도구에서 실제 크기를 확인합니다.

CSS / Tutorial / 마우스 호버 효과 / 다른 이미지 보여주는 방법

CSS / Tutorial / 마우스 호버 효과 / 다른 이미지 보여주는 방법

이미지에 마우스를 올렸을 때 다른 이미지가 나타나는 효과를 CSS로 만들어보겠습니다. 다음과 같이 이미지 두 개를 넣고 div로 감쌉니다.

CSS 강좌 / @keyframes 애니메이션과 움직임 줄이기

CSS 강좌 / @keyframes 애니메이션과 움직임 줄이기

키프레임의 중간 상태와 재생 시간·지연·방향·횟수·종료 상태를 비교합니다. 일시 정지와 움직임 감소 설정까지 전체 예제로 확인합니다.

CSS 강좌 / 유동적인 크기로 반응형 레이아웃 시작하기

CSS 강좌 / 유동적인 크기로 반응형 레이아웃 시작하기

본문의 최대 폭, min()·clamp(), 카드와 이미지의 유동 크기를 비교합니다. 전체 예제와 긴 문자열 실습으로 좁은 공간에서도 내용을 유지하는 방법을 배웁니다.

CSS 강좌 / 컨테이너 쿼리로 구성 요소 너비에 맞춰 꾸미기

CSS 강좌 / 컨테이너 쿼리로 구성 요소 너비에 맞춰 꾸미기

같은 카드가 본문과 사이드바의 폭에 맞춰 배치를 바꾸도록 컨테이너 쿼리를 적용합니다. 중첩 컨테이너와 이름 선택, 조건이 적용되지 않는 원인도 살펴봅니다.

CSS 강좌 / 상속(inheritance)

CSS 강좌 / 상속(inheritance)

CSS 속성은 상속하는 속성과 상속하지 않는 속성이 있습니다. 상속하는 속성은 자식 요소에 영향을 미칩니다. 상속하지 않는 속성은 자식 요소에 영향을 미치지 않습니다.

CSS 강좌 / Grid 반복 열·자동 배치·영역 이름 사용하기

CSS 강좌 / Grid 반복 열·자동 배치·영역 이름 사용하기

repeat·auto-fit·auto-fill의 차이와 자동 배치 순서를 실행 예제로 비교합니다. grid-template-areas로 페이지를 구성하고 모바일 전환, 영역 이름 오류와 읽기 순서도 확인합니다.

CSS 강좌 / display와 기본 배치 흐름 이해하기

CSS 강좌 / display와 기본 배치 흐름 이해하기

block·inline·inline-block의 줄바꿈과 크기 적용을 같은 요소로 비교합니다. display: none과 visibility: hidden의 공간 차이, 인라인 이미지의 크기 예외를 실행하며 확인합니다.

CSS 강좌 / Flexbox 정렬·간격·줄바꿈·크기 조절하기

CSS 강좌 / Flexbox 정렬·간격·줄바꿈·크기 조절하기

Flexbox의 아이템 정렬과 여러 줄 정렬을 구분하고, gap·줄바꿈·grow·shrink·basis를 비교 예제로 익힙니다. 긴 문자열이 넘치는 원인과 크기·줄바꿈의 해결 방법도 다룹니다.

CSS 강좌 / 관계 선택자와 가상 클래스로 요소 고르기

CSS 강좌 / 관계 선택자와 가상 클래스로 요소 고르기

자손·자식·인접 형제·일반 형제 선택자를 같은 문서 구조에서 비교합니다. hover·focus-visible·first-child·nth-child를 포함한 전체 예제와 구조를 바꿔 선택 결과를 확인하는 실습을 제공합니다.