12컬럼 그리드 없이 반응형 레이아웃 — CSS Grid와 Flexbox 비교

9 min read
CSSGridFlexbox반응형레이아웃
12컬럼 그리드 없이 반응형 레이아웃 — CSS Grid와 Flexbox 비교

한 줄 결론

미디어 쿼리 없는 자동 반응형이 필요하면 Grid, 고아 컬럼을 반드시 없애야 하면 Flexbox.

Bootstrap의 12컬럼 그리드 시스템은 오랫동안 반응형 레이아웃의 표준이었다. col-md-4, col-lg-6 같은 클래스를 잔뜩 붙여야 했고, 심지어 HTML 구조까지 레이아웃에 맞춰야 했다.

CSS Grid와 Flexbox가 성숙한 지금, 유틸리티 클래스를 쓰지 않고도 1~4컬럼 반응형 레이아웃을 구현할 수 있다.


왜 12컬럼이 문제였나

12컬럼 시스템의 문제는 HTML이 레이아웃을 알아야 한다는 점이다.

index.html — 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 GridFlexbox
자동 반응형✅ 미디어 쿼리 없이 가능❌ 미디어 쿼리 필요
고아 컬럼 방지❌ 발생 가능✅ 방지 가능
서브컨테이너✅ 독립적으로 작동❌ 뷰포트 기반
2차원 제어✅ 행+열 동시❌ 1차원만
적합한 위치카드 그리드, 갤러리헤더, 내비게이션, 주 레이아웃

CSS Grid 방식

기본 패턴

styles/grid-layout.css — 자동 반응형 카드 그리드
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 1.5rem;
}
index.html — 클래스 없이 깔끔한 HTML
<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)) 를 풀면:

  1. minmax(15rem, 1fr) — 최소 너비는 15rem, 나머지 공간은 1fr로 균등 분배
  2. auto-fit — 컨테이너 너비에 맞게 컬럼 수를 자동으로 결정
  3. 컨테이너가 좁아지면 15rem 최소 너비를 지키기 위해 자동으로 다음 행으로 이동

비유하면 책상 위에 최소 가로 15cm 크기의 책을 최대한 많이 펼쳐놓는 것과 같다. 책상이 좁아지면 책이 자연스럽게 밑으로 내려간다.

[💡 잠깐! 이 용어는?] auto-fit vs auto-fill: auto-fit은 남는 공간을 기존 아이템에 분배해 늘린다. auto-fill은 빈 트랙을 만들어 남겨둔다. 카드 그리드처럼 아이템이 꽉 찬 경우에는 차이가 없다.

Grid의 한계: 고아 컬럼

4컬럼 그리드에 5개 아이템이 있으면:

[카드1] [카드2] [카드3] [카드4]
[카드5] [빈칸] [빈칸] [빈칸]  ← 고아 컬럼

카드5가 혼자 왼쪽에 달랑 남는다. 서비스 성격에 따라 이게 허용될 수도, 안 될 수도 있다.

가변 컬럼 수 고정

최소/최대 컬럼 수를 고정하고 싶다면:

styles/grid-clamped.css — 최소 2개, 최대 4개 컬럼
.card-grid {
  display: grid;
  /* 최소 2컬럼, 최대 4컬럼 */
  grid-template-columns: repeat(
    clamp(2, calc(100% / 250px), 4),
    1fr
  );
  gap: 1.5rem;
}

또는 미디어 쿼리로 명시적 제어:

styles/grid-responsive.css — 명시적 반응형 컬럼
.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 방식

기본 패턴

styles/flex-layout.css — 고아 컬럼 없는 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컬럼 */
  }
}
index.html — Flexbox 방식 HTML
<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

styles/blog-grid.css — 블로그 카드 그리드
.post-list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 2rem;
}

헤더 / 내비게이션 → Flexbox

styles/header.css — 헤더 레이아웃
.site-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
}

두 가지를 조합

styles/page-layout.css — Grid + Flexbox 조합
/* 페이지 전체 레이아웃: 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 두 줄이면 충분하다.


참고:

관심 있을 만한 포스트

볼링 핀 배치의 비밀 — CSS Grid auto-placement로 피라미드 레이아웃 구현하기

CSS Grid의 자동 배치 알고리즘과 nth-child 셀렉터를 조합해 반응형 피라미드 그리드를 구현하는 방법을 다룬다.

CSS Grid레이아웃

CSS animation-timeline — 스크롤 오버플로우를 감지해 Border-Radius 동적 조절하기

CSS scroll() 타임라인으로 컨테이너 오버플로우를 감지하고 border-radius를 자동 조절하는 순수 CSS 트릭을 정리한다.

CSSanimation-timeline

CSS linear() Easing — 자바스크립트 없이 스프링 애니메이션 만들기

CSS linear() 함수로 바운스와 탄성을 가진 스프링 애니메이션을 순수 CSS만으로 구현하는 방법을 정리한다.

CSSanimation

SVG 아이콘 — 코드 배포 없이 프로덕트 팀이 직접 관리하는 법

CSS mask-image와 S3를 조합해 개발자 개입 없이 아이콘을 교체하는 패턴을 소개한다.

SVGCSS

CSS 한 줄로 업그레이드 — 지금 바로 적용할 수 있는 12가지 모던 CSS 속성

aspect-ratio부터 scrollbar-gutter까지, 한 줄 추가로 스타일시트를 현대화하는 12가지 CSS 속성을 정리한다.

CSS모던 CSS

모던 CSS 컴포넌트 아키텍처 — 네이티브 기능만으로 설계하는 컴포넌트 시스템

CSS Nesting, Cascade Layers, Container Queries, :has() 등 네이티브 CSS 기능으로 컴포넌트 기반 아키텍처를 구축하는 방법을 정리한다.

CSSContainer Queries

CSS @property — 커스텀 속성에 타입을 부여하는 방법

CSS @property at-rule로 커스텀 속성에 타입 정의, 상속 제어, 폴백을 추가해 렌더링 안정성과 애니메이션 가능성을 확보하는 방법을 다룬다.

CSS@property

CSS :near() — 마우스가 '가까이' 오면 반응하는 새로운 의사 클래스

CSS Working Group에 제안된 :near() 의사 클래스는 포인터 근접성을 감지해 호버 전에 UI를 활성화하는 새로운 상호작용 패턴을 연다.

CSS:near()