본문으로 건너뛰기

flex 여러 줄과 grid

HTML·CSS 목차격자로 배치: grid레슨 71 / 93

목차

grid(격자)는 부모가 열을 먼저 나눠 두고 직계 자식을 그 칸에 차례로 놓는 배치입니다. flex도 flex-wrap으로 여러 줄을 만들지만, 줄마다 남는 자리를 따로 나눠 마지막 줄 항목이 위 줄과 폭이 달라집니다.

레시피 카드 여섯 장을 여러 줄로 놓으면 위아래 카드의 폭과 경계가 맞아야 훑어보기 좋습니다. 12단원의 flex로 줄을 바꾸면 첫 줄은 고르게 나뉘는데, 마지막 줄의 두 장만 넓어집니다. 이 레슨은 flex와 grid가 여러 줄을 나누는 방식의 차이와, 둘 가운데 무엇을 고르는지를 봅니다.

이 단원을 마치면 레시피 목록의 카드 여섯 장을 목록 폭에 맞춰 세 열 · 두 열 · 한 열로 바뀌는 격자로 놓을 수 있습니다. 이 단원(격자로 배치: grid)을 이미 안다면 단원 연습 문제부터 풀고 틀린 레슨만 보세요. 건너뛸지 볼 때는 힌트를 펴지 않고 풉니다.

그림 준비 중

1flex는 줄마다 남는 자리를 따로 나눈다

<div class="list">
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
  <div>5</div>
  <div>6</div>
</div>
.list {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
}

.list div {
  flex: 1 1 200px;
  height: 80px;
  background-color: #d2d2d7;
}
결과 화면폭 900px
잰 값개발자 도구에서 본 값
  • .list의 너비884px
  • .list의 줄 수2
  • .list div의 너비209px
  • .list div:last-child의 너비434px

창 900px에서 목록은 body의 바깥 여백 8px 둘을 뺀 884px입니다. 상자는 저마다 나누기 전 크기 200px에서 출발해, 남는 자리를 같은 몫으로 나눠 갖습니다(flex: 1 1 200px — gap과 flex: 1). 한 줄에 넷까지는 들어가고(200 × 4 + 간격 16 × 3 = 848), 다섯째부터 다음 줄로 넘어갑니다.

그다음 flex는 남는 자리를 줄마다 그 줄의 항목끼리 나눕니다. 첫 줄은 넷이 나눠 209px, 둘째 줄은 둘이 나눠 434px입니다. 둘째 줄 상자의 경계는 위 줄의 어느 경계와도 맞지 않습니다.

2grid는 모든 줄이 같은 열 경계를 쓴다

1절 코드에서 부모 규칙의 두 줄만 바꿉니다.

<div class="list">
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
  <div>5</div>
  <div>6</div>
</div>
.list {
  display: grid;
  grid-template-columns:
    repeat(4, 1fr);
  gap: 16px;
}

.list div {
  flex: 1 1 200px;
  height: 80px;
  background-color: #d2d2d7;
}
결과 화면폭 900px
잰 값개발자 도구에서 본 값
  • .list의 열 수4
  • .list div의 너비209px
  • .list div:last-child의 너비209px

display: grid는 이 요소를 격자로 만들고, grid-template-columns⁠는 열을 정합니다. repeat(4, 1fr)은 같은 폭의 열 넷이라는 뜻입니다 — 읽는 법은 다음 레슨 열 만들기에서 봅니다.

격자는 부모가 열 경계를 먼저 긋고, 직계 자식(항목)을 HTML 차례대로 한 칸씩 채웁니다. 다섯째 상자는 둘째 줄 첫 열, 여섯째는 둘째 열로 갑니다. 줄이 바뀌어도 같은 열 경계를 쓰니 여섯 상자가 모두 209px입니다.

자식의 flex: 1 1 200px은 그대로 두었지만 쓰이지 않습니다. flex든 grid든 놓는 방식은 부모가 정하고 직계 자식에게만 닿으며, 자식은 그 방식 안의 제 몫만 정합니다(브라우저 규칙 7). grid 항목의 제 몫은 차지할 칸이고, 칸 여러 개 차지하기에서 봅니다.

flex 여러 줄grid
폭을 정하는 곳항목의 출발 크기와 그 줄의 남는 자리부모가 정한 열
줄이 바뀌면줄마다 따로 나눔모든 줄이 같은 열 경계
마지막 줄남은 항목이 줄을 다 나눠 넓어짐위 줄과 같은 폭

3목록은 grid, 한 줄 안은 flex

한 페이지에서 둘 가운데 하나만 고르지 않습니다. 놓을 항목들이 한 줄 안에서 자리를 나누면 flex, 여러 줄이 같은 열 경계를 맞춰야 하면 grid입니다.

놓을 것부모고르는 것
사이트 이름 ↔ 내비게이션(양 끝)header.site-headerflex
입력 칸 + 단추폼의 한 줄flex
카드 여섯 장ul.recipe-listgrid
카드 안 요리 이름 ↔ 배지div.recipe-card-headflex

카드 목록을 grid로 바꿔도 카드 안의 flex는 그대로입니다. 부모가 다르면 놓는 방식도 따로이기 때문입니다.

예제

목록은 grid로 세 열, 카드 안은 flex로 요리 이름과 조리 시간을 양 끝에 놓았습니다. 결과 화면을 펴기 전에, grid 항목인 요소와 flex 항목인 요소를 나눠 적어 보세요.

<ul class="recipe-list">
  <li>
    <h2>김치볶음밥</h2>
    <span>15분</span>
  </li>
  <li>
    <h2>계란말이</h2>
    <span>20분</span>
  </li>
  <li>
    <h2>된장찌개</h2>
    <span>25분</span>
  </li>
</ul>
.recipe-list {
  display: grid;
  grid-template-columns:
    repeat(3, 1fr);
  gap: 16px;
  padding: 0;
  list-style: none;
}

.recipe-list li {
  display: flex;
  justify-content:
    space-between;
  align-items: center;
}
결과 화면 보기
결과 화면폭 640px
잰 값개발자 도구에서 본 값
  • .recipe-list의 displaygrid
  • .recipe-list의 열 수3
  • .recipe-list li의 displayflex

grid 항목은 ul의 직계 자식인 li 셋입니다 — 세 열에 한 장씩 놓였습니다. flex 항목은 li마다 그 안의 h2와 span입니다 — 이름은 왼쪽 끝, 시간은 오른쪽 끝입니다. h2와 span은 목록의 칸에 들지 않습니다. ul에서 보면 손자이기 때문입니다(브라우저 규칙 7).

자주 틀리는 곳

틀린 생각: flex 여러 줄도 폭을 맞추면 격자가 된다.

상자마다 width를 같게 주고 양 끝 정렬을 하면 열이 맞을 것 같습니다.

<div class="list">
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
  <div>5</div>
  <div>6</div>
</div>
.list {
  display: flex;
  flex-wrap: wrap;
  justify-content:
    space-between;
}

.list div {
  width: 200px;
  height: 80px;
  background-color: #d2d2d7;
}
결과 화면폭 900px
잰 값개발자 도구에서 본 값
  • .list div:nth-child(2)의 왼쪽 자리236px
  • .list div:nth-child(6)의 왼쪽 자리692px

첫 줄은 넷이 고르게 벌어졌지만, 둘째 줄은 둘뿐이라 양 끝으로 갈라졌습니다. 여섯째 상자는 둘째 상자 아래(236px)가 아니라 넷째 상자 아래(692px)에 놓입니다. 남는 자리를 줄마다 따로 나누는 것은 1절과 같습니다.

맞는 규칙: flex는 줄마다 자리를 나눕니다. 여러 줄의 경계를 맞추려면 부모를 grid로 하고 열을 정합니다.

틀린 생각: 한 줄 배치에도 grid를 쓰면 된다.

grid가 더 새 기능이니, 머리글 한 줄도 grid로 놓으면 될 것 같습니다.

<header>
  <h1>레시피 노트</h1>
  <nav>
    <a href="index.html">목록</a>
  </nav>
</header>
header {
  display: grid;
}
결과 화면
잰 값개발자 도구에서 본 값
  • header의 열 수1
  • header의 줄 수2

열을 정하지 않은 격자는 한 열이라, 사이트 이름 아래 줄에 내비게이션이 놓였습니다. 한 줄로 놓으려면 열을 둘 정하고 그 폭도 골라야 합니다.

맞는 규칙: 한 줄 안에서 양 끝 · 남는 자리를 나누는 배치는 flex가 맞습니다 — display: flex와 justify-content: space-between(주축 정렬).

틀린 생각: 칸에 놓일 상자에 display: grid를 준다.

상자들이 격자 칸에 놓이길 바라니, 상자 쪽에 grid를 줄 것 같습니다(flex가 후손까지라는 생각과 같은 뿌리).

<div class="list">
  <div>1</div>
  <div>2</div>
  <div>3</div>
</div>
.list div {
  display: grid;
  grid-template-columns:
    repeat(3, 1fr);
}
결과 화면
잰 값개발자 도구에서 본 값
  • .list div의 displaygrid
  • .list의 열 수1

상자 셋은 저마다 격자가 되었지만, 저희끼리는 여전히 위에서 아래로 쌓였습니다. 상자를 칸에 놓는 것은 상자를 담은 부모의 일입니다.

맞는 규칙: grid도 나란히 놓을 항목들의 직계 부모에 줍니다(브라우저 규칙 7). 여기서는 .list입니다.

확인 문제

1. 예제 CSS에서 첫 규칙의 선택자 .recipe-list를 .recipe-list li로 바꾸고, 둘째 규칙(display: flex 규칙)은 지웠습니다. 첫 카드에서 grid 항목인 요소를 모두 고르세요. 기호를 다 쓰세요.

<ul class="recipe-list">
  <li>
    <h2>김치볶음밥</h2>
    <span>15분</span>
  </li>
  <li>
    <h2>계란말이</h2>
    <span>20분</span>
  </li>
</ul>
.recipe-list li {
  display: grid;
  grid-template-columns:
    repeat(3, 1fr);
  gap: 16px;
  padding: 0;
  list-style: none;
}
  • ㉠ ul.recipe-list
  • ㉡ 첫 li
  • ㉢ h2 김치볶음밥
  • ㉣ span 15분
  • ㉤ 없음
힌트

이제 display: grid가 걸린 요소가 어느 것인지 찾고, 그 요소의 직계 자식을 봅니다.

정답

㉢ ㉣ — 첫 카드 안의 h2와 span입니다.

결과 화면폭 640px
잰 값개발자 도구에서 본 값
  • .recipe-list의 displayblock
  • .recipe-list li의 displaygrid
  • li:first-child > *의 고른 글김치볶음밥 · 15분
  • grid는 선언이 걸린 li가 정하고, 그 직계 자식 h2와 span이 항목이 됩니다 — 카드마다 세 열 가운데 두 칸에 이름과 시간이 놓입니다(브라우저 규칙 7).
  • ㉡을 고르면 grid가 여전히 목록에 있다고 본 것입니다. 이제 ul은 격자가 아니라, 카드 둘이 위에서 아래로 쌓입니다.
  • ㉠은 격자를 정하는 쪽이 항목도 된다고 본 것입니다. 항목은 격자가 된 요소의 직계 자식입니다.

2. 사이트 이름과 내비게이션을 한 줄 양 끝에 놓으려 합니다. ____ 자리에 알맞은 값을 쓰세요.

.site-header {
  display: ____;
  justify-content:
    space-between;
}
힌트

놓을 것이 한 줄 안의 둘인지, 여러 줄이 맞춰야 할 격자인지 봅니다.

정답

flex

<header class="site-header">
  <span>레시피 노트</span>
  <nav>목록</nav>
</header>
.site-header {
  display: flex;
  justify-content:
    space-between;
}
결과 화면
잰 값개발자 도구에서 본 값
  • .site-header의 displayflex
  • .site-header의 줄 수1

한 줄 안의 둘을 양 끝에 놓는 일은 flex입니다. grid를 쓰면 열을 정하지 않아 한 열이 되고, 내비게이션이 아래 줄로 내려갑니다.

3. 플로우 카페 메뉴판을 세 열 격자로 놓았습니다. 창 폭 648px에서 둘째 줄로 넘어간 쿠키 칸은 어떻게 놓일까요?

<ul class="menu">
  <li>커피 4000원</li>
  <li>라테 4500원</li>
  <li>녹차 3500원</li>
  <li>쿠키 2000원</li>
</ul>
.menu {
  display: grid;
  grid-template-columns:
    repeat(3, 1fr);
  gap: 16px;
  padding: 0;
  list-style: none;
}
  • ① 쿠키 칸 혼자 둘째 줄을 다 차지한다
  • ② 쿠키 칸이 커피 칸 바로 아래에 같은 폭으로 놓인다
  • ③ 쿠키 칸이 둘째 줄 가운데 열에 놓인다
힌트

열을 정한 것이 어느 요소인지, 그 열을 둘째 줄도 쓰는지 봅니다.

정답

② — 쿠키 칸이 커피 칸 바로 아래에 같은 폭으로 놓입니다.

결과 화면폭 648px
잰 값개발자 도구에서 본 값
  • .menu li:first-child의 너비200px
  • .menu li:last-child의 너비200px
  • .menu li:last-child의 왼쪽 자리8px
  • grid는 부모가 그은 열 셋을 모든 줄이 씁니다. 넷째 칸 쿠키는 HTML 차례대로 둘째 줄 첫째 열에 들어가, 위 줄 칸과 같은 200px입니다(브라우저 규칙 7).
  • ①은 flex처럼 그 줄의 남는 자리를 혼자 가진다고 본 것입니다. 줄마다 따로 나누는 것은 flex입니다.
  • ③은 남은 칸이 가운데로 모인다고 본 것입니다. 격자는 첫째 열부터 차례로 채웁니다.

컴퓨터에서 해 보기(선택) — 점수에 들지 않습니다. 폰으로만 보고 있다면 건너뛰세요. hello.html의 body 안을 1절 HTML로, style 요소 안의 규칙을 1절 CSS로 바꿔 저장 · 새로고침합니다. 창 폭을 천천히 줄여 마지막 줄 상자의 폭이 어떻게 바뀌는지 본 뒤, 부모 규칙을 2절 CSS로 바꿔 같은 일을 해 봅니다. grid에서는 어느 폭에서도 마지막 줄이 위 줄과 같은 폭입니다. 개발자 도구 Elements 패널(한국어 화면에서는 '요소')에서 .list 줄 끝의 grid 표시를 누르면 열 경계선이 페이지 위에 겹쳐 보입니다.

끝까지 내려오면 저절로 표시됩니다. 이 기기에만 저장합니다.

HTML·CSS 목차로