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-header | flex |
| 입력 칸 + 단추 | 폼의 한 줄 | flex |
| 카드 여섯 장 | ul.recipe-list | grid |
| 카드 안 요리 이름 ↔ 배지 | div.recipe-card-head | flex |
카드 목록을 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의 열 수1header의 줄 수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김치볶음밥 - ㉣
span15분 - ㉤ 없음
힌트
이제 display: grid가 걸린 요소가 어느 것인지 찾고, 그 요소의 직계 자식을 봅니다.
정답
㉢ ㉣ — 첫 카드 안의 h2와 span입니다.
640px.recipe-list의displayblock.recipe-list li의displaygridli: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 표시를 누르면 열 경계선이 페이지 위에 겹쳐 보입니다.