07 — Flexbox vs Grid: 선택 기준과 합성 패턴

이 문서가 답하는 질문: 같은 화면을 두 모델로 다 짤 수 있을 때, 무엇이 선택의 기준인가?


한 줄 답

차원이 결정한다. 한 축 위에 콘텐츠 흐름이면 Flex, 두 축의 격자면 Grid. 실무에서는 둘 중 하나가 아니라 계층적으로 함께 — 페이지 골격은 Grid, 그 안의 인라인 정렬(버튼 그룹, 헤더 네비, 칩 리스트)은 Flex.


Why — 왜 이 선택이 중요한가

잘못 선택하면 코드는 동작하지만 변경에 깨진다:

  • Flex로 카드 그리드 → 행 사이 컬럼 정렬이 미디어 쿼리 추가 시 깨진다.
  • Grid로 메뉴 바 → 메뉴 개수가 동적으로 바뀔 때 트랙 정의가 어색.
  • Grid 안에 Flex 트랙 → 트랙 폭과 Flex 분배가 충돌해 디버깅 어려움.

선택은 지금 보기에 동작한다가 아니라 디자인 의도가 어느 축인가에 따라야 한다.


How — 결정 트리

8가지 패턴별 권장

패턴권장이유
헤더 네비게이션 (로고 + 메뉴 + 액션)Flex한 축, 메뉴 개수 가변, gap·align만 필요
버튼 그룹 (Primary + Secondary)Flex한 축, gap, optional 줄바꿈
칩/태그 리스트Flex wrap한 축 + 자동 줄바꿈, 폭 가변
폼 (label + input 행)Grid (2 columns)라벨/입력 컬럼이 정렬되어야
카드 그리드 (반응형)Grid auto-fill/fit화면 폭에 따라 컬럼 수 자동
페이지 골격 (header/sidebar/main)Grid template-areas영역이 명시적, 반응형 재배치 쉬움
사진 갤러리 (다양한 비율)Grid + dense 또는 Masonry/JS시각 밀도
사이드바 내부 메뉴 리스트Flex column한 축, gap, 줄바꿈 없음

합성 패턴 (계층)

/* 골격 — Grid */
.app {
  display: grid;
  grid-template-columns: 240px 1fr;
  grid-template-rows: 56px 1fr;
  grid-template-areas:
    "header header"
    "side   main";
}
.app > header { grid-area: header; }
.app > aside  { grid-area: side; }
.app > main   { grid-area: main; }
 
/* 헤더 내부 — Flex */
.app > header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 0 24px;
}
 
/* 메인 콘텐츠 — Grid (카드 리스트) */
.app > main {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 16px;
  padding: 24px;
}
 
/* 카드 내부 — Flex column */
.card {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

한 컴포넌트에 둘을 동시에 쓰지 말 것 — 컨테이너의 display는 한 번에 하나, 그 자식 레벨에서 다시 선택.


What — 비교 매트릭스

항목FlexboxGrid
차원1D (한 축)2D (양 축)
트랙 정의 주체콘텐츠컨테이너
기본 정렬align-items: stretch (cross)align-items: stretch (양 축)
항목 명시 배치order만 (한 축 순서)grid-area, line, span
줄바꿈flex-wrap (각 줄 독립)자동 — 트랙이 정의되어 있음
빈 셀없음 (콘텐츠만)가능 (. in areas)
사이징flex-grow/shrink/basisfr, minmax, intrinsic
반응형 카드어려움 (각 줄 독립)한 줄 (auto-fill + minmax)
중첩 정렬불가 (각 컨테이너 독립)Subgrid로 가능
가장 흔한 함정min-width: 0 누락minmax(0, 1fr) 누락

같은 결과, 다른 코드 — 헤더 예제

/* Flex 버전 */
header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
/* Grid 버전 */
header {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 16px;
}
header > nav { justify-self: end; } /* 우측 정렬 */
기준Flex 유리Grid 유리
메뉴 개수 가변(auto 1fr auto는 3 영역 가정)
space-between/evenly✓ (justify-content)△ (수동 계산)
영역이 고정되어야 (로고는 좌측, 액션은 우측)✓ (justify-self)

헤더는 Flex가 사실상 표준이지만, 영역이 의미를 가져야 하면 Grid도 가능.


What-if — 잘못 쓰면

1) Flex로 12-column 그리드 흉내

.row { display: flex; flex-wrap: wrap; }
.col-4 { flex: 0 0 33.333%; }

문제:

  • 각 행이 독립이라 다른 행과 컬럼 폭이 안 맞음 (콘텐츠 길이 차).
  • gap을 줘도 percentage가 그 위에 더해져 overflow.
  • 카드 높이 정렬은 align-items: stretch로 부분 해결되지만 행 간엔 안 됨.

해결: Grid로 전환.

.row { display: grid; grid-template-columns: repeat(12, 1fr); gap: 16px; }
.col-4 { grid-column: span 4; }

2) Grid로 동적 메뉴 바

nav { display: grid; grid-template-columns: repeat(5, auto); }

문제: 메뉴가 5개 가정. 4개·6개일 때 트랙이 부족하거나 빈 칸이 생김.

해결: Flex로 전환.

nav { display: flex; gap: 16px; }

3) Grid 컨테이너 자식에 Flex를 또 줬는데 자식이 안 늘어남

원인: Grid 자식이 Grid item. display: flex가 그 자체 컨텍스트를 만들지만, Grid item으로서의 셀 폭은 Grid가 결정. min-width: 0 함정도 동일하게 발생.

해결: Grid track에 minmax(0, 1fr), Flex 자식에 min-width: 0.

4) place-items: center로 모든 걸 해결하려 함

place-items: center는 모든 항목을 셀 중앙에 — 카드 콘텐츠가 셀보다 작으면 가운데 정렬. 대부분의 경우 의도는 stretch인데 의도치 않게 콘텐츠가 작아 보임.

해결: 기본 stretch 유지, 필요한 셀에만 align-self: center.

5) Flex + Flex + Flex 3중 중첩

증상: 디버깅이 미궁.

원인: 1D 도구로 2D 문제를 풀려고 함.

해결: 중간을 Grid로 바꿀 수 있는지 검토.


Insight — 두 모델의 미래

CSSWG는 두 모델을 수렴시키는 방향으로 진화시키고 있다.

  • Box Alignment Module Level 3align-* / justify-* / gap / place-*을 Flex·Grid·Block·Multi-column에 공통으로 적용. gap이 2021년 Flex로 들어온 게 그 신호.
  • Subgrid — Grid의 중첩 한계를 풀면서 Flex 영역에 더 큰 그림(전체 정렬)을 가능하게 함.
  • Masonry — Grid가 Flex의 “흐름” 모델을 일부 흡수하려는 시도.

미래의 이상은 — 하나의 display 모드에서 1D/2D를 매끄럽게 표현. 하지만 실무에서는 당분간 두 모델을 의식적으로 분리해서 합성하는 게 가장 안정적.

디자이너의 인지 모델과 매칭

흥미로운 관찰: 디자이너는 figma/sketch에서 두 가지 도구를 쓴다.

  • Auto Layout (Figma) → Flex와 1:1 매칭. 항목·gap·정렬.
  • Constraints/Grid (Figma) → Grid와 매칭. 영역 정의, 12-column.

Figma에서 *“이건 Auto Layout으로 짰어요”*가 Flex로 구현, *“12 컬럼 그리드 위에 올렸어요”*가 Grid로 구현과 거의 1:1. 디자이너의 의도를 코드 모델로 직접 옮길 수 있다는 게 모던 CSS의 큰 진전이다.

한 줄 요약 (실무용)

Flex로 줄을 짜고, Grid로 격자를 짜고, 둘을 한 컴포넌트 안에 섞지 말고 계층으로 합성한다.


요약 + Mermaid

  • 차원이 결정 — 1D면 Flex, 2D면 Grid.
  • 계층적으로 합성 — 골격 Grid, 영역 Flex.
  • 12-column 그리드는 Grid, 메뉴/버튼/칩은 Flex.
  • 반응형 카드 = repeat(auto-fill, minmax(...)) — Grid의 가장 큰 승리.
  • Box Alignment 표준이 두 모델을 수렴시키는 중.

이 챕터의 끝. 챕터 인덱스로 돌아가려면 README, 도메인 홈은 ../.