한 줄 정답 — 부모에
display: grid를 주고grid-template-columns로 열을 정의하면, 자식들이 그 격자에 채워진다. 행과 열을 동시에(2차원) 다루는 게 1차원인 flex와의 핵심 차이다. 반응형 카드 격자는repeat(auto-fit, minmax(200px, 1fr))한 줄이면 미디어쿼리 없이도 된다.
핵심 요약#
- Grid는 부모에
display: grid를 주고grid-template-columns로 열을 정의하는 데서 시작한다. - 남는 공간은
fr단위로 나눈다.1fr 1fr 1fr은 3등분,200px 1fr은 한쪽 고정 + 나머지 채움. - 반복은
repeat(), 반응형 자동 줄바꿈은repeat(auto-fit, minmax(200px, 1fr))로 만든다. - 아이템은
grid-column·grid-row로 여러 칸을 차지하게 하거나,grid-template-areas로 이름을 붙여 배치한다. - 2차원(행+열) 전체 틀은 Grid, 1차원(한 줄) 정렬은 flex — 둘은 함께 쓴다.
CSS Grid 사용법, 행과 열부터#
Grid가 flex와 갈리는 지점은 단 하나, 차원이다. flex는 한 방향(가로 또는 세로)으로 흐르는 1차원이고, Grid는 행과 열을 동시에 잡는 2차원이다. 그래서 "전체 페이지 틀"이나 "격자형 카드"처럼 가로·세로를 함께 맞춰야 할 때 Grid가 강하다.
시작은 부모에 display: grid를 주고 열을 정의하는 것이다.
.container {
display: grid;
grid-template-columns: 1fr 1fr 1fr; /* 너비를 3등분한 3열 */
gap: 16px;
}
이러면 자식들이 왼쪽 위부터 차례로 칸에 채워지고, 한 줄(3칸)이 차면 자동으로 다음 줄로 넘어간다. flex와 달리 칸의 틀을 먼저 정하고 콘텐츠를 채우는 방식이다.
컨테이너(부모)에 주는 속성#
격자의 모양은 대부분 부모에서 정한다.
grid-template-columns / rows — 열과 행 정의#
.container {
display: grid;
grid-template-columns: 200px 1fr 1fr; /* 첫 열 고정 200px, 나머지 둘이 남는 공간 1:1 */
grid-template-rows: auto 1fr; /* 첫 행은 내용만큼, 둘째 행은 남는 높이(컨테이너 높이가 있을 때) */
}
핵심은 fr(fraction) 단위다. 남는 공간을 비율로 나누는 단위라, 1fr 2fr이면 1:2로 나눈다. px·%·auto와 섞어 쓸 수 있다.
gap — 칸 사이 간격#
.container {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px; /* 행·열 간격 한 번에. row-gap / column-gap로 따로도 가능 */
}
repeat() — 반복을 짧게#
같은 열을 여러 번 적는 대신 repeat()로 줄인다.
.container {
display: grid;
grid-template-columns: repeat(3, 1fr); /* 1fr 1fr 1fr 과 같음 */
}
minmax() + auto-fit — 미디어쿼리 없는 반응형#
Grid의 가장 강력한 패턴이다. 화면 폭에 따라 열 개수가 알아서 늘고 주는 카드 격자를 한 줄로 만든다.
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 16px;
}
minmax(200px, 1fr)은 "각 칸을 최소 200px, 최대 남는 공간(1fr)"으로 잡으라는 뜻이고, auto-fit은 폭이 허락하는 만큼 칸을 채운 뒤 남는 칸을 접어(최댓값이 1fr처럼 가변일 때) 아이템을 그만큼 늘린다. 비슷한 auto-fill은 빈 칸을 접지 않고 남겨 둔다. 아이템이 적을 때 칸을 끝까지 늘리고 싶으면 auto-fit, 빈 칸을 그대로 두고 고정 폭을 유지하고 싶으면 auto-fill을 쓴다.
정의한 칸보다 아이템이 많으면#
grid-template으로 정한 칸보다 아이템이 많아지면 브라우저가 행을 자동으로 더 만든다(암묵적 격자). 이렇게 생기는 행의 높이는 기본적으로 내용만큼인데, grid-auto-rows로 지정할 수 있다.
.container {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-auto-rows: 120px; /* 자동으로 생기는 행의 높이 */
}
칸 안 정렬 vs 격자 전체 정렬#
정렬 속성이 두 갈래라 헷갈리기 쉽다.
.container {
display: grid;
/* 각 칸 '안에서' 아이템 정렬 */
justify-items: center; /* 가로(인라인 축) */
align-items: center; /* 세로(블록 축) */
/* 격자 '전체'를 컨테이너 안에서 정렬 (격자가 더 작을 때) */
justify-content: center;
align-content: center;
}
justify-items/align-items— 칸 안에서 아이템을 정렬한다. 둘을 합친 단축이place-items.justify-content/align-content— 격자 묶음이 컨테이너보다 작을 때 격자 전체를 정렬한다.
아이템(자식)에 주는 속성#
특정 칸을 여러 칸에 걸치게 하거나, 이름으로 배치할 수 있다.
grid-column / grid-row — 여러 칸 차지#
격자의 선(line) 번호로 시작~끝을 지정한다. 선은 1번부터 시작한다.
.hero {
grid-column: 1 / 3; /* 1번 선부터 3번 선까지 = 2칸 차지 */
grid-row: 1 / 2;
}
.wide {
grid-column: span 2; /* 현재 위치에서 2칸 차지 */
}
grid-template-areas — 이름으로 레이아웃#
칸에 이름을 붙여 그림 그리듯 배치하면 한눈에 읽힌다.
.layout {
display: grid;
grid-template-columns: 200px 1fr;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
gap: 16px;
}
.layout .header { grid-area: header; }
.layout .sidebar { grid-area: sidebar; }
.layout .main { grid-area: main; }
.layout .footer { grid-area: footer; }
실전 예제#
반응형 카드 격자 — 미디어쿼리 없이#
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 16px;
}
칸 하나가 220px 밑으로 좁아지면 열 수가 자동으로 줄고, 넓어지면 늘어난다. 대부분 이 한 줄로 충분하고, 특정 폭에서 열 수를 강제하는 등 세밀한 제어가 필요할 때만 미디어쿼리를 더한다.
페이지 레이아웃 — 헤더·사이드바·본문·푸터#
<div class="layout">
<header class="header">헤더</header>
<aside class="sidebar">사이드바</aside>
<main class="main">본문</main>
<footer class="footer">푸터</footer>
</div>
.layout {
display: grid;
grid-template-columns: 200px 1fr;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
min-height: 100vh;
gap: 16px;
}
.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main { grid-area: main; }
.footer { grid-area: footer; }
헤더와 푸터는 두 열을 모두 차지하고, 가운데 줄만 사이드바와 본문으로 나뉜다. 영역 이름만 봐도 구조가 읽히는 게 grid-template-areas의 장점이다.
Grid vs Flex, 언제 무엇을?#
둘은 경쟁이 아니라 역할이 다르다.
| 기준 | Flex | Grid |
|---|---|---|
| 차원 | 1차원(한 줄) | 2차원(행+열) |
| 접근 | 콘텐츠 흐름에 맞춰 배치 | 틀을 먼저 정하고 채움 |
| 잘 맞는 곳 | 내비게이션 바, 버튼 줄, 컴포넌트 내부 정렬 | 페이지 전체 틀, 격자형 카드, 대시보드 |
실무에선 같이 쓴다. 페이지의 큰 틀은 Grid로 잡고, 그 안의 헤더 바나 카드 내부 요소 정렬은 flex로 처리하는 식이다. flex 자체가 헷갈린다면 CSS flexbox 정렬 글을 먼저 보면 좋다.
자주 묻는 질문#
Q. grid-template-columns에서 fr은 무슨 단위인가요?
A. 남는 공간을 비율로 나누는 단위(fraction)입니다. 1fr 1fr 1fr은 가용 너비를 3등분하고, 200px 1fr은 200px를 먼저 떼어낸 뒤 나머지를 한 칸이 다 차지합니다. 화면 폭이 바뀌면 fr 칸들이 비율을 유지하며 같이 늘고 줄어듭니다.
Q. 미디어쿼리 없이 반응형 격자를 만들 수 있나요?
A. 네. grid-template-columns: repeat(auto-fit, minmax(200px, 1fr))을 쓰면 칸이 최소 너비 밑으로 좁아질 때 열 수가 자동으로 줄고, 넓어지면 늘어납니다. 대부분의 카드 목록은 이 한 줄로 충분합니다.
Q. Grid와 Flex는 무엇이 다른가요? 언제 뭘 쓰나요?
A. Flex는 한 방향으로 흐르는 1차원, Grid는 행과 열을 동시에 잡는 2차원입니다. 내비게이션 바·버튼 줄·컴포넌트 내부 정렬은 flex, 페이지 전체 틀·격자형 카드·대시보드는 Grid가 맞습니다. 보통 큰 틀은 Grid, 그 안의 정렬은 flex로 함께 씁니다.
Q. 특정 칸을 여러 칸에 걸치게 하려면요?
A. grid-column·grid-row에 선 번호를 주면 됩니다. 예를 들어 grid-column: 1 / 3은 1번 선부터 3번 선까지 두 칸을 차지하고, grid-column: span 2는 현재 위치에서 두 칸을 차지합니다.
관련 글#
레이아웃은 결국 Grid와 flex를 상황에 맞게 섞는 일이다. 1차원 정렬과 반응형 분기를 함께 익혀두면 응용이 쉬워진다.
- CSS flexbox 정렬 — 주축·교차축으로 한 번에 이해하기
- CSS 미디어쿼리 사용법 — 모바일 퍼스트 반응형
- display 속성 — block, inline, inline-block의 차이