CSS Grid 이전에는 제대로 된 페이지 레이아웃을 구축하는 것이 float, clearfix, 음수 마진, 그리고 열이 무너지지 않기를 바라는 기도를 의미했습니다. Flexbox가 도움이 되었지만, 근본적으로 1차원입니다 — 행 또는 열을 얻을 수 있지만, 둘 다는 아닙니다. CSS Grid는 실제로 2차원 레이아웃을 위해 설계된 CSS 최초의 네이티브 레이아웃 시스템입니다. 문제는 브라우저 지원이 아닙니다 — 2017년부터 보편적으로 지원됩니다. 문제는 API의 표면적이 넓고, 기반 모델이 명확하지 않으면 이름 지정이 혼란스럽다는 것입니다. 이 글은 실제 작업에서 사용하게 될 부분에 집중합니다.

행, 열, 그리드 컨테이너

모든 것은 컨테이너에 display: grid를 설정하는 것에서 시작됩니다. 설정이 완료되면 직접 자식 요소들이 자동으로 그리드 아이템이 됩니다 — 별도의 클래스가 필요하지 않습니다. grid-template-columnsgrid-template-rows로 열과 행 트랙을 정의합니다.

먼저 이해해야 할 핵심 단위는 fr입니다 — 분수 단위. 고정 크기 트랙이 처리된 후 그리드 컨테이너의 가용 공간의 일부를 나타냅니다. 1fr은 "남은 공간의 한 몫을 가져가라"를 의미합니다. 1fr 1fr 두 열은 동일한 절반을 의미합니다. 2fr 1fr은 첫 번째 열이 두 번째의 두 배를 얻는다는 의미입니다. 이 단위가 유동적 그리드를 자연스럽게 만드는 것입니다.

css
/* 3-column blog card layout */
.card-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: auto;
  gap: 1.5rem;
}

/* repeat(3, 1fr) is shorthand for: 1fr 1fr 1fr */
/* auto rows size to their content */

repeat(n, track)은 동일한 트랙 크기를 다섯 번 쓰지 않아도 되는 단축 표현입니다. 혼합 값으로도 작동합니다: repeat(3, 1fr 2fr)은 좁고 넓은 트랙을 번갈아 갖는 6열 그리드를 제공합니다. 행의 경우, auto는 거의 항상 원하는 것입니다 — 명시적으로 고정 높이가 필요한 경우가 아니면 행은 콘텐츠에 맞게 크기가 조정됩니다.

html
<div class="card-grid">
  <article class="card">
    <img src="/posts/grid-post-1.jpg" alt="CSS Grid post 1">
    <h2>Getting Started with Grid</h2>
    <p>A practical intro for developers moving from Flexbox.</p>
  </article>
  <article class="card">
    <img src="/posts/grid-post-2.jpg" alt="CSS Grid post 2">
    <h2>Grid vs Flexbox</h2>
    <p>Which layout tool to reach for, and when.</p>
  </article>
  <article class="card">
    <img src="/posts/grid-post-3.jpg" alt="CSS Grid post 3">
    <h2>Responsive Grids Without Media Queries</h2>
    <p>Using minmax() to handle reflow automatically.</p>
  </article>
</div>

gap 속성

Grid 이전에는 레이아웃 자식 요소 사이에 간격을 추가하는 것이 마진을 의미했습니다 — 마진은 마지막 자식 초기화를 기억하고, 충돌을 처리하고, 보상을 위한 컨테이너에 음수 마진을 추가하는 것을 의미했습니다. gap은 이것을 깔끔하게 해결합니다. 트랙 사이에만 공간을 추가하고, 그리드의 외부 가장자리에는 추가하지 않습니다.

css
/* Uniform gap between all rows and columns */
.card-grid {
  gap: 1.5rem;
}

/* Different row and column gaps */
.dashboard {
  row-gap: 2rem;
  column-gap: 1rem;
}

/* gap is shorthand: row-gap then column-gap */
.dashboard {
  gap: 2rem 1rem;
}
참고: gap은 Flexbox에서도 작동합니다(이전의 grid-gap 별칭을 대체했습니다). 컴포넌트에 이미 Flexbox를 사용하고 있고 아이템 사이의 간격만 필요하다면, Grid로 전환하지 않고 gap을 사용할 수 있습니다.

아이템 명시적 배치

기본적으로 그리드 아이템은 소스 순서에서 다음 가용 셀로 흐릅니다. 하지만 Grid의 진정한 힘은 아이템을 어디에나 배치할 수 있고, 여러 트랙에 걸쳐 확장할 수도 있다는 것입니다. 속성은 grid-columngrid-row이며, 그리드 라인(트랙 사이의 선, 1부터 번호가 매겨짐)을 참조합니다.

css
/* Dashboard where the first card spans 2 columns */
.dashboard {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1rem;
}

.card--featured {
  grid-column: 1 / 3;   /* start at line 1, end at line 3 */
}

/* span keyword: simpler when you don't care about exact position */
.card--featured {
  grid-column: span 2;  /* occupy 2 column tracks from wherever it lands */
}

/* Spanning rows too */
.card--tall {
  grid-row: span 2;     /* occupy 2 row tracks */
}

/* Explicit positioning: place a panel in column 3-5, row 1-2 */
.sidebar {
  grid-column: 3 / 5;
  grid-row: 1 / 2;
}

라인 번호는 왼쪽/위쪽 가장자리에서 1부터 시작하여 트랙 수가 n인 경우 n+1까지 올라갑니다. 음수 라인 번호는 오른쪽/아래쪽에서 세어집니다: grid-column: 1 / -1은 열이 몇 개든 관계없이 그리드의 전체 너비에 걸쳐 아이템을 늘립니다 — 다중 열 컨테이너 내의 전체 너비 요소에 유용한 트릭입니다.

grid-template-areas — 레이아웃 이름 지정

이것은 Grid를 페이지 수준 레이아웃에 진정으로 훌륭하게 만드는 기능이며, CSS 전체에서 가장 읽기 쉬운 API입니다. 라인 번호로 아이템을 배치하는 대신, CSS에서 레이아웃을 ASCII 아트로 그리고 각 아이템에 어떤 명명된 영역을 차지하는지 알려줍니다. grid-template-areas 명세는 모든 행이 동일한 수의 셀을 가져야 하며, 각 명명된 영역은 직사각형이어야 한다고 요구합니다 — L자 모양은 안 됩니다.

css
/* Full page layout: header, sidebar, main, footer */
.page-shell {
  display: grid;
  grid-template-columns: 240px 1fr;
  grid-template-rows: 60px 1fr 50px;
  grid-template-areas:
    "header  header"
    "sidebar main"
    "footer  footer";
  min-height: 100vh;
  gap: 0;
}

.page-header  { grid-area: header; }
.page-sidebar { grid-area: sidebar; }
.page-main    { grid-area: main; }
.page-footer  { grid-area: footer; }
html
<div class="page-shell">
  <header class="page-header">Site header / nav</header>
  <nav class="page-sidebar">Sidebar navigation</nav>
  <main class="page-main">Primary content</main>
  <footer class="page-footer">Footer</footer>
</div>

템플릿 문자열의 마침표(.)는 의도적으로 셀을 비워둡니다. 또한 여러 셀에 걸쳐 동일한 이름을 쌓아 확장할 수도 있습니다 — 위의 "header header"가 정확히 그것입니다. 다른 뷰포인트를 위해 레이아웃을 크기 조정하거나 재구성할 때, 여러 grid-column 선언을 찾아다니는 대신 한 곳에서 템플릿 문자열을 업데이트합니다.

minmax()를 사용한 auto-fill vs auto-fit

미디어 쿼리 없이 반응형 그리드를 만드는 패턴은 Grid의 가장 실용적인 트릭 중 하나입니다: repeat(auto-fill, minmax(250px, 1fr)). 분해하면 — minmax(250px, 1fr)은 각 열이 최소 250px 너비이지만 가용 공간을 채우도록 확장된다고 말합니다. auto-fill은 그 제약을 고려하여 컨테이너에 맞는 만큼 많은 열을 만들라고 Grid에 지시합니다. 결과는 뷰포트가 변경됨에 따라 자동으로 열을 추가하고 잃는 그리드입니다.

css
/* Product card grid — reflows automatically, no media queries */
.product-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 1.5rem;
}

/* auto-fit collapses empty tracks — items stretch to fill the container */
.product-grid--stretch {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 1.5rem;
}

auto-fillauto-fit의 차이는 미묘하지만 열보다 아이템이 적을 때 중요합니다. auto-fill은 비어 있더라도 잠재적 트랙을 위한 공간을 예약합니다 — 아이템은 최소 너비로 유지됩니다. auto-fit은 빈 트랙을 0으로 접어 아이템이 행을 채우도록 확장합니다. 아이템이 가용 공간을 사용하기를 원하는 제품 그리드와 갤러리의 경우, auto-fit이 보통 더 좋아 보입니다. 컨테이너를 채우는 것보다 일관된 아이템 너비가 더 중요한 그리드의 경우, auto-fill이 적합한 선택입니다.

브라우저 지원 참고: minmax()를 사용한 auto-fillauto-fit은 훌륭한 지원을 받고 있습니다 — caniuse에 따르면 전 세계적으로 97% 이상. 모던 프로젝트에서 폴백 없이 오늘 이 패턴을 사용할 수 있습니다.

Grid에서의 정렬

Grid는 Flexbox와 동일한 Box Alignment 모듈을 사용하므로, Flexbox를 이미 안다면 속성 이름이 직접 매핑됩니다. 핵심 정신적 모델: justify는 인라인 축(대부분의 쓰기 모드에서 수평)에서 작동하고, align은 블록 축(수직)에서 작동합니다.

  • justify-items / align-items — 그리드 셀 내 모든 아이템의 기본 정렬. 기본값은 stretch로, 그리드 아이템이 자동으로 셀을 채우는 이유입니다.
  • justify-content / align-content — 그리드가 컨테이너보다 작을 때 컨테이너 내에서 트랙 자체를 배분합니다(flex 컨테이너의 Flexbox justify-content와 유사).
  • justify-self / align-self — 단일 아이템의 정렬을 재정의합니다. 다른 아이템들이 늘어나는 동안 하나의 아이템을 중앙에 배치해야 할 때 유용합니다.
  • 값: start, end, center, stretch, space-between, space-around, space-evenly(마지막 세 가지는 *-content에만).
css
/* Center all card content within its cell */
.card-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  align-items: start;     /* cards align to their cell top, not stretched */
  gap: 1.5rem;
}

/* Center a single call-to-action card both ways */
.card--cta {
  justify-self: center;
  align-self: center;
}

/* Distribute tracks with space between — useful for nav bars */
.nav-grid {
  display: grid;
  grid-template-columns: auto auto auto;
  justify-content: space-between;
}

Flexbox에서 오는 경우, 주요 차이점은 Grid 정렬이 2차원 공간에서 작동한다는 것입니다 — 두 축을 동시에 제어할 수 있습니다. Flexbox에서 교차축 정렬은 아이템의 전체 행이나 열에 적용됩니다; Grid에서는 각 아이템이 자체 셀을 가지므로, justify-selfalign-self가 래퍼 요소 없이 아이템별 제어를 제공합니다.

실제 레이아웃: 완전한 대시보드 쉘

다음은 대시보드 애플리케이션을 위한 완전한 CSS Grid 페이지 구조입니다 — 헤더, 접을 수 있는 네비게이션 사이드바, 자체 내부 그리드가 있는 메인 콘텐츠 영역, 그리고 푸터. 이것은 SaaS 대시보드나 관리 패널의 가장 바깥쪽 레이아웃 쉘로 사용할 패턴입니다.

css
/* ── Dashboard shell ──────────────────────────────────── */
.dashboard-shell {
  display: grid;
  grid-template-columns: 220px 1fr;
  grid-template-rows: 56px 1fr 44px;
  grid-template-areas:
    "topbar  topbar"
    "sidenav content"
    "sidenav footer";
  min-height: 100vh;
}

.dashboard-topbar  { grid-area: topbar;  background: #1e293b; }
.dashboard-sidenav { grid-area: sidenav; background: #0f172a; overflow-y: auto; }
.dashboard-content { grid-area: content; padding: 1.5rem; overflow-y: auto; }
.dashboard-footer  { grid-area: footer;  background: #f8fafc; border-top: 1px solid #e2e8f0; }

/* ── Collapsed sidebar variant ────────────────────────── */
.dashboard-shell--collapsed {
  grid-template-columns: 56px 1fr;
}

/* ── Content area — internal card grid ─────────────────── */
.dashboard-content {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  grid-template-rows: auto;
  align-content: start;
  gap: 1.25rem;
}

/* Stat cards take one column each; the main chart spans all */
.stat-card  { /* auto-placed, one column */ }
.main-chart { grid-column: 1 / -1; }   /* always full width */

/* ── Responsive: collapse to single-column on small screens */
@media (max-width: 768px) {
  .dashboard-shell {
    grid-template-columns: 1fr;
    grid-template-rows: 56px 1fr auto 44px;
    grid-template-areas:
      "topbar"
      "content"
      "sidenav"
      "footer";
  }
}
html
<div class="dashboard-shell">
  <header class="dashboard-topbar">
    <a href="/dashboard" class="brand">AppName</a>
    <nav class="topbar-actions">
      <button class="btn-icon" aria-label="Notifications">
        <span class="icon-bell"></span>
      </button>
      <img src="/avatar/user.png" class="user-avatar" alt="User menu">
    </nav>
  </header>

  <nav class="dashboard-sidenav">
    <ul>
      <li><a href="/dashboard">Overview</a></li>
      <li><a href="/dashboard/analytics">Analytics</a></li>
      <li><a href="/dashboard/users">Users</a></li>
      <li><a href="/dashboard/settings">Settings</a></li>
    </ul>
  </nav>

  <main class="dashboard-content">
    <div class="stat-card">Monthly Revenue</div>
    <div class="stat-card">Active Users</div>
    <div class="stat-card">Churn Rate</div>
    <div class="main-chart">Revenue Chart (full width)</div>
  </main>

  <footer class="dashboard-footer">
    <p>AppName v2.4.1 &mdash; <a href="/status">System Status</a></p>
  </footer>
</div>

이 예제에서 몇 가지 주목할 만한 사항들이 있습니다. 푸터는 템플릿 영역 정의를 사용하여 대형 화면에서 sidenav 열에 배치되어, 메인 콘텐츠 스크롤 영역과 시각적으로 분리됩니다. 행의 1fr과 콘텐츠 창의 overflow-y: auto는 콘텐츠 영역이 독립적으로 스크롤된다는 것을 의미합니다 — 상단 바와 사이드네비는 제자리에 고정되며, 이것이 표준 대시보드 UX입니다. 하단의 @media 쿼리는 HTML을 전혀 건드리지 않고 모바일을 위해 그리드 영역을 재정렬합니다.

마무리

CSS Grid의 학습 곡선은 대부분 이름에서 옵니다 — 라인, 트랙, 영역, fr 단위 — 하지만 모델이 이해되면 놀랍도록 직접적입니다. 실제 레이아웃의 경우: 페이지 쉘에는 grid-template-areas를 사용하고(와이어프레임처럼 읽힙니다), 반응형 카드 그리드에는 repeat(auto-fit, minmax())를 사용하고, 표준 흐름을 벗어나는 아이템에는 명시적 grid-column: span N을 사용하세요. 실제 작업의 90%에는 Grid의 모든 API가 필요하지 않습니다 — 이 패턴들이 대부분을 커버합니다.

추가 읽기: CSS-Tricks의 Grid 완전 가이드는 최고의 단일 페이지 참조입니다; MDN의 CSS Grid 문서는 모든 속성에 대해 깊이 다룹니다; W3C CSS Grid 레벨 1 명세는 엣지 케이스의 정확한 동작을 이해하고 싶다면 놀랍도록 읽기 쉽습니다. 브라우저 호환성은 훌륭합니다 — 현재 수치는 caniuse.com/css-grid에서 확인하세요. 라이브 프로젝트를 진행하면서 CSS를 정리하고 싶다면, 이 사이트의 CSS 포맷터가 스타일시트를 깔끔하게 정리해 줄 것입니다.