CSS Grid 실습: Flex 학습 카드로 열 맞추기와 auto-fit 비교

2026.07.28·수정 2026.09.14·약 27분·작성: 해비·블로그 소개

Flex에서 만든 학습 카드로 Grid의 행·열, minmax, auto-fit과 auto-fill을 비교합니다. 작은 코드에서 시작해 두 파일로 완성하고 긴 내용의 넘침까지 확인합니다.

시작·완성 예제 ZIP 내려받기 — 압축을 푼 뒤 starter/index.html을 브라우저에서 엽니다. 외부 서버나 JavaScript 없이 실행할 수 있습니다. Network 확인이 필요한 경우 README의 로컬 서버 실행 방법을 따르세요.

CSS Grid 실습: Flex 학습 카드로 열 맞추기와 auto-fit 비교 핵심 개념 도식
Grid는 열과 행의 트랙을 기준으로 항목을 배치합니다. 열 정의와 auto-fit에 따른 변화를 비교합니다.

1. Flex 다음에 Grid를 사용하는 이유

앞선 Flex 실습의 HTML 문서, CSS 박스, Flex 배치 카드를 그대로 사용합니다. 바꾸는 핵심은 목록 부모의 배치입니다. Flex는 각 줄에서 공간을 나누므로 마지막 줄의 카드가 넓어질 수 있습니다. Grid는 같은 열 트랙을 여러 행이 공유하게 만들 수 있습니다. 카드 내부에서 링크를 아래로 미는 Flex는 계속 써도 됩니다. 두 방식은 한 페이지에서 함께 사용할 수 있습니다.

.course-list {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1rem;
}

이 코드를 적용하면 목록의 직접 자식 세 개가 같은 너비의 열에 놓입니다. repeat(3, …)은 트랙 정의를 세 번 반복합니다. fr은 남는 공간을 비율로 나누는 단위라서 1fr 세 개는 같은 비율이고 2fr 1fr은 남는 공간을 2:1로 나눕니다. gap은 트랙 사이 공간이며 바깥 여백을 추가하지 않습니다. 완성 CSS는 좁은 화면에 한 열을 먼저 제공하고 48rem 이상에서만 세 열로 전환합니다.

2. 컨테이너, 아이템, 트랙과 축 구분하기

개념 이 예제에서 확인할 곳
Grid 컨테이너 display: grid가 있는 .course-list
Grid 아이템 목록의 직접 자식 article 세 개
열 트랙 grid-template-columns로 정한 공간
행 트랙 카드가 늘어나면 자동 배치로 추가되는 행
그리드 선 트랙을 나누는 경계이며, 세 열에는 네 개의 열 선이 생깁니다.

이 문서는 기본 writing-mode: horizontal-tb를 사용합니다. 이때 열은 좌우로 나열되고 행은 위아래로 쌓입니다. justify-items는 각 셀의 인라인 축, align-items는 블록 축에서 아이템을 정렬합니다. 세로쓰기에서는 물리적인 가로·세로 대응이 달라지므로 justify를 무조건 가로로 외우지 마세요. justify-content와 align-content는 아이템 하나가 아니라 전체 트랙 묶음과 컨테이너 사이의 남는 공간을 다룹니다.

3. auto-fit과 auto-fill을 실제로 비교하기

.fit { grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr)); }
.fill { grid-template-columns: repeat(auto-fill, minmax(min(100%, 14rem), 1fr)); }

비교 영역에는 각각 카드 두 개만 넣었습니다. 창을 충분히 넓히면 최소 14rem짜리 열이 세 개 이상 들어갈 수 있습니다. auto-fill은 들어갈 수 있는 반복 열을 유지하여 오른쪽에 비어 있는 열 공간이 보입니다. auto-fit은 배치 후 비어 있는 반복 트랙을 접으므로 두 카드가 남은 너비를 나눠 가집니다. 빈 열이 생기지 않는 좁은 폭에서는 두 예제가 같아 보이는 것이 정상입니다.

minmax의 최소값에 min(100%, 14rem)을 넣은 이유는 가용 너비가 14rem보다 작은 경우 한 카드가 부모보다 커지는 것을 막기 위해서입니다. 이는 카드에 무조건 14rem을 보장하는 규칙이 아닙니다. 기본 글꼴 16px이라면 비교 부모 1000px에는 224px짜리 열 네 개와 세 gap이 들어가므로 차이를 관찰하기 쉽습니다. DevTools의 Grid overlay를 켜서 카드의 실제 너비와 빈 트랙을 함께 확인하세요.

4. minmax(0, 1fr)과 긴 내용의 넘침

단순한 1fr 트랙은 자동 최소 크기의 영향을 받아 긴 내용 때문에 예상보다 덜 줄어들 수 있습니다. minmax(0, 1fr)은 트랙의 최소값을 0으로 명시합니다. 하지만 트랙이 줄어드는 것과 글자가 줄바꿈하는 것은 다른 문제입니다. 아이템의 min-width: 0과 overflow-wrap: anywhere도 함께 확인해야 합니다.

.course-card {
  min-width: 0;
  overflow-wrap: anywhere;
}

긴 식별자가 있는 세 번째 카드에서 overflow-wrap만 잠시 해제하면 트랙은 줄어들어도 글자가 바깥으로 나올 수 있습니다. 이후 min-width와 트랙 최소값도 한 번에 하나씩 바꿔 어떤 제약이 넘침을 만드는지 살펴보세요. overflow-x: hidden으로 잘라 버리면 원인은 남고 내용만 사라집니다. 고정 너비 이미지나 자식의 min-width가 있으면 별도로 점검해야 합니다.

5. 완료 기준과 다음 실습

  • 320px에서는 첫 영역이 한 열이고 긴 식별자도 읽힙니다.
  • 기본 글꼴 16px일 때 767px·768px 부근에서 첫 영역의 열 전환을 비교합니다.
  • 1000px 이상에서 auto-fit의 카드가 auto-fill보다 넓어지는지 확인합니다.
  • 카드를 하나 복제하면 네 번째 카드가 다음 행의 첫 열에 놓입니다. 첫 영역의 앞 행과 열 위치가 맞아야 합니다.
  • Tab 순서는 HTML에 적은 링크 순서입니다. grid-row나 order로 보이는 순서만 바꾸지 않습니다.

grid-template-areas는 이름으로 영역을 배치하는 선택 학습입니다. 이름이 있는 영역은 직사각형을 이루어야 하고 DOM 읽기 순서를 바꾸지 않는 범위에서 사용합니다. 이번 실습의 목표인 카드 열 맞추기에는 필요하지 않습니다. 다음 Container Query 실습에서는 같은 카드가 놓인 부모 너비에 따라 내부 배치를 바꿉니다.

공식 규칙 확인: W3C CSS Grid 명세, MDN repeat().

전체 코드: 두 파일로 완성하기

아래 HTML은 complete/index.html, CSS는 complete/styles.css와 같습니다. 같은 폴더에 저장하세요. 이미지가 있는 실습은 ZIP에 포함된 images 폴더도 같은 위치에 둡니다.

index.html

실습 파일

파일 경로를 확인하고 같은 프로젝트 안에 저장하세요. 이미지 등 소스 목록에 없는 파일과 실행 안내는 실습 ZIP에 포함되어 있습니다.

카드의 행과 열을 함께 맞추도록 Grid 배치를 구성합니다. 수정 전은 시작 상태, 수정 후는 비교할 완성 상태입니다.

수정 전

index.html

<!doctype html>
<html lang="ko">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>학습 카드 | Grid 실습</title>
    <link rel="stylesheet" href="styles.css" />
  </head>
  <body>
    <a href="#main">본문으로 바로 가기</a>
    <header><p>퍼블리싱 연습실</p></header>
    <main id="main" tabindex="-1">
      <h1>같은 학습 카드, 행과 열 맞추기</h1>
      <p>Flex 실습의 HTML·CSS·Flex 카드를 Grid로 배치합니다.</p>
      <section>
        <h2>01 / 세 카드의 열 맞추기</h2>
        <div class="course-list">
          <article class="course-card">
            <h3>HTML 문서</h3>
            <p>내용의 역할에 맞게 제목, 목록, 폼을 작성합니다.</p>
            <a class="card-link" href="#html-task">HTML 문서 과제 보기</a>
          </article>
          <article class="course-card">
            <h3>CSS 박스</h3>
            <p>패딩과 테두리가 차지하는 크기를 계산합니다. 선택자 충돌도 확인합니다.</p>
            <a class="card-link" href="#css-task">CSS 박스 과제 보기</a>
          </article>
          <article class="course-card">
            <h3>Flex 배치</h3>
            <p>긴 식별자: publishingpracticewithoutspaces1234567890</p>
            <a class="card-link" href="#flex-task">Flex 배치 과제 보기</a>
          </article>
        </div>
      </section>
      <section>
        <h2>02 / auto-fit: 비어 있는 반복 트랙 접기</h2>
        <div class="demo fit">
          <article class="course-card">
            <h3>HTML 문서</h3>
            <p>내용의 역할에 맞게 제목, 목록, 폼을 작성합니다.</p>
            <a class="card-link" href="#html-task">HTML 문서 과제 보기</a>
          </article>
          <article class="course-card">
            <h3>CSS 박스</h3>
            <p>패딩과 테두리가 차지하는 크기를 계산합니다. 선택자 충돌도 확인합니다.</p>
            <a class="card-link" href="#css-task">CSS 박스 과제 보기</a>
          </article>
        </div>
      </section>
      <section>
        <h2>03 / auto-fill: 비어 있는 반복 트랙 유지하기</h2>
        <div class="demo fill">
          <article class="course-card">
            <h3>HTML 문서</h3>
            <p>내용의 역할에 맞게 제목, 목록, 폼을 작성합니다.</p>
            <a class="card-link" href="#html-task">HTML 문서 과제 보기</a>
          </article>
          <article class="course-card">
            <h3>CSS 박스</h3>
            <p>패딩과 테두리가 차지하는 크기를 계산합니다. 선택자 충돌도 확인합니다.</p>
            <a class="card-link" href="#css-task">CSS 박스 과제 보기</a>
          </article>
        </div>
      </section>
      <section>
        <h2>확인 과제</h2>
        <ul>
          <li id="html-task">문서 순서와 Tab 순서를 비교합니다.</li>
          <li id="css-task">DevTools에서 긴 식별자의 줄바꿈을 껐다 켭니다.</li>
          <li id="flex-task">
            두 카드만 있는 비교 영역을 1000px 이상의 창에서 확인합니다.
          </li>
        </ul>
      </section>
    </main>
  </body>
</html>

styles.css

*,
*::before,
*::after {
  box-sizing: border-box;
}
body {
  margin: 0;
  padding: 1rem;
  color: #202020;
  background: #fff;
  font-family: system-ui, sans-serif;
  line-height: 1.7;
}
header,
main {
  max-width: 72rem;
  margin-inline: auto;
}
a {
  color: inherit;
  text-underline-offset: 0.2em;
}
a:focus-visible {
  outline: 2px solid #202020;
  outline-offset: 4px;
}
h1 {
  font-size: clamp(1.6rem, 4vw, 2.5rem);
  line-height: 1.3;
}
h2 {
  font-size: 1.3rem;
}
h3 {
  margin-top: 0;
  font-size: 1.1rem;
}
section {
  margin-block: 2.5rem;
}
/* TODO 1: 목록에 Grid와 gap을 적용하세요. */
.course-list,
.demo {
  display: block;
}
.course-card {
  padding: 1rem;
  border-top: 1px solid #777;
  background: #f3f3f3;
}
/* TODO 2: 48rem 이상에서 세 열을 만드세요. */
/* TODO 3: .fit과 .fill에 자동 반복 열을 각각 적용하세요. */
/* TODO 4: 긴 식별자의 넘침과 카드 링크 위치를 처리하세요. */

수정 방법

index.html

이 파일은 변경하지 않습니다. 다른 파일을 수정하는 동안 기존 내용을 유지하세요.

styles.css

styles.css에서 아래 차이가 있는 위치를 수정하세요. -는 삭제할 줄, +는 추가할 줄이며 표시는 실제 코드에 넣지 않습니다.

카드의 행과 열을 함께 맞추도록 Grid 배치를 구성합니다.

--- 수정 전/styles.css
+++ 수정 후/styles.css
@@ -38,16 +38,38 @@
 section {
   margin-block: 2.5rem;
 }
-/* TODO 1: 목록에 Grid와 gap을 적용하세요. */
 .course-list,
 .demo {
-  display: block;
+  display: grid;
+  gap: 1rem;
+}
+.course-list {
+  grid-template-columns: minmax(0, 1fr);
 }
 .course-card {
+  min-width: 0;
+  display: flex;
+  flex-direction: column;
   padding: 1rem;
   border-top: 1px solid #777;
   background: #f3f3f3;
+  overflow-wrap: anywhere;
 }
-/* TODO 2: 48rem 이상에서 세 열을 만드세요. */
-/* TODO 3: .fit과 .fill에 자동 반복 열을 각각 적용하세요. */
-/* TODO 4: 긴 식별자의 넘침과 카드 링크 위치를 처리하세요. */
+.card-link {
+  margin-top: auto;
+  padding-top: 1rem;
+}
+.demo {
+  background: #ddd;
+}
+.fit {
+  grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr));
+}
+.fill {
+  grid-template-columns: repeat(auto-fill, minmax(min(100%, 14rem), 1fr));
+}
+@media (min-width: 48rem) {
+  .course-list {
+    grid-template-columns: repeat(3, minmax(0, 1fr));
+  }
+}

수정 후 탭의 전체 파일과 비교한 뒤 본문의 확인 절차를 실행하세요.

수정 후

index.html

<!doctype html>
<html lang="ko">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>학습 카드 | Grid 실습</title>
    <link rel="stylesheet" href="styles.css" />
  </head>
  <body>
    <a href="#main">본문으로 바로 가기</a>
    <header><p>퍼블리싱 연습실</p></header>
    <main id="main" tabindex="-1">
      <h1>같은 학습 카드, 행과 열 맞추기</h1>
      <p>Flex 실습의 HTML·CSS·Flex 카드를 Grid로 배치합니다.</p>
      <section>
        <h2>01 / 세 카드의 열 맞추기</h2>
        <div class="course-list">
          <article class="course-card">
            <h3>HTML 문서</h3>
            <p>내용의 역할에 맞게 제목, 목록, 폼을 작성합니다.</p>
            <a class="card-link" href="#html-task">HTML 문서 과제 보기</a>
          </article>
          <article class="course-card">
            <h3>CSS 박스</h3>
            <p>패딩과 테두리가 차지하는 크기를 계산합니다. 선택자 충돌도 확인합니다.</p>
            <a class="card-link" href="#css-task">CSS 박스 과제 보기</a>
          </article>
          <article class="course-card">
            <h3>Flex 배치</h3>
            <p>긴 식별자: publishingpracticewithoutspaces1234567890</p>
            <a class="card-link" href="#flex-task">Flex 배치 과제 보기</a>
          </article>
        </div>
      </section>
      <section>
        <h2>02 / auto-fit: 비어 있는 반복 트랙 접기</h2>
        <div class="demo fit">
          <article class="course-card">
            <h3>HTML 문서</h3>
            <p>내용의 역할에 맞게 제목, 목록, 폼을 작성합니다.</p>
            <a class="card-link" href="#html-task">HTML 문서 과제 보기</a>
          </article>
          <article class="course-card">
            <h3>CSS 박스</h3>
            <p>패딩과 테두리가 차지하는 크기를 계산합니다. 선택자 충돌도 확인합니다.</p>
            <a class="card-link" href="#css-task">CSS 박스 과제 보기</a>
          </article>
        </div>
      </section>
      <section>
        <h2>03 / auto-fill: 비어 있는 반복 트랙 유지하기</h2>
        <div class="demo fill">
          <article class="course-card">
            <h3>HTML 문서</h3>
            <p>내용의 역할에 맞게 제목, 목록, 폼을 작성합니다.</p>
            <a class="card-link" href="#html-task">HTML 문서 과제 보기</a>
          </article>
          <article class="course-card">
            <h3>CSS 박스</h3>
            <p>패딩과 테두리가 차지하는 크기를 계산합니다. 선택자 충돌도 확인합니다.</p>
            <a class="card-link" href="#css-task">CSS 박스 과제 보기</a>
          </article>
        </div>
      </section>
      <section>
        <h2>확인 과제</h2>
        <ul>
          <li id="html-task">문서 순서와 Tab 순서를 비교합니다.</li>
          <li id="css-task">DevTools에서 긴 식별자의 줄바꿈을 껐다 켭니다.</li>
          <li id="flex-task">
            두 카드만 있는 비교 영역을 1000px 이상의 창에서 확인합니다.
          </li>
        </ul>
      </section>
    </main>
  </body>
</html>

styles.css

*,
*::before,
*::after {
  box-sizing: border-box;
}
body {
  margin: 0;
  padding: 1rem;
  color: #202020;
  background: #fff;
  font-family: system-ui, sans-serif;
  line-height: 1.7;
}
header,
main {
  max-width: 72rem;
  margin-inline: auto;
}
a {
  color: inherit;
  text-underline-offset: 0.2em;
}
a:focus-visible {
  outline: 2px solid #202020;
  outline-offset: 4px;
}
h1 {
  font-size: clamp(1.6rem, 4vw, 2.5rem);
  line-height: 1.3;
}
h2 {
  font-size: 1.3rem;
}
h3 {
  margin-top: 0;
  font-size: 1.1rem;
}
section {
  margin-block: 2.5rem;
}
.course-list,
.demo {
  display: grid;
  gap: 1rem;
}
.course-list {
  grid-template-columns: minmax(0, 1fr);
}
.course-card {
  min-width: 0;
  display: flex;
  flex-direction: column;
  padding: 1rem;
  border-top: 1px solid #777;
  background: #f3f3f3;
  overflow-wrap: anywhere;
}
.card-link {
  margin-top: auto;
  padding-top: 1rem;
}
.demo {
  background: #ddd;
}
.fit {
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr));
}
.fill {
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 14rem), 1fr));
}
@media (min-width: 48rem) {
  .course-list {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

index.html 전체 코드 보기

styles.css

styles.css 전체 코드 보기

이 글이 도움이 되었나요?

조회 중

웹 퍼블리싱 학습 순서

필수 13개 · 전체 15개

읽음 기록 관리

전체 과정 목차 (15개)
  1. 필수 학습 · HTML 입문: 시맨틱 문서와 접근 가능한 폼 만들기
  2. 필수 학습 · CSS 입문: 박스 모델·선택자·캐스케이드 직접 확인하기
  3. 필수 학습 · CSS Flex와 미디어 쿼리 입문: 반응형 카드 페이지 만들기
  4. 필수 학습 · CSS position 완전 이해: relative·absolute·fixed·sticky 실습
  5. 필수 학습 · HTML details와 summary로 접근 가능한 FAQ 만들기
  6. 필수 학습 · CSS Grid 실습: Flex 학습 카드로 열 맞추기와 auto-fit 비교 현재 글
  7. 필수 학습 · CSS overflow 해결: 긴 텍스트·min-width: 0·말줄임 실습
  8. 필수 학습 · HTML 표 만들기: caption·scope로 비교표를 읽기 쉽게, 모바일에서는 표만 스크롤하기
  9. 필수 선수 · HTML CSS 이미지 문제 해결 실습: 경로·비율·잘림·흐림·CLS
  10. 필수 학습 · Container Query 실습: 같은 카드를 본문과 사이드바에 배치하기
  11. 선택 참고 · CSS Baseline 보는 법: 지원 범위와 폴백을 직접 확인하기
  12. 필수 학습 · CSS z-index가 안 먹힐 때: stacking context와 부모 층 디버깅
  13. 필수 선수 · 웹 접근성 기초 점검: 키보드 순서·초점 표시·확대 화면을 직접 확인하는 체크리스트
  14. 필수 학습 · HTML CSS 종합 실습: 소개·카드·이미지·요금표·문의 폼·FAQ가 있는 반응형 서비스 페이지
  15. 선택 참고 · 프론트엔드 SEO 기초: HTML 구조와 이미지 성능 진단하기

새 글 받아보기

RSS 리더에서 BlogFlow의 새 글을 확인할 수 있습니다.

RSS 피드 구독하기

댓글 남기기