Oneul Code - 02.display: grid

2025-09-15

📦 CSS Grid

1. display: grid의 중요성

웹 레이아웃을 만들 때, 과거에는 float이나 flexbox를 주로 사용했지만 복잡한 2차원 레이아웃을 만들 때는 한계가 있었습니다.
display: flex는 1차원(행 또는 열) 레이아웃에 최적화되어 있지만, CSS Grid는 행과 열을 동시에 다룰 수 있는 2차원 레이아웃 시스템입니다.
전체 웹페이지의 틀을 잡거나 복잡한 형태의 레이아웃을 만들 때 매우 유용합니다. 저와 같이 개발을 처음 시작하시는 분들은 display:flexbox는 비교적 쉽게 이해가 되지만 grid는 저는 조금 난해한 부분이 많아 이해를 하는데 힘들었습니다. (지금도 마찬가지입니다…ㅎㅎ) 그래서 오늘의 주제로 가져오게 되었습니다.

2. Flexbox와 Grid 비교

✅ Grid의 장점

  • 2차원 레이아웃에 최적회: 행과 열을 동시에 제어하면서 복잡한 구조를 쉽게 만들 수 있습니다.

  • 유지보수 용이성: grid-template-aras를 사용하면 코드만 보고도 레이아웃 구조를 한눈에 파악할 수 있습니다.

  • 반응형 설계의 편리성: repeat(auto-fit, minmax(...))와 같은 함수를 활용하면 미디어 쿼리 없이도 반응형 레이아웃을 만들 수 있습니다.

❌ Grid의 단점

  • 1차원 정렬에는 과할 수 있음: 단순한 수평/수직 정렬에는 Flexbox가 더 간단하고 직관적입니다.

  • 구형 브라우저 지원 문제: IE 등 구형 브라우저에서는 지원이 제한적일 수 있습니다.

3. Grid 기본 개념

  • Grid Container: display: grid 속성이 적용된 부모 요소를 말합니다. 이 컨테이너 안에 자식 요소들이 격자 모양으로 배치됩니다.

  • Grid Item: Grid Container의 직접적인 자식 요소를 뜻합니다. 이 아이템들이 격자 칸에 들어가는 실제 내용물이 됩니다.

  • Grid Line:: 격자를 나누는 수평, 수직 선을 의미해요. grid-column이나 grid-row 속성으로 아이템의 위치를 지정할 때 이 선의 번호를 사용합니다.

  • Grid Track: 두 개의 Grid Line 사이의 공간, 즉 행 또는 열을 말합니다.

  • Grid Cell: Grid의 가장 작은 단위로, 네 개의 Grid Line으로 둘러싸인 한 칸을 의미합니다.

  • Grid Area: 하나 이상의 Grid Cell이 합쳐져 만들어진 직사각형 영역입니다. 이름을 지정해서 특정 영역을 쉽게 만들 수 있어요.

4. Grid의 주요 속성

🔹 display: grid; – Grid 컨테이너 만들기

.container {
  display: grid;
}
  • .container 는 Grid 컨테이너가 되고, 그 안에 있는 모든 자식 요소들은 Grid 아이템이 됩니다.

🔹 grid-template-columns / grid-template-rows - 행과 열 설정

.container {
  grid-template-columns: 100px 1fr 200px;
  grid-template-rows: 50px 1fr;
}
  • fr: 남은 공간을 비율로 나누어 가집니다. (fraction의 약자)

  • repeat(): repeat(3, 1fr)처럼 반복되는 열/행을 간결하게 정의할 수 있습니다.

🔹 grid-template-areas - 레이아웃 영역 이름 지정

.container {
  grid-template-areas:
    "header header"
    "sidebar main";
}
  • 각 영역에 이름을 부여해 레이아웃의 구조를 한눈에 파악할 수 있게 해줍니다.

🔹 gap - 아이템 간 간격 설정

.container {
  gap: 20px; /* 행과 열 모두 20px 간격 */
  /* 또는 */
  gap: 20px 30px; /* 행 간격 20px, 열 간격 30px */
}

🔹 grid-column / grid-row - 아이템의 위치 및 크기 설정

.item {
  grid-column: 1 / 3; /* 1번 라인부터 3번 라인까지 차지 */
  grid-row: 2 / span 2; /* 2번 라인부터 2칸 차지 */
}

4. 자주 헷갈리는 부분 정리

🔍 fr 단위의 의미

fr은 Grid 레이아웃에서 가장 중요한 단위 중 하나입니다. grid-template-columns: 200px 1fr 1fr은 컨테이너 너비에서 200px을 뺀 나머지 공간을 두 아이템이 1:1 비율로 나누어 가진다는 의미입니다.

🔄 grid-template-areas와 grid-area의 차이점

  • grid-template-areas: Grid 컨테이너에 적용하며, 전체 레이아웃의 영역들을 정의합니다.

  • grid-area: Grid 아이템에 적용하며, 해당 아이템이 어떤 영역에 위치할지 이름을 지정하거나 row-start / col-start / row-end / col-end 형식으로 위치를 직접 설정합니다.

🧱 justify-content vs justify-items

  • justify-content: Grid 자체를 컨테이너 내에서 수평으로 정렬합니다.

  • justify-items: 개별 Grid 아이템의 내용을 수평으로 정렬합니다. (Flexbox의 justify-content와는 다른 동작 방식)

5. Grid 심화

🔹 미디어 쿼리 없는 반응형 Grid

  • repeat(auto-fit, minmax(250px, 1fr))를 사용하면 화면 크기에 따라 자동으로 열의 개수를 조절하는 유연한 레이아웃을 만들 수 있습니다.
.responsive-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 20px;
}

🔹 Subgrid 활용하기

  • display: subgrid는 중첩된 그리드 아이템이 부모 그리드의 열이나 행을 그대로 상속받게 해줍니다. 예를 들어, 카드 리스트에서 모든 카드의 제목, 내용, 버튼 위치를 완벽하게 맞출 때 유용합니다.
.card-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
}

.card {
  display: grid;
  grid-template-rows: subgrid; /* 부모의 행을 상속 */
  grid-row: span 3; /* 세 칸 차지 */
}

🔹 grid-auto-rows와 grid-auto-flow

  • grid-auto-rows: 명시적으로 정의되지 않은 행의 크기를 자동으로 설정합니다.

  • grid-auto-flow: 아이템이 자동으로 배치되는 방향을 제어합니다.

6. 예제: Grid를 활용한 레이아웃 구성

기본 예제

1. 기본 웹사이트 레이아웃

.layout {
  display: grid;
  min-height: 100vh;
  grid-template-columns: 250px 1fr;
  grid-template-rows: 80px 1fr 60px;
  grid-template-areas:
    "header header"
    "sidebar main"
    "footer footer";
}

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

2. 카드 그리드 레이아웃

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 20px;
  padding: 20px;
}

.card {
  background: white;
  border-radius: 8px;
  padding: 20px;
  box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}

3. 성배 레이아웃 (Holy Grail Layout)

.holy-grail {
  display: grid;
  grid-template-columns: 200px 1fr 150px;
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "header header header"
    "nav main ads"
    "footer footer footer";
  min-height: 100vh;
}

4. 매거진 스타일 레이아웃

.magazine {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  grid-template-rows: repeat(4, 200px);
  gap: 10px;
}

.featured {
  grid-column: 1 / 4;
  grid-row: 1 / 3;
}

.story-1 {
  grid-column: 4 / 7;
  grid-row: 1;
}

.story-2 {
  grid-column: 4 / 6;
  grid-row: 2;
}

반응형 Grid 설계

1. 미디어 쿼리 없는 반응형

/* 자동으로 반응하는 그리드 */
.responsive-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 20px;
}

2. 복잡한 반응형 레이아웃

.responsive-layout {
  display: grid;
  gap: 20px;
  grid-template-areas:
    "header"
    "main"
    "sidebar"
    "footer";
}

@media (min-width: 768px) {
  .responsive-layout {
    grid-template-columns: 1fr 300px;
    grid-template-areas:
      "header header"
      "main sidebar"
      "footer footer";
  }
}

@media (min-width: 1024px) {
  .responsive-layout {
    grid-template-columns: 250px 1fr 300px;
    grid-template-areas:
      "nav header header"
      "nav main sidebar"
      "nav footer footer";
  }
}

3. Container Queries와 Grid

.card-container {
  container-type: inline-size;
}

.card-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
}

@container (min-width: 400px) {
  .card-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

@container (min-width: 600px) {
  .card-grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

심화 예제

1. Subgrid 활용하기

.main-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
}

.sub-container {
  display: grid;
  grid-template-columns: subgrid;
  grid-column: 1 / 4;
  gap: inherit;
}

2. 암시적 그리드 제어

.container {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  
  /* 암시적으로 생성되는 행의 크기 */
  grid-auto-rows: minmax(100px, auto);
  
  /* 아이템이 배치되는 방향 */
  grid-auto-flow: row | column | row dense | column dense;
}

3. 동적 그리드 아이템

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

.item:nth-child(3n) {
  grid-row: span 2;
}

.item:nth-child(5n) {
  grid-column: span 2;
}

4. 오버랩 레이아웃

.overlap-container {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-template-rows: repeat(3, 200px);
}

.background-image {
  grid-column: 1 / 5;
  grid-row: 1 / 4;
  z-index: 1;
}

.content-overlay {
  grid-column: 2 / 4;
  grid-row: 2 / 3;
  z-index: 2;
  background: rgba(255, 255, 255, 0.9);
  padding: 20px;
}

7. 코딩하면서 자주 하는 실수들

1. fr 단위 오해

/* 잘못된 사용 */
.wrong {
  grid-template-columns: 200px 1fr 1fr; /* 200px + 남은공간의 1/2 + 남은공간의 1/2 */
}

/* 올바른 사용 */
.correct {
  grid-template-columns: 200px 2fr 1fr; /* 고정폭 + 남은공간의 2/3 + 남은공간의 1/3 */
}

2. grid-area 순서 혼동

/* 올바른 순서: row-start / column-start / row-end / column-end */
.item {
  grid-area: 1 / 2 / 3 / 4;
}

3. 불필요한 복잡성

/* 복잡한 방법 */
.complex {
  grid-column-start: 1;
  grid-column-end: 3;
  grid-row-start: 2;
  grid-row-end: 4;
}

/* 간단한 방법 */
.simple {
  grid-area: 2 / 1 / 4 / 3;
}

8. 추가 학습 자료

8. Oneul Code를 정리하며…

오늘 Grid에 대해서 학습하고 블로그 게시물을 작성하면서 직접 코드를 작성하고 글을 써보니, Flexbox와 Grid가 어떤 점에서 다른지, 그리고 복잡한 레이아웃을 grid속성을 활용해 만들 수 있는지 이해하게 되었습니다.
Oneul Code는 오늘 학습한 내용을 기록하면서, 이 글을 읽는 여러분도 바로 활용할 수 있기를 바랍니다.