한 줄 정답 — 부모에 display: grid를 주고 grid-template-columns을 정의하면, 자식들이 그 격자에 채워진다. 행과 열을 동시에(2차원) 다루는 게 1차원인 flex와의 핵심 차이다. 반응형 카드 격자는 repeat(auto-fit, minmax(200px, 1fr)) 한 줄이면 미디어쿼리 없이도 된다.

핵심 요약#

  • Grid는 부모에 display: grid 를 주고 grid-template-columns로 열을 정의하는 데서 시작한다.
  • 남는 공간은 fr 단위로 나눈다. 1fr 1fr 1fr은 3등분, 200px 1fr은 한쪽 고정 + 나머지 채움.
  • 반복은 repeat(), 반응형 자동 줄바꿈은 repeat(auto-fit, minmax(200px, 1fr))로 만든다.
  • 아이템은 grid-column·grid-row로 여러 칸을 차지하게 하거나, grid-template-areas로 이름을 붙여 배치한다.
  • 2차원(행+열) 전체 틀은 Grid, 1차원(한 줄) 정렬은 flex — 둘은 함께 쓴다.

CSS Grid 사용법, 행과 열부터#

Grid가 flex와 갈리는 지점은 단 하나, 차원이다. flex는 한 방향(가로 또는 세로)으로 흐르는 1차원이고, Grid는 행과 열을 동시에 잡는 2차원이다. 그래서 "전체 페이지 틀"이나 "격자형 카드"처럼 가로·세로를 함께 맞춰야 할 때 Grid가 강하다.

시작은 부모에 display: grid를 주고 열을 정의하는 것이다.

.container {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr; /* 너비를 3등분한 3열 */
  gap: 16px;
}

이러면 자식들이 왼쪽 위부터 차례로 칸에 채워지고, 한 줄(3칸)이 차면 자동으로 다음 줄로 넘어간다. flex와 달리 칸의 틀을 먼저 정하고 콘텐츠를 채우는 방식이다.

컨테이너(부모)에 주는 속성#

격자의 모양은 대부분 부모에서 정한다.

grid-template-columns / rows — 열과 행 정의#

.container {
  display: grid;
  grid-template-columns: 200px 1fr 1fr; /* 첫 열 고정 200px, 나머지 둘이 남는 공간 1:1 */
  grid-template-rows: auto 1fr;         /* 첫 행은 내용만큼, 둘째 행은 남는 높이(컨테이너 높이가 있을 때) */
}

핵심은 fr(fraction) 단위다. 남는 공간을 비율로 나누는 단위라, 1fr 2fr이면 1:2로 나눈다. px·%·auto와 섞어 쓸 수 있다.

gap — 칸 사이 간격#

.container {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;          /* 행·열 간격 한 번에. row-gap / column-gap로 따로도 가능 */
}

repeat() — 반복을 짧게#

같은 열을 여러 번 적는 대신 repeat()로 줄인다.

.container {
  display: grid;
  grid-template-columns: repeat(3, 1fr); /* 1fr 1fr 1fr 과 같음 */
}

minmax() + auto-fit — 미디어쿼리 없는 반응형#

Grid의 가장 강력한 패턴이다. 화면 폭에 따라 열 개수가 알아서 늘고 주는 카드 격자를 한 줄로 만든다.

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 16px;
}

minmax(200px, 1fr)은 "각 칸을 최소 200px, 최대 남는 공간(1fr)"으로 잡으라는 뜻이고, auto-fit은 폭이 허락하는 만큼 칸을 채운 뒤 남는 칸을 접어(최댓값이 1fr처럼 가변일 때) 아이템을 그만큼 늘린다. 비슷한 auto-fill은 빈 칸을 접지 않고 남겨 둔다. 아이템이 적을 때 칸을 끝까지 늘리고 싶으면 auto-fit, 빈 칸을 그대로 두고 고정 폭을 유지하고 싶으면 auto-fill을 쓴다.

정의한 칸보다 아이템이 많으면#

grid-template으로 정한 칸보다 아이템이 많아지면 브라우저가 행을 자동으로 더 만든다(암묵적 격자). 이렇게 생기는 행의 높이는 기본적으로 내용만큼인데, grid-auto-rows로 지정할 수 있다.

.container {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: 120px; /* 자동으로 생기는 행의 높이 */
}

칸 안 정렬 vs 격자 전체 정렬#

정렬 속성이 두 갈래라 헷갈리기 쉽다.

.container {
  display: grid;
  /* 각 칸 '안에서' 아이템 정렬 */
  justify-items: center; /* 가로(인라인 축) */
  align-items: center;   /* 세로(블록 축) */
  /* 격자 '전체'를 컨테이너 안에서 정렬 (격자가 더 작을 때) */
  justify-content: center;
  align-content: center;
}
  • justify-items / align-items칸 안에서 아이템을 정렬한다. 둘을 합친 단축이 place-items.
  • justify-content / align-content — 격자 묶음이 컨테이너보다 작을 때 격자 전체를 정렬한다.

아이템(자식)에 주는 속성#

특정 칸을 여러 칸에 걸치게 하거나, 이름으로 배치할 수 있다.

grid-column / grid-row — 여러 칸 차지#

격자의 선(line) 번호로 시작~끝을 지정한다. 선은 1번부터 시작한다.

.hero {
  grid-column: 1 / 3; /* 1번 선부터 3번 선까지 = 2칸 차지 */
  grid-row: 1 / 2;
}

.wide {
  grid-column: span 2; /* 현재 위치에서 2칸 차지 */
}

grid-template-areas — 이름으로 레이아웃#

칸에 이름을 붙여 그림 그리듯 배치하면 한눈에 읽힌다.

.layout {
  display: grid;
  grid-template-columns: 200px 1fr;
  grid-template-areas:
    "header header"
    "sidebar main"
    "footer footer";
  gap: 16px;
}

.layout .header { grid-area: header; }
.layout .sidebar { grid-area: sidebar; }
.layout .main { grid-area: main; }
.layout .footer { grid-area: footer; }

실전 예제#

반응형 카드 격자 — 미디어쿼리 없이#

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 16px;
}

칸 하나가 220px 밑으로 좁아지면 열 수가 자동으로 줄고, 넓어지면 늘어난다. 대부분 이 한 줄로 충분하고, 특정 폭에서 열 수를 강제하는 등 세밀한 제어가 필요할 때만 미디어쿼리를 더한다.

페이지 레이아웃 — 헤더·사이드바·본문·푸터#

<div class="layout">
  <header class="header">헤더</header>
  <aside class="sidebar">사이드바</aside>
  <main class="main">본문</main>
  <footer class="footer">푸터</footer>
</div>
.layout {
  display: grid;
  grid-template-columns: 200px 1fr;
  grid-template-areas:
    "header header"
    "sidebar main"
    "footer footer";
  min-height: 100vh;
  gap: 16px;
}

.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main { grid-area: main; }
.footer { grid-area: footer; }

헤더와 푸터는 두 열을 모두 차지하고, 가운데 줄만 사이드바와 본문으로 나뉜다. 영역 이름만 봐도 구조가 읽히는 게 grid-template-areas의 장점이다.

Grid vs Flex, 언제 무엇을?#

둘은 경쟁이 아니라 역할이 다르다.

기준FlexGrid
차원1차원(한 줄)2차원(행+열)
접근콘텐츠 흐름에 맞춰 배치틀을 먼저 정하고 채움
잘 맞는 곳내비게이션 바, 버튼 줄, 컴포넌트 내부 정렬페이지 전체 틀, 격자형 카드, 대시보드

실무에선 같이 쓴다. 페이지의 큰 틀은 Grid로 잡고, 그 안의 헤더 바나 카드 내부 요소 정렬은 flex로 처리하는 식이다. flex 자체가 헷갈린다면 CSS flexbox 정렬 글을 먼저 보면 좋다.

자주 묻는 질문#

Q. grid-template-columns에서 fr은 무슨 단위인가요?

A. 남는 공간을 비율로 나누는 단위(fraction)입니다. 1fr 1fr 1fr은 가용 너비를 3등분하고, 200px 1fr은 200px를 먼저 떼어낸 뒤 나머지를 한 칸이 다 차지합니다. 화면 폭이 바뀌면 fr 칸들이 비율을 유지하며 같이 늘고 줄어듭니다.

Q. 미디어쿼리 없이 반응형 격자를 만들 수 있나요?

A. 네. grid-template-columns: repeat(auto-fit, minmax(200px, 1fr))을 쓰면 칸이 최소 너비 밑으로 좁아질 때 열 수가 자동으로 줄고, 넓어지면 늘어납니다. 대부분의 카드 목록은 이 한 줄로 충분합니다.

Q. Grid와 Flex는 무엇이 다른가요? 언제 뭘 쓰나요?

A. Flex는 한 방향으로 흐르는 1차원, Grid는 행과 열을 동시에 잡는 2차원입니다. 내비게이션 바·버튼 줄·컴포넌트 내부 정렬은 flex, 페이지 전체 틀·격자형 카드·대시보드는 Grid가 맞습니다. 보통 큰 틀은 Grid, 그 안의 정렬은 flex로 함께 씁니다.

Q. 특정 칸을 여러 칸에 걸치게 하려면요?

A. grid-column·grid-row에 선 번호를 주면 됩니다. 예를 들어 grid-column: 1 / 3은 1번 선부터 3번 선까지 두 칸을 차지하고, grid-column: span 2는 현재 위치에서 두 칸을 차지합니다.

관련 글#

레이아웃은 결국 Grid와 flex를 상황에 맞게 섞는 일이다. 1차원 정렬과 반응형 분기를 함께 익혀두면 응용이 쉬워진다.

더 알아보기#