03 — Grid 기본 (template / fr / gap)
이 문서가 답하는 질문: Grid template은 어떻게 트랙을 정의하며,
fr단위는 정확히 무엇을 의미하는가?
한 줄 답
Grid는 컨테이너가 행·열의 트랙을 먼저 정의하고 항목을 그 격자에 배치하는 2차원 레이아웃이다.
fr은 남은 공간(free space)의 비율 단위이며, 내부적으로 **minmax(auto, 1fr)**이라 콘텐츠 최소 폭이 우선한다 — 균등을 강제하려면minmax(0, 1fr).
Why — 왜 Grid가 필요한가
Flexbox는 한 축만 다룬다. 행과 열을 동시에 맞추려면 중첩 Flex가 필요한데, 이때 행 사이의 정렬이 깨진다 — 한 행의 컬럼 폭이 다른 행과 무관하기 때문.
Grid는 이 문제를 트랙을 한 번만 선언하여 해결한다.
Grid의 또 다른 강점: template-areas로 시각적 레이아웃을 코드로 표현할 수 있다. 디자이너가 그린 와이어프레임이 CSS로 그대로 옮겨진다.
How — 어떻게 트랙을 정의하는가
1) 명시적 트랙 (explicit grid)
.grid {
display: grid;
grid-template-columns: 200px 1fr 100px; /* 3개 컬럼 */
grid-template-rows: 60px auto 40px; /* 3개 행 */
gap: 16px;
}200px— 고정1fr— 남은 공간 1단위auto— 콘텐츠에 맞춤min-content/max-content— 콘텐츠 기반 (05번 문서)minmax(min, max)— 범위fit-content(<length>)—min(max-content, max(min-content, length))
2) repeat() — 반복
grid-template-columns: repeat(12, 1fr); /* 12 컬럼 */
grid-template-columns: repeat(3, 100px 1fr); /* 100px 1fr 100px 1fr 100px 1fr */
grid-template-columns: repeat(auto-fill, 200px); /* 폭에 맞춰 자동 채움 — 05번 */3) template-areas
.app {
display: grid;
grid-template-columns: 200px 1fr;
grid-template-rows: 60px 1fr 40px;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
gap: 16px;
}
.app > header { grid-area: header; }
.app > aside { grid-area: sidebar; }
.app > main { grid-area: main; }
.app > footer { grid-area: footer; }문자열 모양 그대로 시각적 레이아웃. 빈 셀은 . (점)으로 표시. 같은 이름은 인접해야 사각형으로 인식됨 — L자, T자는 불가.
4) grid shorthand
.app {
display: grid;
grid:
"header header" 60px
"sidebar main" 1fr
"footer footer" 40px
/ 200px 1fr;
}grid: <rows> / <columns> 또는 grid: <areas with row sizes> / <column sizes>. 읽기 어렵다는 평이 많아 실무에서는 template-*을 분리해서 쓰는 게 흔하다.
5) gap
.grid {
display: grid;
gap: 16px; /* row + column */
/* row-gap: 16px; column-gap: 24px; */
/* gap: 16px 24px; — row column 순 */
}Flex와 동일. 셀 사이에만, 컨테이너 끝에는 안 붙음.
What — fr 단위의 진짜 의미
fr의 정의
1fr= 컨테이너에서 **고정 트랙(고정 폭 + 콘텐츠 폭) + gap을 제외한 남은 공간(free space)**을fr합으로 나눈 한 단위.
.grid {
display: grid;
grid-template-columns: 100px 1fr 2fr;
width: 700px;
gap: 0;
}→ 남은 공간 = 700 - 100 = 600px. 1fr+2fr=3 단위 → 1fr = 200px, 2fr = 400px.
fr은 사실 minmax(auto, 1fr)이다
스펙상 1fr은 minmax(auto, 1fr)로 해석된다. 즉:
- 최소는 콘텐츠의 min-content(긴 단어, 이미지 폭)
- 최대는 1fr (남은 공간의 비율)
이게 *“grid-template-columns: repeat(3, 1fr)인데 한 칸이 커진다”*의 원인이다 — 그 칸의 콘텐츠가 다른 칸의 1fr 폭보다 길어서 min-content가 발동.
/* 균등 강제 */
grid-template-columns: repeat(3, minmax(0, 1fr));
minmax(0, 1fr)는 Grid의min-width: 0— flex item의 함정과 같은 종류.
percentage vs fr
| 단위 | 기준 | gap 처리 |
|---|---|---|
33.333% | 컨테이너 폭의 % | gap을 포함 안 함 → 합이 100%+gap이라 overflow |
1fr | 남은 공간(gap 제외) | gap 자동 처리 |
→ 반응형 그리드는 fr 우선, percentage는 거의 쓰지 않는다.
What-if — 잘못 쓰면
1) repeat(3, 1fr)인데 한 칸이 커진다
원인: 1fr = minmax(auto, 1fr). 콘텐츠 min-content가 1fr 폭보다 크면 그 칸이 커지고 다른 칸이 줄어든다.
해결:
grid-template-columns: repeat(3, minmax(0, 1fr));혹은 자식에 min-width: 0, overflow: hidden.
2) template-areas가 깨진다
원인:
- 같은 이름이 사각형을 이루지 않음 (L자/T자)
- 빈 셀에
.대신 공백 - 한 줄의 셀 수가 컬럼 수와 다름
해결: 각 문자열의 셀 수가 컬럼 수와 같고, 같은 이름은 인접한 사각형인지 확인.
3) gap이 % 트랙과 안 맞는다
원인: percentage는 컨테이너 폭 기준, gap은 그 위에 더해짐.
해결: percentage 대신 fr 사용.
4) Grid 안의 Flex 자식이 줄지 않는다
원인: Grid item도 min-width: auto가 기본 — 콘텐츠 최소 폭을 보장.
해결: min-width: 0 또는 minmax(0, 1fr) 트랙.
5) grid-template-rows를 안 줬는데 행이 안 늘어난다
원인: 명시 안 한 행은 **암시적 그리드(implicit grid)**가 만든다. 기본 grid-auto-rows: auto이므로 콘텐츠가 결정. 더 큰 행이 필요하면 grid-auto-rows: minmax(100px, auto) 같은 식.
자세히는 04-grid-placement.
Insight — Grid가 늦게 나온 이유
Grid는 2017년에 모든 evergreen 브라우저에 들어왔다. Flexbox보다 8년 늦었다. 왜?
답은 인쇄 산업의 영향이다. Grid 스펙의 첫 초안(2007)은 Microsoft가 XAML의 Grid 패널을 웹으로 가져오면서 시작됐고, 인쇄·잡지 디자인의 그리드 시스템(스위스 타이포그래피, 12-column grid)을 웹에 옮기려는 야심이었다.
문제는 두 가지였다:
- 2D 레이아웃 알고리즘은 어렵다 — Flexbox는 한 축만 풀면 되지만 Grid는 행·열 의존성이 얽힌다. track sizing algorithm은 스펙에서 가장 긴 알고리즘 중 하나.
- 명세 합의가 오래 걸렸다 — IE10이 prefixed 버전(
-ms-grid)을 먼저 출시했고, 표준은 그 뒤에 다른 문법으로 합의됐다.
fr 단위의 발명도 흥미롭다. CSS는 *“남은 공간의 비율”*을 표현할 단위가 없었다 (percentage는 컨테이너 폭 기준). fr(fractional unit)은 Grid를 위해 새로 만들어진 단위다 — Flex의 flex-grow가 항목 레벨이라면, fr은 트랙 레벨의 같은 개념.
트리비아:
fr은 spec 초안에서 한때*(별표)로 표기됐다 — XAML의 영향. 결국 더 명시적인fr로 정착.
요약 + Mermaid
- Grid는 컨테이너가 트랙을 먼저 선언 — 항목은 그 격자에 배치.
grid-template-columns/rows,repeat(),template-areas가 핵심.fr은 남은 공간의 비율, 내부적으로minmax(auto, 1fr).- 균등 강제:
minmax(0, 1fr). percentage 대신fr. gap은 셀 사이에만, 끝에는 없음.
다음: 04-grid-placement — 항목 배치, span, implicit grid, dense.