Grid 레이아웃 — 2차원 배치의 모든 것
Flexbox로 한 줄은 잘 정렬했는데, 행과 열을 동시에 제어하려면 어떻게 해야 할까?
Grid 레이아웃 — 2차원 배치의 모든 것
Flexbox는 한 방향(주축) 정렬에 특화되어 있어요. 하지만 대시보드, 카드 목록, 매거진 레이아웃처럼 행과 열을 동시에 다루어야 할 때는 CSS Grid가 훨씬 직관적입니다. 이 글에서는 Grid의 기본 용어부터 실전 패턴까지 한 번에 정리해 보겠습니다.
1. Grid란
CSS Grid는 2차원(행 + 열) 레이아웃 시스템 입니다. 부모 요소(컨테이너)에 display: grid를 선언하면, 자식 요소(아이템)들이 행과 열로 이루어진 격자 위에 배치됩니다.
.container {
display: grid; /* 격자 레이아웃 활성화 */
}
Flexbox가 "한 줄 안에서의 정렬"이라면, Grid는 "전체 격자판 위에서의 배치"라고 생각하면 됩니다.
2. 기본 용어
Grid를 다루려면 6가지 핵심 용어를 알아야 합니다.
| 용어 | 설명 |
|---|---|
| Grid Container | display: grid가 선언된 부모 요소 |
| Grid Item | 컨테이너의 직계 자식 요소 |
| Grid Line | 트랙을 나누는 수직/수평 선 (1번부터 시작) |
| Grid Track | 인접한 두 라인 사이의 공간 — 하나의 행 또는 열 |
| Grid Cell | 하나의 행 트랙과 열 트랙이 교차하는 최소 단위 |
| Grid Area | 하나 이상의 셀로 이루어진 직사각형 영역 |
공부하다 보면 "라인 번호"가 가장 헷갈리는 부분인데, ** 컬럼 3개를 만들면 라인은 4개 **(양쪽 끝 + 사이)라는 점만 기억하면 됩니다.
3. 컨테이너 속성
grid-template-columns / rows
행과 열의 크기를 명시적으로 정의합니다.
.container {
display: grid;
grid-template-columns: 200px 1fr 2fr; /* 3개 컬럼 */
grid-template-rows: 100px auto; /* 2개 행 */
}
fr 단위
fr(fraction)은 남은 공간을 비율로 나누는 단위 입니다.
/* 1:2:1 비율로 3개 컬럼 — 가운데가 양 옆의 2배 */
grid-template-columns: 1fr 2fr 1fr;
fr은 고정 크기(px, rem 등)를 먼저 할당한 뒤 남은 공간 에 대해 비율을 계산합니다. 200px 1fr 1fr이면 200px을 빼고 나머지를 반반 나눕니다.
repeat()
반복되는 트랙 정의를 간결하게 작성합니다.
/* 동일한 너비의 4개 컬럼 */
grid-template-columns: repeat(4, 1fr);
/* 패턴 반복: 100px, 1fr을 3번 반복 → 6개 컬럼 */
grid-template-columns: repeat(3, 100px 1fr);
auto-fill vs auto-fit
둘 다 repeat() 안에서 사용하며, 컨테이너 너비에 따라 트랙 수를 자동 계산합니다.
/* auto-fill: 빈 트랙도 공간을 차지 */
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
/* auto-fit: 빈 트랙을 0으로 축소 → 아이템이 늘어남 */
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
- auto-fill — 아이템이 적을 때 빈 트랙이 남아 있어 아이템 크기가 유지됩니다
- auto-fit — 빈 트랙을 없애서 아이템이 남은 공간까지 차지합니다
아이템 수가 충분하면 둘의 차이가 없어요. 아이템이 적을 때만 동작이 달라지므로, 반응형 카드 그리드에서는 보통 auto-fill이 더 예측 가능합니다.
gap
행과 열 사이의 간격을 설정합니다.
.container {
gap: 16px; /* 행·열 동일 간격 */
gap: 20px 16px; /* 행 간격 20px, 열 간격 16px */
}
grid-template-areas
영역에 이름을 붙여 레이아웃을 시각적으로 설계합니다. 자세한 내용은 6장에서 다룹니다.
4. 아이템 속성
grid-column / grid-row
아이템이 차지할 영역을 라인 번호 로 지정합니다.
.item {
grid-column: 1 / 3; /* 1번 라인 ~ 3번 라인 (2개 컬럼) */
grid-row: 1 / 2; /* 1번 라인 ~ 2번 라인 (1개 행) */
}
span 키워드
라인 번호 대신 "몇 칸 차지"로 지정할 수 있습니다.
.item {
grid-column: span 2; /* 현재 위치에서 2개 컬럼 차지 */
grid-row: span 3; /* 3개 행 차지 */
}
grid-area
grid-row-start / grid-column-start / grid-row-end / grid-column-end를 한 줄로 축약합니다. grid-template-areas와 함께 쓸 때는 영역 이름을 지정합니다.
/* 라인 번호 축약 */
.item { grid-area: 1 / 1 / 3 / 4; }
/* Named Area 이름 지정 */
.header { grid-area: header; }
justify-self / align-self
개별 아이템의 정렬을 제어합니다.
.item {
justify-self: center; /* 가로 방향 가운데 정렬 */
align-self: end; /* 세로 방향 끝 정렬 */
}
5. 암시적 그리드
grid-template-*으로 정의한 것보다 아이템이 더 많으면, Grid는 자동으로 암시적(implicit) 트랙 을 생성합니다.
.container {
display: grid;
grid-template-columns: repeat(3, 1fr); /* 명시적: 3개 컬럼 */
grid-auto-rows: 150px; /* 암시적 행 높이 */
}
grid-auto-flow
아이템이 자동 배치되는 방향을 제어합니다.
.container {
grid-auto-flow: row; /* 기본값: 행 방향으로 채움 */
grid-auto-flow: column; /* 열 방향으로 채움 */
grid-auto-flow: row dense; /* 행 방향 + 빈 공간 채우기 */
}
dense는 빈 공간을 줄여주지만, DOM 순서와 시각적 순서가 달라질 수 있으므로 접근성에 주의 가 필요합니다. 스크린 리더는 DOM 순서로 읽기 때문이에요.
6. Named Areas로 레이아웃 설계하기
grid-template-areas를 쓰면 코드만 봐도 레이아웃이 눈에 보입니다.
.dashboard {
display: grid;
grid-template-columns: 240px 1fr 1fr;
grid-template-rows: 60px 1fr 60px;
grid-template-areas:
"header header header" /* 상단 전체 */
"sidebar main aside" /* 본문 영역 */
"footer footer footer"; /* 하단 전체 */
gap: 12px;
min-height: 100vh;
}
/* 각 아이템에 영역 이름 연결 */
.dashboard-header { grid-area: header; }
.dashboard-sidebar { grid-area: sidebar; }
.dashboard-main { grid-area: main; }
.dashboard-aside { grid-area: aside; }
.dashboard-footer { grid-area: footer; }
핵심 규칙 몇 가지를 기억해 두면 좋습니다.
- 영역은 반드시 직사각형 이어야 합니다 (L자 형태 불가)
- 빈 셀은
.(마침표)로 표시합니다 - 같은 이름이 인접해야 하나의 영역으로 인식됩니다
/* 오른쪽 사이드바 없는 레이아웃 */
grid-template-areas:
"header header header"
"sidebar main ." /* 마침표 = 빈 영역 */
"footer footer footer";
7. 실전 패턴
반응형 카드 그리드
가장 자주 쓰이는 패턴입니다. 미디어 쿼리 없이 컨테이너 너비에 따라 컬럼 수가 자동으로 조절됩니다.
.card-grid {
display: grid;
/* 최소 280px, 남은 공간은 균등 분배 */
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 24px;
}
이 한 줄이면 화면이 넓을 때는 45개, 좁을 때는 12개 컬럼으로 자동 전환됩니다. minmax()의 최소값을 조절해서 카드의 최소 너비를 결정합니다.
대시보드 레이아웃
Named Areas와 미디어 쿼리를 결합하면 반응형 대시보드를 깔끔하게 만들 수 있습니다.
.dashboard {
display: grid;
grid-template-columns: 1fr;
grid-template-areas:
"header"
"main"
"sidebar"
"footer";
gap: 16px;
}
/* 태블릿 이상 */
@media (min-width: 768px) {
.dashboard {
grid-template-columns: 240px 1fr;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
}
}
/* 데스크톱 */
@media (min-width: 1200px) {
.dashboard {
grid-template-columns: 240px 1fr 300px;
grid-template-areas:
"header header header"
"sidebar main aside"
"footer footer footer";
}
}
grid-template-areas만 바꾸면 레이아웃 구조가 완전히 달라지니까, 미디어 쿼리와 조합했을 때 코드가 정말 읽기 좋아집니다.
정리
- **Grid는 2차원 **, Flexbox는 1차원 — 행과 열을 동시에 제어해야 하면 Grid
- fr 단위 는 남은 공간을 비율로 나눈다
- auto-fill vs auto-fit 은 아이템이 부족할 때만 차이가 난다
- Named Areas 를 쓰면 레이아웃을 시각적으로 설계할 수 있다
- 암시적 그리드 와
grid-auto-flow를 이해하면 예상 밖의 배치를 방지할 수 있다 repeat(auto-fill, minmax(최소값, 1fr))은 반응형 카드 그리드의 공식이다