CSS 강좌 / Grid로 행과 열 만들기
CSS Grid는 부모 안에 행과 열을 만들고 자식 요소를 격자에 배치하는 레이아웃입니다. display: grid로 컨테이너를 만들고 grid-template-columns와 grid-template-rows로 열과 행을 지정하며, 지정한 행이 부족하면 자동 행이 생성됩니다.
Grid의 컨테이너·격자선·트랙·셀 구분하기
Grid 컨테이너는 display: grid를 적용한 부모이고, Grid 아이템은 그 부모의 직접 자식입니다. 첫 자식 안에 있는 자식까지 바깥 격자의 독립 아이템이 되는 것은 아닙니다. CSS에서 격자를 그려도 HTML 요소가 새로 생기지 않으며 각 자식이 격자의 어느 영역에 놓일지 계산하는 방식이 바뀝니다.
| 용어 | 뜻 | 2열 2행의 예 |
|---|---|---|
| 격자선(line) | 행과 열을 구분하는 경계선 | 세로선 3개, 가로선 3개 |
| 트랙(track) | 인접한 두 격자선 사이의 한 행 또는 한 열 | 첫 열, 둘째 행 |
| 셀(cell) | 한 행과 한 열이 만나는 최소 영역 | 왼쪽 위의 한 칸 |
| 영역(area) | 하나 이상의 셀로 만든 직사각형 | 두 열에 걸친 제목 영역 |
선 번호와 열 번호는 다릅니다. 두 열의 바깥 경계까지 세면 세로선은 1·2·3입니다. 아이템을 1번 선부터 3번 선까지 놓으면 두 열을 차지합니다. gap은 트랙 사이의 간격이며 아이템의 padding이나 테두리와는 별도로 계산됩니다. 개발자 도구의 Elements에서 grid 표시를 켜면 선 번호와 실제 영역을 확인하기 쉽습니다. MDN의 Grid 기본 개념에 용어와 기본 동작이 정리되어 있습니다.
px·%·fr로 열 너비 지정하기
.grid {
display: grid;
grid-template-columns: 160px 1fr;
gap: 16px;
}
이 부분 코드는 전체 예제의 .grid 규칙 안에서 열 선언을 바꾸어 비교할 수 있습니다. 첫 열은 160px, 둘째 열은 남는 공간을 사용합니다. fr은 분수 단위(fraction)로, 다른 트랙과 gap 등을 고려한 뒤 남는 공간을 비율로 나눕니다. 첫 열을 내용과 관계없이 고정하면 좁은 화면에서 본문이 너무 좁아질 수 있으므로 실제 콘텐츠와 함께 판단하세요.
너비 640px인 컨테이너에서 gap이 16px이고 두 열이 1fr·2fr이면 분배할 공간은 624px입니다. 최소 크기 제약이 개입하지 않는 조건에서 첫 열은 208px, 둘째 열은 416px입니다. 전체 예제는 minmax(0, 1fr) minmax(0, 2fr)를 사용해 트랙의 최소 너비가 콘텐츠 때문에 크게 고정되지 않도록 합니다.
50% 50%와 gap: 16px을 함께 쓰면 열만으로 이미 컨테이너 너비를 채운 뒤 gap이 더해져 넘칠 수 있습니다. 동일한 유동 열에는 fr이 간편합니다. 그렇다고 모든 열을 같은 비율로 만들 필요는 없습니다. 목록의 이름 열은 작고 설명 열은 넓어야 할 수 있으며, 고정 열과 유동 열의 조합도 사용할 수 있습니다.
명시적 행과 자동으로 생기는 행 이해하기
grid-template-rows: 80px는 첫 행 하나를 명시적으로 정의합니다. 뒤의 아이템을 놓을 행이 부족하면 암시적 행(implicit row)이 생기며, 그 크기는 grid-auto-rows로 조절할 수 있습니다. 열을 두 개 만들었다고 행이 두 개로 제한되는 것은 아닙니다.
| 행 크기 | 동작 | 주의할 상황 |
|---|---|---|
| auto | 내용과 컨테이너의 공간·정렬 조건에 따라 크기를 정함 | 고정 높이 컨테이너에서는 남는 공간이 반영될 수 있음 |
| 100px | 행 높이를 고정 | 내용이 길면 아이템이나 글자가 넘칠 수 있음 |
| minmax(100px, auto) | 최소 100px을 확보하고 필요하면 커짐 | 내용이 늘어날 수 있는 카드에 유용 |
고정 행은 이미지 슬롯처럼 크기 조건이 명확한 곳에서 사용할 수 있지만, 설명 문단이 들어가는 카드에서는 글자 크기 확대나 번역으로 줄 수가 늘어날 수 있습니다. minmax의 최대값 auto는 모든 행의 높이를 똑같이 만들라는 의미가 아닙니다. 각 행의 콘텐츠와 레이아웃 조건에 따라 필요한 높이가 달라집니다. MDN의 grid-auto-rows 설명에서 명시적 행 밖의 크기 규칙을 확인할 수 있습니다.
격자선과 span으로 아이템 배치하기
.feature {
grid-column: 1 / -1;
}
/* 같은 2열 명시적 격자에서 두 열에 걸치는 다른 표현 */
/* .feature { grid-column: span 2; } */
1 / -1은 명시적 격자의 첫 선부터 마지막 선까지라는 뜻입니다. 2열에서도 1열로 바뀐 화면에서도 전체 너비를 차지하는 제목이나 강조 영역에 적합합니다. 음수 선 번호는 명시적 격자의 끝을 기준으로 세며 자동 생성된 모든 트랙의 끝을 뜻하지는 않습니다.
span 2는 두 트랙을 차지하라는 뜻이며, 정확한 시작 선을 지정하지 않으면 자동 배치가 위치를 정합니다. 모바일에서 열이 하나뿐인데 span 2를 그대로 유지하면 의도치 않은 추가 열을 만들 수 있습니다. 전체 너비를 유지할 목적이면 1 / -1을 사용하거나 모바일에서 span 값을 다시 지정하세요. 배치 지정을 최소화하면 남은 아이템은 기본적으로 HTML 순서에 따라 빈 셀로 들어갑니다.
전체 예제로 열·행·전체 너비 영역 확인하기
아래 코드를 grid-basics.html로 저장해 엽니다. A·B는 첫 고정 행, C는 전체 열을 차지하는 자동 행, D·E는 그다음 자동 행에 놓입니다. 480px 이하에서는 열이 하나로 바뀌며 C는 계속 전체 너비를 차지합니다.
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>grid-basics.html</title>
<style>
* { box-sizing: border-box; }
body { margin: 16px; font: 16px/1.6 sans-serif; color: #243447; }
h1 { font-size: 24px; } h2 { font-size: 20px; margin-top: 32px; }
a { color: #145f8f; } a:focus-visible { outline: 3px solid #b4470b; outline-offset: 3px; }
.grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
grid-template-rows: 80px; grid-auto-rows: minmax(100px, auto); gap: 16px;
width: min(100%, 640px); }
.grid > div { min-width: 0; padding: 12px; background: #dceefa; overflow-wrap: anywhere; }
.feature { grid-column: 1 / -1; }
@media (max-width: 480px) { .grid { grid-template-columns: minmax(0, 1fr); } }
</style>
</head>
<body>
<h1>열 비율과 행 높이</h1>
<div class="grid"><div>A: 첫 행</div><div>B: 첫 행</div><div class="feature">C: 두 열을 차지하는 영역입니다. 내용이 늘어나면 이 자동 행의 높이도 늘어납니다.</div><div>D: 다음 행</div><div>E: 설명이 긴 카드입니다. 콘텐츠의 길이에 따라 필요한 높이가 달라집니다. 고정된 높이에 모든 내용을 억지로 맞추는 대신 최소 높이 이후에는 내용을 수용하도록 설정합니다.</div></div>
</body>
</html>
- 넓은 화면에서 .grid 너비가 640px인지 확인하고 개발자 도구의 Grid 표시를 켭니다. 열은 208px·416px, 사이 간격은 16px입니다. 열 비율을 1fr·1fr로 바꾸면 각각 312px이 됩니다. 비교 후 1fr·2fr로 복원합니다.
- 첫 행의 높이는 80px입니다. A 안에 문장을 여러 줄 추가하면 행 높이 자체는 그대로이고 내용이 넘칠 수 있습니다. 텍스트를 원래대로 돌린 뒤 grid-template-rows를 minmax(80px, auto)로 바꾸면 내용에 따라 커질 수 있습니다.
- C의 내용을 충분히 늘립니다. grid-auto-rows가 minmax(100px, auto)이므로 행이 100px보다 커질 수 있습니다. grid-auto-rows만 100px로 바꾸어 고정 행의 차이를 관찰한 뒤 원래 값으로 되돌립니다.
- C의 grid-column 선언만 제거하면 C가 한 셀에 들어가고 뒤 아이템의 위치도 바뀝니다. 다시 1 / -1을 복원합니다. 화면을 480px 아래로 좁혀 전체 너비 지정이 1열에서도 유지되는지 확인합니다.
긴 콘텐츠와 모바일 넘침 확인하기
Grid의 1fr 트랙은 기본적인 자동 최소 크기 때문에 긴 문자열에 끌려 넓어질 수 있습니다. minmax(0, 1fr)는 트랙의 최소 크기를 0으로 두는 방법이고, 문자열의 실제 줄바꿈은 별도 규칙이 필요합니다. 아이템 자체에도 상황에 따라 min-width: 0을 적용합니다. 다음 코드는 grid-long-text.html로 저장합니다.
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>grid-long-text.html</title>
<style>
* { box-sizing: border-box; }
body { margin: 16px; font: 16px/1.6 sans-serif; color: #243447; }
h1 { font-size: 24px; } h2 { font-size: 20px; margin-top: 32px; }
a { color: #145f8f; } a:focus-visible { outline: 3px solid #b4470b; outline-offset: 3px; }
.grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 16px;
width: min(100%, 600px); }
.grid > div { padding: 12px; background: #e8f5de; overflow-wrap: anywhere; }
</style>
</head>
<body>
<h1>Grid의 최소 크기와 긴 문자열</h1><div class="grid"><div>ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789</div><div>보통 길이의 설명</div></div>
</body>
</html>
- overflow-wrap만 normal로 바꾸어 트랙 너비는 유지되어도 글자가 밖으로 넘칠 수 있음을 확인합니다.
- 그 상태에서 두 열을 1fr 1fr로 바꾸면 긴 문자열의 최소 크기가 열 배분에 미치는 영향을 볼 수 있습니다. 비교 후 minmax(0, 1fr) 두 개와 anywhere를 복원합니다.
반응형 전환점은 기기 이름보다 실제 내용이 읽히는 너비를 기준으로 선택하세요. 이 예제의 480px은 학습을 위한 선택이며 모든 Grid의 기준이 아닙니다. 화면을 좁히고 글자를 확대해 고정 행, 긴 문자열, span이 만든 추가 열을 함께 확인하면 넘침의 원인을 구분할 수 있습니다.











