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)이다

스펙상 1frminmax(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)을 웹에 옮기려는 야심이었다.

문제는 두 가지였다:

  1. 2D 레이아웃 알고리즘은 어렵다 — Flexbox는 한 축만 풀면 되지만 Grid는 행·열 의존성이 얽힌다. track sizing algorithm은 스펙에서 가장 긴 알고리즘 중 하나.
  2. 명세 합의가 오래 걸렸다 — 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.