12컬럼 그리드 없이 반응형 레이아웃 — CSS Grid와 Flexbox 비교
한 줄 결론
미디어 쿼리 없는 자동 반응형이 필요하면 Grid, 고아 컬럼을 반드시 없애야 하면 Flexbox.
Bootstrap의 12컬럼 그리드 시스템은 오랫동안 반응형 레이아웃의 표준이었다. col-md-4, col-lg-6 같은 클래스를 잔뜩 붙여야 했고, 심지어 HTML 구조까지 레이아웃에 맞춰야 했다.
CSS Grid와 Flexbox가 성숙한 지금, 유틸리티 클래스를 쓰지 않고도 1~4컬럼 반응형 레이아웃을 구현할 수 있다.
왜 12컬럼이 문제였나
12컬럼 시스템의 문제는 HTML이 레이아웃을 알아야 한다는 점이다.
<!-- 레이아웃 정보가 HTML 클래스로 노출됨 -->
<div class="row">
<div class="col-12 col-md-6 col-lg-4">카드 1</div>
<div class="col-12 col-md-6 col-lg-4">카드 2</div>
<div class="col-12 col-md-6 col-lg-4">카드 3</div>
</div>클래스가 많아질수록 HTML이 지저분해지고, 레이아웃을 바꾸려면 HTML을 건드려야 한다. CSS만 수정해서 레이아웃을 바꿀 수 없다.
핵심 비교표
| 특성 | CSS Grid | Flexbox |
|---|---|---|
| 자동 반응형 | ✅ 미디어 쿼리 없이 가능 | ❌ 미디어 쿼리 필요 |
| 고아 컬럼 방지 | ❌ 발생 가능 | ✅ 방지 가능 |
| 서브컨테이너 | ✅ 독립적으로 작동 | ❌ 뷰포트 기반 |
| 2차원 제어 | ✅ 행+열 동시 | ❌ 1차원만 |
| 적합한 위치 | 카드 그리드, 갤러리 | 헤더, 내비게이션, 주 레이아웃 |
CSS Grid 방식
기본 패턴
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
gap: 1.5rem;
}<div class="card-grid">
<div class="card">카드 1</div>
<div class="card">카드 2</div>
<div class="card">카드 3</div>
<div class="card">카드 4</div>
</div>이 두 줄로 미디어 쿼리 없는 반응형 그리드가 완성된다.
작동 원리
repeat(auto-fit, minmax(15rem, 1fr)) 를 풀면:
minmax(15rem, 1fr)— 최소 너비는15rem, 나머지 공간은1fr로 균등 분배auto-fit— 컨테이너 너비에 맞게 컬럼 수를 자동으로 결정- 컨테이너가 좁아지면
15rem최소 너비를 지키기 위해 자동으로 다음 행으로 이동
비유하면 책상 위에 최소 가로 15cm 크기의 책을 최대한 많이 펼쳐놓는 것과 같다. 책상이 좁아지면 책이 자연스럽게 밑으로 내려간다.
[💡 잠깐! 이 용어는?]
auto-fit vs auto-fill: auto-fit은 남는 공간을 기존 아이템에 분배해 늘린다. auto-fill은 빈 트랙을 만들어 남겨둔다. 카드 그리드처럼 아이템이 꽉 찬 경우에는 차이가 없다.
Grid의 한계: 고아 컬럼
4컬럼 그리드에 5개 아이템이 있으면:
[카드1] [카드2] [카드3] [카드4]
[카드5] [빈칸] [빈칸] [빈칸] ← 고아 컬럼
카드5가 혼자 왼쪽에 달랑 남는다. 서비스 성격에 따라 이게 허용될 수도, 안 될 수도 있다.
가변 컬럼 수 고정
최소/최대 컬럼 수를 고정하고 싶다면:
.card-grid {
display: grid;
/* 최소 2컬럼, 최대 4컬럼 */
grid-template-columns: repeat(
clamp(2, calc(100% / 250px), 4),
1fr
);
gap: 1.5rem;
}또는 미디어 쿼리로 명시적 제어:
.card-grid {
display: grid;
grid-template-columns: 1fr;
gap: 1rem;
}
@media (min-width: 640px) {
.card-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (min-width: 1024px) {
.card-grid {
grid-template-columns: repeat(4, 1fr);
}
}Flexbox 방식
기본 패턴
.card-grid {
display: flex;
flex-wrap: wrap;
gap: 1.5rem;
}
.card-grid > * {
flex: 1 1 calc(25% - 1.5rem); /* 4컬럼 기준 */
min-width: 200px;
}
@media (max-width: 640px) {
.card-grid > * {
flex: 1 1 100%; /* 모바일: 1컬럼 */
}
}<div class="card-grid">
<div class="card">카드 1</div>
<div class="card">카드 2</div>
<div class="card">카드 3</div>
<div class="card">카드 4</div>
<div class="card">카드 5</div>
</div>5번째 카드가 다음 행으로 내려오지만, flex: 1이 적용되어 행 전체를 채운다. 고아 컬럼이 사라진다.
고아 컬럼을 없애는 원리
flex: 1 1 calc(25% - 1.5rem):
1 1— 늘어나고(grow) 줄어들(shrink) 수 있음calc(25% - 1.5rem)— 4컬럼의 기본 너비 (gap 제외)
마지막 행의 아이템이 적어도 flex-grow: 1이 빈 공간을 채우므로 혼자 남지 않고 늘어난다.
[💡 잠깐! 이 용어는?] 고아 컬럼(Orphan Column): 그리드의 마지막 행에 컬럼 수보다 적은 아이템이 있을 때, 왼쪽에 달랑 남는 아이템. 카드 갤러리에서 시각적으로 어색해 보일 수 있다.
선택 체크리스트
CSS Grid가 유리한 경우:
- 미디어 쿼리 없이 자동 반응형이 필요하다
- 서브컨테이너(컴포넌트 안의 그리드)에도 사용할 계획이다
- 행과 열을 동시에 제어해야 한다
- 고아 컬럼이 허용된다 (갤러리, 블로그 카드 등)
Flexbox가 유리한 경우:
- 고아 컬럼을 반드시 없애야 한다
- 주로 페이지 상단 레이아웃에만 사용한다
- 아이템 수가 일정하지 않다
- 한 방향(가로 또는 세로)으로만 제어하면 된다
실전 사용 패턴
대부분의 카드 그리드 → Grid
.post-list {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 2rem;
}헤더 / 내비게이션 → Flexbox
.site-header {
display: flex;
justify-content: space-between;
align-items: center;
gap: 1rem;
}두 가지를 조합
/* 페이지 전체 레이아웃: Grid */
.page {
display: grid;
grid-template-columns: 1fr 300px;
gap: 2rem;
}
/* 사이드바 내부 카드: Flexbox */
.sidebar-cards {
display: flex;
flex-direction: column;
gap: 1rem;
}마무리
12컬럼 그리드는 CSS가 부족하던 시절의 해결책이었다. 지금은 필요 없다.
- 자동 반응형 카드 그리드 →
grid-template-columns: repeat(auto-fit, minmax(...))한 줄 - 고아 컬럼 없는 균등 레이아웃 →
flex: 1 1 calc(25% - gap) - 두 방식을 용도에 맞게 조합하면 Bootstrap 없이 깔끔한 반응형을 만들 수 있다.
유틸리티 클래스가 없어도 된다. HTML을 레이아웃에 종속시키지 않아도 된다. CSS 두 줄이면 충분하다.
참고:
- Stephanie Eckles — Solutions to Replace the 12-Column Grid: https://moderncss.dev/solutions-to-replace-the-12-column-grid/
- MDN — CSS Grid Layout: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout
- MDN — CSS Flexible Box Layout: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_flexible_box_layout
관심 있을 만한 포스트
볼링 핀 배치의 비밀 — CSS Grid auto-placement로 피라미드 레이아웃 구현하기
CSS Grid의 자동 배치 알고리즘과 nth-child 셀렉터를 조합해 반응형 피라미드 그리드를 구현하는 방법을 다룬다.
CSS animation-timeline — 스크롤 오버플로우를 감지해 Border-Radius 동적 조절하기
CSS scroll() 타임라인으로 컨테이너 오버플로우를 감지하고 border-radius를 자동 조절하는 순수 CSS 트릭을 정리한다.
CSS linear() Easing — 자바스크립트 없이 스프링 애니메이션 만들기
CSS linear() 함수로 바운스와 탄성을 가진 스프링 애니메이션을 순수 CSS만으로 구현하는 방법을 정리한다.
SVG 아이콘 — 코드 배포 없이 프로덕트 팀이 직접 관리하는 법
CSS mask-image와 S3를 조합해 개발자 개입 없이 아이콘을 교체하는 패턴을 소개한다.
CSS 한 줄로 업그레이드 — 지금 바로 적용할 수 있는 12가지 모던 CSS 속성
aspect-ratio부터 scrollbar-gutter까지, 한 줄 추가로 스타일시트를 현대화하는 12가지 CSS 속성을 정리한다.
모던 CSS 컴포넌트 아키텍처 — 네이티브 기능만으로 설계하는 컴포넌트 시스템
CSS Nesting, Cascade Layers, Container Queries, :has() 등 네이티브 CSS 기능으로 컴포넌트 기반 아키텍처를 구축하는 방법을 정리한다.
CSS @property — 커스텀 속성에 타입을 부여하는 방법
CSS @property at-rule로 커스텀 속성에 타입 정의, 상속 제어, 폴백을 추가해 렌더링 안정성과 애니메이션 가능성을 확보하는 방법을 다룬다.
CSS :near() — 마우스가 '가까이' 오면 반응하는 새로운 의사 클래스
CSS Working Group에 제안된 :near() 의사 클래스는 포인터 근접성을 감지해 호버 전에 UI를 활성화하는 새로운 상호작용 패턴을 연다.