04 — Grid Placement (line / span / area / implicit / dense)
이 문서가 답하는 질문: Grid 항목은 어떻게 격자에 배치되며,
line/span/area/ implicit /dense는 각각 무엇을 푸는가?
한 줄 답
Grid 항목은 시작/끝 라인 번호로 배치된다.
grid-column: 2 / 4(2부터 4 직전까지) 또는grid-column: 2 / span 2(2부터 2칸). 명시되지 않은 항목은 **암시적 그리드(implicit grid)**가 받아내며,grid-auto-flow: dense는 빈 셀을 채우기 위해 항목 순서를 재배치한다.
Why — 배치 모델
flex처럼 순서대로 흐르기만 하면 충분할 때도 많다. 하지만 다음 요구를 한 번에 풀려면 라인 기반 배치가 필요하다.
- “이 항목은 1열에서 3열까지 차지” — 가로 span.
- “이 카드는 항상 마지막 행” — 음수 라인 인덱스(
-1)로 표현. - “빈 셀을 자동으로 채우되, 순서는 깨지지 않게” —
auto-flow기본. - “빈 셀이 있으면 작은 항목이 끼어들어가 채우게” —
auto-flow: dense.
Flex로는 (1)은 가능하지만 (2)~(4)는 불가능하다.
How — 라인과 셀
1) 라인 번호
n개 컬럼이면 라인은 n+1개 — 1이 시작, n+1이 끝. 음수 인덱스는 끝에서부터 — -1이 마지막 라인.
2) grid-column / grid-row
.item {
grid-column: 1 / 3; /* line 1부터 line 3 직전까지 = 2칸 */
grid-column: 1 / span 2; /* line 1부터 2칸 — 동일 결과 */
grid-column: span 2; /* 2칸, 시작은 auto-flow가 결정 */
grid-column: 1 / -1; /* 1부터 끝까지 (full width) */
grid-row: 2 / 4;
}shorthand:
.item {
grid-area: 2 / 1 / 4 / 3; /* row-start / col-start / row-end / col-end */
}3) 이름 있는 라인
.grid {
grid-template-columns: [content-start] 1fr [content-end gutter-start] 200px [gutter-end];
}
.item {
grid-column: content-start / content-end;
}repeat()와 결합:
grid-template-columns: repeat(3, [col-start] 1fr [col-end]);
/* col-start 1, col-start 2, col-start 3 자동 생성 */
.item { grid-column: col-start 2 / col-end 3; }4) template-areas 이름
.app {
grid-template-areas: "sidebar main";
}
.sidebar { grid-area: sidebar; }grid-area는 이름이거나 4-line shorthand — 형태로 구분된다.
5) Implicit Grid
명시 안 한 트랙은 암시적으로 생성된다.
.grid {
display: grid;
grid-template-columns: 200px 1fr; /* 명시 2개 컬럼 */
/* grid-template-rows 없음 */
grid-auto-rows: minmax(80px, auto); /* 암시 행의 크기 */
grid-auto-columns: 100px; /* 항목이 컬럼을 넘으면 암시 컬럼 생성 */
grid-auto-flow: row; /* 새 항목을 행 우선으로 채움 (기본) */
}| 속성 | 의미 |
|---|---|
grid-auto-rows | 암시 행의 기본 크기 |
grid-auto-columns | 암시 컬럼의 기본 크기 |
grid-auto-flow | 자동 배치 알고리즘 — row / column / dense / row dense |
6) auto-flow: dense
기본 auto-flow는 항목을 DOM 순서대로 배치하며, 큰 항목 때문에 빈 셀이 생기면 비워둔다.
dense는 빈 셀을 채우기 위해 뒤 항목을 앞으로 당긴다 — 시각적 순서가 DOM 순서와 달라짐.
→ 갤러리·Pinterest 류에서 시각 밀도를 높이는 패턴. 다만 키보드/스크린리더 순서가 시각 순서와 어긋남에 주의.
What — 항목 정렬
Grid 항목 속성
| 속성 | 의미 |
|---|---|
grid-column / grid-row | line 기반 배치 |
grid-column-start/end, grid-row-start/end | 분리 형 |
grid-area | 이름 또는 4-line shorthand |
justify-self | 셀 내부 main 축(가로) 정렬 |
align-self | 셀 내부 cross 축(세로) 정렬 |
place-self | align-self justify-self shorthand |
order | 자동 배치 시 순서 (DOM 순서와 별개) |
컨테이너 정렬 속성
| 속성 | 의미 |
|---|---|
justify-items | 모든 항목의 셀 내부 가로 정렬 (기본) |
align-items | 셀 내부 세로 정렬 |
place-items | shorthand |
justify-content | 트랙 전체가 컨테이너보다 작을 때 컨테이너 내부 가로 정렬 |
align-content | 동일, 세로 |
place-content | shorthand |
Grid에서는
justify-items/justify-self가 Flex와 달리 동작한다 (Flex는 무시). Grid는 셀이 항상 정의되므로 셀 내부 정렬이 의미 있다.
기본값
justify-items,align-items기본stretch— 항목이 셀 전체를 채움.- Flex와 마찬가지로 이미지 squashing 발생 —
align-items: start또는align-self: start로 회피.
What-if — 잘못 쓰면
1) grid-column: 1 / 3인데 항목이 1칸만 차지한다
원인: 컬럼이 1개뿐이면 line 3이 없어 implicit grid가 생성된다. 결과적으로 항목이 1칸으로 보이거나, 새 컬럼이 생기며 다른 항목을 밀어낸다.
해결: grid-template-columns에 명시된 트랙 수 확인.
2) grid-area 이름이 안 먹는다
원인: grid-template-areas에 그 이름이 없거나 오타. 빈 칸은 . 필수.
해결: 이름이 사각형을 이루는지, 모든 행 문자열의 셀 수가 같은지 확인.
3) Implicit row가 너무 작다
원인: grid-auto-rows가 기본 auto — 콘텐츠 폭만. 디자인 시스템상 최소 행 높이를 원하면 grid-auto-rows: minmax(80px, auto).
4) dense로 갤러리를 짰는데 키보드 포커스가 어색하다
원인: dense는 시각 배치만 재정렬, DOM(=tab) 순서는 그대로.
해결:
- DOM 순서를 의미 있게 유지.
- 또는 dense 포기하고 빈 셀 허용.
- 또는
display: grid+subgrid로 더 정밀한 배치 (06-subgrid).
5) -1 라인이 의도와 다르다
원인: -1은 명시 트랙의 마지막. implicit 트랙으로 grid가 늘어나도 -1은 변하지 않는다 (예상과 반대).
해결: 끝까지 채우려면 grid-template-columns를 명시적으로 충분히 정의, 또는 grid-column: 1 / -1 대신 grid-column: 1 / span 12 같이 명시.
Insight — line vs area, 무엇이 먼저인가
CSSWG는 두 가지 배치 문법을 의도적으로 같이 제공했다.
- Line 기반 (
grid-column: 2 / 4) — 계산적, JS로 동적으로 만들 때 유리. - Area 기반 (
grid-area: main) — 선언적, 디자이너가 와이어프레임 그리듯 표현.
같은 결과를 두 방식으로 다 표현할 수 있다. 어느 쪽이 낫다기보다 변하는 차원이 다르다.
| 상황 | 권장 |
|---|---|
| 페이지 골격(header/sidebar/main/footer) | template-areas (선언적) |
| 카드 그리드, 갤러리, 12-column | repeat() + line/span (계산적) |
| 동적으로 항목 위치 바뀜 (JS) | grid-row/column 직접 |
| 미디어 쿼리로 레이아웃 재배치 | template-areas (다시 그리기 쉬움) |
흥미로운 패턴: 반응형 area 재배치.
.app {
grid-template-areas:
"header"
"main"
"sidebar"
"footer";
}
@media (min-width: 768px) {
.app {
grid-template-columns: 200px 1fr;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
}
}자식 CSS는 한 줄도 안 바뀐다. 이게 Grid가 약속한 진짜 반응형.
auto-flow: dense의 역사도 재밌다 — 2014년 스펙 초안에서는 기본이었다가, 순서가 어긋나는 게 더 큰 사고라는 피드백으로 opt-in이 됐다. 같은 논리로 min-width: auto가 opt-in이 아니라 opt-out인 것과 대비된다.
요약 + Mermaid
- 항목은 line 번호 또는 area 이름으로 배치 —
1 / 3,1 / span 2,1 / -1,grid-area: name. - 명시 안 한 트랙은 implicit grid가 생성 —
grid-auto-rows/columns/flow로 제어. auto-flow: dense는 빈 셀 채움 — 시각/DOM 순서가 어긋남에 주의.- Grid에서는
justify-items/justify-self가 의미가 있다 (Flex와 다름).
다음: 05-intrinsic-sizing — minmax, auto-fill vs auto-fit.