안녕하세요! 프론트엔드 강사입니다. MDN 문서를 통해 CSS Grid를 공부하고 계시는군요! 정말 좋은 학습 방법이에요. 공식 문서를 읽는 습관은 탄탄한 실력을 쌓는 데 아주 중요하답니다. 문서 내용이 영어라 조금 막막하셨을 텐데, 제가 딱딱하지 않게, 현업에서 쓰는 방식과 꿀팁들을 듬뿍 담아서 번역해 드릴게요! 자, 그럼 시작해 볼까요?
CSS 그리드 레이아웃(CSS grid layout)에는 우리가 그리드를 만들고 자식 아이템들의 일부나 전체를 직접(명시적으로) 배치하지 않았을 때, 브라우저가 알아서 아이템들을 어떻게 놔둘지 결정하는 규칙들이 담겨있어요. 우리가 아이템 하나하나의 위치를 깐깐하게 정해줄 필요가 없을 때, 이 "자동 배치(auto-placement)" 기능은 여러 아이템을 촤르륵 예쁘게 배열하는 가장 쉽고 빠른 방법이랍니다.
👨🏫 강사의 팁: > 처음 Grid를 배우면
grid-column이나grid-row로 아이템 자리를 일일이 지정해 주는 것에 익숙해지곤 해요. 하지만 실무에서는 게시판 목록이나 사진 첩처럼 데이터의 개수가 10개, 100개로 계속 변하는 경우가 훨씬 많잖아요? 이럴 때마다 위치를 다 적어줄 순 없으니, 이 '자동 배치' 알고리즘을 이해하고 뼈대만 잘 잡아두는 것이 정말 정말 중요합니다!
만약 여러분이 아이템들에게 어디에 위치하라는 정보를 주지 않으면, 아이템들은 그리드 위에 스스로 알아서 자리를 잡아요. 기본적으로는 하나의 그리드 셀(칸)마다 하나의 그리드 아이템이 들어가게 됩니다.
body {
font: 1.2em sans-serif;
}
* {
box-sizing: border-box;
}
.wrapper {
border: 2px solid #f76707;
border-radius: 5px;
background-color: #fff4e6;
}
.wrapper > div {
border: 2px solid #ffa94d;
border-radius: 5px;
background-color: #ffd8a8;
padding: 1em;
color: #d9480f;
}
.wrapper {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
}
<div class="wrapper">
<div>One</div>
<div>Two</div>
<div>Three</div>
<div>Four</div>
<div>Five</div>
</div>
위의 예제에서 보신 것처럼, 특별한 위치 지정 없이 그리드만 만들어 두면 자식 아이템들은 소스 코드에 적힌 순서대로 그리드의 빈 셀에 하나씩 쏙쏙 들어갑니다. 기본 흐름(flow)은 아이템들을 행(row), 즉 가로 방향으로 채워나가는 거예요. 그리드는 첫 번째 행의 셀들을 먼저 채우고 공간이 모자라면 다음 행으로 넘어갑니다.
만약 여러분이 grid-template-rows 속성을 사용해서 행(가로줄)들을 미리 넉넉하게 만들어 두었다면, 그리드는 그 행들을 따라 계속해서 아이템을 채울 거예요. 그런데 모든 아이템을 담기에 명시적 그리드(explicit grid)의 행 개수가 부족하다면 어떨까요? 걱정 마세요. 브라우저가 알아서 새로운 암시적(implicit) 행들을 만들어냅니다.
💡 보충 설명:
여기서 명시적(Explicit) 그리드와 암시적(Implicit) 그리드의 개념을 확실히 잡고 가야 해요!
- 명시적 그리드: 우리가 CSS 파일에
grid-template-columns나grid-template-rows로 "나는 세 칸짜리, 높이 100px짜리 그리드를 쓸 거야!"라고 직접 선언해 둔 공간입니다.- 암시적 그리드: 데이터가 너무 많아서 우리가 직접 만들어둔 명시적 공간을 뚫고 밖으로 삐져나갈 때, 브라우저가 "앗, 공간이 부족하네? 내가 임시로 방을 더 만들어 줄게!" 하고 자동으로 확장해서 만들어낸 공간을 말합니다.
암시적 그리드에서 브라우저가 알아서 뚝딱 만들어낸 행들은 기본적으로 자동 크기(auto-sized) 로 설정돼요. 이 말은 내용물이 들어왔을 때 글씨가 밖으로 삐져나가지(overflow) 않도록, 내용물 크기에 딱 맞게 행의 높이가 알아서 쫙 늘어난다는 뜻이에요.
물론 이 암시적 행들의 크기도 우리가 마음대로 조절할 수 있습니다! 바로 grid-auto-rows라는 속성을 쓰면 되는데요. 예를 들어 브라우저가 알아서 만드는 모든 행의 높이를 똑같이 100px로 고정하고 싶다면 grid-auto-rows: 100px;이라고 적어주면 됩니다.
body {
font: 1.2em sans-serif;
}
* {
box-sizing: border-box;
}
.wrapper {
border: 2px solid #f76707;
border-radius: 5px;
background-color: #fff4e6;
}
.wrapper > div {
border: 2px solid #ffa94d;
border-radius: 5px;
background-color: #ffd8a8;
padding: 1em;
color: #d9480f;
}
<div class="wrapper">
<div>One</div>
<div>Two</div>
<div>Three</div>
<div>Four</div>
<div>Five</div>
</div>
.wrapper {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
grid-auto-rows: 100px;
}
minmax() 함수를 grid-auto-rows 값으로 쓰면 정말 유연한 레이아웃을 만들 수 있어요. 이 함수를 쓰면 행의 최소 크기를 보장하면서도, 안쪽 내용이 많아지면 내용물에 맞춰 행이 자연스럽게 늘어나게(grow) 할 수 있죠.
예를 들어 grid-auto-rows: minmax(100px, auto);라고 쓰면, "일단 모든 행은 최소 100px 높이로 만들어 줘. 그런데 글씨가 너무 많아서 100px을 넘어가면? 글씨가 짤리지 않게 필요한 만큼 높이를 쭉쭉 늘려줘!"라는 뜻이 됩니다.
body {
font: 1.2em sans-serif;
}
* {
box-sizing: border-box;
}
.wrapper {
border: 2px solid #f76707;
border-radius: 5px;
background-color: #fff4e6;
}
.wrapper > div {
border: 2px solid #ffa94d;
border-radius: 5px;
background-color: #ffd8a8;
padding: 1em;
color: #d9480f;
}
<div class="wrapper">
<div>One</div>
<div>Two</div>
<div>Three</div>
<div>
Four <br />This cell <br />Has extra <br />content. <br />Max is auto
<br />so the row expands.
</div>
<div>Five</div>
</div>
.wrapper {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
grid-auto-rows: minmax(100px, auto);
}
👨🏫 강사의 팁: > 이
minmax()기법은 현업에서 카드 컴포넌트를 만들 때 진짜 많이 쓰이는 단골손님입니다! 텍스트가 짧을 때는 카드가 너무 납작해지면 안 예쁘니까 최소 높이(예:200px)를 잡아주고, 반대로 사용자가 게시글을 엄청 길게 썼을 때 카드가 글씨를 먹어버리면(잘리면) 안 되니까 최대 크기를auto로 열어두는 거죠. 완벽한 카드 리스트를 만드는 치트키예요.
꼭 값을 하나만 넣을 필요는 없어요. 트랙 크기 리스트(track listing)를 여러 개 넘겨줄 수도 있거든요. 이렇게 하면 지정한 패턴이 반복해서 적용됩니다.
예를 들어 grid-auto-rows: 100px 200px; 이라고 적어볼게요. 그러면 브라우저가 처음 만들어내는 암시적 행의 높이는 100px이 되고, 두 번째 만들어내는 행은 200px이 돼요. 콘텐츠가 계속 더 많아져서 세 번째 행을 만들면 다시 100px, 네 번째는 200px… 이렇게 100px, 200px 패턴이 번갈아 가면서 계속 적용된답니다.
body {
font: 1.2em sans-serif;
}
* {
box-sizing: border-box;
}
.wrapper {
border: 2px solid #f76707;
border-radius: 5px;
background-color: #fff4e6;
}
.wrapper > div {
border: 2px solid #ffa94d;
border-radius: 5px;
background-color: #ffd8a8;
padding: 1em;
color: #d9480f;
}
<div class="wrapper">
<div>One</div>
<div>Two</div>
<div>Three</div>
<div>Four</div>
<div>Five</div>
<div>Six</div>
<div>Seven</div>
<div>Eight</div>
</div>
.wrapper {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
grid-auto-rows: 100px 200px;
}
아이템들을 가로(행)가 아니라 세로(열) 기준으로 차곡차곡 배치하라고 그리드에게 명령할 수도 있어요. 이때 사용하는 것이 바로 grid-auto-flow 속성이고, 값으로 column을 주시면 됩니다.
이 설정을 하면, 그리드는 여러분이 grid-template-rows로 미리 만들어둔 가로 칸들을 따라서 세로 방향(↓)으로 먼저 쭈욱 채워 내려가요. 세로로 한 줄이 꽉 차면 다음 열로 이동하거나, 아니면 암시적 그리드에 새로운 세로 열을 하나 만들어냅니다.
암시적 행을 만들 때와 똑같이, 이렇게 만들어진 암시적 열 트랙들도 기본적으로 내용물에 맞춰 크기가 조절(auto sized)돼요. 만약 이 암시적 열들의 너비를 내 마음대로 통제하고 싶다면 grid-auto-columns 속성을 쓰면 됩니다. 아까 위에서 배운 grid-auto-rows와 완전히 똑같은 방식으로 작동한답니다!
아래 예제를 볼까요? 높이가 200px인 행 트랙 3개가 있는 그리드예요. 세로 방향으로 아이템을 먼저 채우라고 grid-auto-flow: column;을 선언했습니다. 그리고 grid-auto-columns: 300px 100px;을 설정했죠. 그러면 아이템이 모두 담길 때까지 새롭게 생겨나는 세로 열들은 너비가 300px이었다가 100px이었다가 하면서 번갈아 나타나게 됩니다.
.wrapper {
display: grid;
grid-template-rows: repeat(3, 200px);
gap: 10px;
grid-auto-flow: column;
grid-auto-columns: 300px 100px;
}
body {
font: 1.2em sans-serif;
}
* {
box-sizing: border-box;
}
.wrapper {
border: 2px solid #f76707;
border-radius: 5px;
background-color: #fff4e6;
}
.wrapper > div {
border: 2px solid #ffa94d;
border-radius: 5px;
background-color: #ffd8a8;
padding: 1em;
color: #d9480f;
}
<div class="wrapper">
<div>One</div>
<div>Two</div>
<div>Three</div>
<div>Four</div>
<div>Five</div>
<div>Six</div>
<div>Seven</div>
<div>Eight</div>
</div>
그리드 안에서는 내가 직접 위치를 콕 집어준 아이템과 알아서 흘러가는 아이템들이 사이좋게 섞여 있을 수 있어요. 어떤 아이템은 특정 자리에 못 박아두고, 나머지 애들은 빈 공간에 알아서 들어가게 두는 거죠.
만약 HTML에 적힌 마크업 순서(DOM order)가 실제 화면에 보여주고 싶은 순서와 잘 맞아떨어진다면, CSS에서 모든 아이템의 자리를 하나하나 짚어줄 필요가 전혀 없습니다. CSS 명세서(스펙 문서)를 보면 그리드 아이템 배치 알고리즘(Grid item placement algorithm)에 대해 설명하는 어마무시하게 긴 내용이 있지만, 걱정 마세요! 우리 같은 프론트엔드 개발자들은 딱 몇 가지 간단한 핵심 규칙만 기억하면 충분하거든요.
아무 위치 지정도 받지 않은 불쌍한(?) 아이템들은 그리드 스펙상 "order modified document order(order 속성이 반영된 문서 순서)"라는 방식으로 자리를 찾아갑니다. 말이 좀 어렵죠? 쉽게 말해, 여러분이 CSS의 order 속성을 하나라도 건드렸다면, 아이템들은 HTML 문서에 적힌 원래 순서가 아니라 그 order 값의 순서대로 정렬되어 배치된다는 뜻이에요. 만약 order 속성을 아예 쓰지 않았다면, 당연히 기본값으로 HTML 문서 소스에 작성된 순서 그대로 차례차례 들어갑니다.
👨🏫 강사의 팁: > Flexbox에서
order: 1,order: 2쓰면서 시각적인 순서를 바꿨던 거 기억나시죠? Grid에서도 완전히 똑같이 작동합니다. 다만 주의할 점! 시각적인 눈속임일 뿐이라, 스크린 리더(시각장애인용 화면 낭독기)는 여전히 원래 HTML 태그 순서대로 읽습니다. 그러니 논리적 순서가 아예 꼬이게order를 너무 남용하는 건 접근성 측면에서 좋지 않아요.
자동 배치 알고리즘에서 가장 중요한 첫 번째 원칙은, "자리가 지정된 VIP 아이템들을 먼저 앉힌다"는 겁니다.
아래 예제를 볼게요. 총 12개의 아이템이 있습니다. 그중 2번 아이템과 5번 아이템은 라인 번호를 기준으로 직접 자리를 지정해 줬어요. 이렇게 자리가 정해진 녀석들이 먼저 쏙 들어가고 나면, 나머지 위치를 모르는 아이템들은 빈 공간을 찾아 차례차례 알아서 들어가는 걸 보실 수 있습니다.
참고로 빈칸을 찾아 들어가는 애들은 자기보다 HTML 순서상 뒤에 있는 위치 지정 아이템이 있더라도 신경 쓰지 않아요. 그 아이템 뒤로 넘어가지 않고, 그냥 앞에서부터 빈 공간이 있으면 쏙쏙 들어간답니다.
body {
font: 1.2em sans-serif;
}
* {
box-sizing: border-box;
}
.wrapper {
border: 2px solid #f76707;
border-radius: 5px;
background-color: #fff4e6;
}
.wrapper > div {
border: 2px solid #ffa94d;
border-radius: 5px;
background-color: #ffd8a8;
padding: 1em;
color: #d9480f;
}
<div class="wrapper">
<div>One</div>
<div>Two</div>
<div>Three</div>
<div>Four</div>
<div>Five</div>
<div>Six</div>
<div>Seven</div>
<div>Eight</div>
<div>Nine</div>
<div>Ten</div>
<div>Eleven</div>
<div>Twelve</div>
</div>
.wrapper {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-auto-rows: 100px;
gap: 10px;
}
.wrapper div:nth-child(2) {
grid-column: 3;
grid-row: 2 / 4;
}
.wrapper div:nth-child(5) {
grid-column: 1 / 3;
grid-row: 1 / 3;
}
여러분은 위치 지정 속성을 쓰면서도 자동 배치의 편안함을 동시에 누릴 수 있어요. 다음 예제에서는 약간의 트릭을 써서 1번, 5번, 9번(즉 4n+1 번째) 아이템들이 가로 세로로 2칸씩(span) 크게 차지하도록 만들어 볼 거예요.
이걸 하려면 grid-column-end와 grid-row-end 속성을 사용하고 값을 span 2라고 주면 됩니다. 이 코드가 마법 같은 역할을 하는데요. 아이템이 "어디서 시작할지(start)"는 브라우저의 자동 배치에 전적으로 맡겨두고, "어디서 끝날지(end)"만 지금 시작한 곳에서부터 무조건 2칸을 덮으라고 지시하는 거예요!
이렇게 하면 결과물에 군데군데 뻥 뚫린 빈칸(gap)들이 생긴 걸 보실 수 있을 거예요. 왜냐하면 자동 배치되는 아이템들이 자리를 찾다가, "앗, 나는 2칸이나 필요한데 지금 이 줄에는 자리가 모자라네?" 싶으면, 남은 자리를 쿨하게 포기하고 다음 가로줄(행)로 훌쩍 넘어가서 들어갈 수 있는 넓은 자리를 찾기 때문이랍니다.
body {
font: 1.2em sans-serif;
}
* {
box-sizing: border-box;
}
.wrapper {
border: 2px solid #f76707;
border-radius: 5px;
background-color: #fff4e6;
}
.wrapper > div {
border: 2px solid #ffa94d;
border-radius: 5px;
background-color: #ffd8a8;
padding: 1em;
color: #d9480f;
}
<div class="wrapper">
<div>One</div>
<div>Two</div>
<div>Three</div>
<div>Four</div>
<div>Five</div>
<div>Six</div>
<div>Seven</div>
<div>Eight</div>
<div>Nine</div>
<div>Ten</div>
<div>Eleven</div>
<div>Twelve</div>
</div>
.wrapper {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-auto-rows: 100px;
gap: 10px;
}
.wrapper div:nth-child(4n + 1) {
grid-column-end: span 2;
grid-row-end: span 2;
background-color: #ffa94d;
}
.wrapper div:nth-child(2) {
grid-column: 3;
grid-row: 2 / 4;
}
.wrapper div:nth-child(5) {
grid-column: 1 / 3;
grid-row: 1 / 3;
}
자, 지금까지 본 예제들에서는 우리가 명시적으로 자리를 찍어준 아이템들 빼고는 그리드가 항상 꿋꿋하게 앞만 보고 전진하면서 DOM 순서를 칼같이 지켰어요. 회원가입 폼(form) 같은 걸 만들 때는 이름 입력칸 밑에 나이 입력칸이 와야지, 빈칸 채우겠다고 순서가 엉망진창 섞이면 큰일 나겠죠? 그래서 보통은 이게 맞습니다. 하지만 간혹 사진 첩처럼 보여주는 순서가 딱히 중요하지 않을 때는, 이빨 빠진 것처럼 듬성듬성한 구멍들을 꽉꽉 예쁘게 채우고 싶을 때가 생겨요.
이럴 땐 당황하지 말고 그리드 부모 컨테이너에 grid-auto-flow 속성을 추가하고 값을 dense(빽빽하게)로 줘보세요! 아까 아이템들을 세로로 흐르게 할 때 column이라고 줬던 그 속성이에요. 만약 세로(열) 기준으로 흐르면서 꽉꽉 채우기까지 하고 싶다면 grid-auto-flow: column dense처럼 합쳐서 쓰면 된답니다.
이렇게 세팅하고 나면, 그리드가 빈 구멍들을 찾아서 알아서 메꾸기 시작합니다! 아이템들을 순서대로 배치하다가 빈칸이 생기면 예전처럼 그냥 넘어가지만, 그 이후에 나타나는 작은 아이템들 중에서 예전 빈칸에 딱 들어맞는 녀석을 발견하면 DOM 순서를 무시하고 그 녀석을 끌고 와서 빈틈에 쏙 박아버리는 거죠.
물론 Grid의 다른 재배치 기능들과 마찬가지로, 눈에 보이는 배치 순서가 바뀌었다고 해서 실제 마크업의 논리적 순서가 바뀌는 건 아니에요. 예를 들어 키보드 Tab 키를 눌러 이동할 때는 눈에 보이는 곳으로 점프하는 게 아니라 여전히 원래 HTML 문서 순서대로 똑바로 이동합니다. 이런 시각적 순서와 논리적 순서의 불일치로 생길 수 있는 접근성 문제들은 그리드 레이아웃과 접근성 가이드(Grid layout and accessibility guide)에서 더 깊게 다루겠지만, 화면 상의 배치와 실제 문서 순서가 달라질 때는 이 점을 항상 유의하셔야 해요!
👨🏫 강사의 팁: > 핀터레스트 스타일(Masonry 레이아웃)처럼 빈 공간 없이 다닥다닥 붙어있는 레이아웃을 만들 때
dense옵션이 정말 효자 노릇을 합니다. 복잡한 자바스크립트 계산 없이 CSS 한 줄로 예쁜 갤러리가 완성되니까요.
body {
font: 1.2em sans-serif;
}
* {
box-sizing: border-box;
}
.wrapper {
border: 2px solid #f76707;
border-radius: 5px;
background-color: #fff4e6;
}
.wrapper > div {
border: 2px solid #ffa94d;
border-radius: 5px;
background-color: #ffd8a8;
padding: 1em;
color: #d9480f;
}
<div class="wrapper">
<div>One</div>
<div>Two</div>
<div>Three</div>
<div>Four</div>
<div>Five</div>
<div>Six</div>
<div>Seven</div>
<div>Eight</div>
<div>Nine</div>
<div>Ten</div>
<div>Eleven</div>
<div>Twelve</div>
</div>
.wrapper div:nth-child(4n + 1) {
grid-column-end: span 2;
grid-row-end: span 2;
background-color: #ffa94d;
}
.wrapper div:nth-child(2) {
grid-column: 3;
grid-row: 2 / 4;
}
.wrapper div:nth-child(5) {
grid-column: 1 / 3;
grid-row: 1 / 3;
}
.wrapper {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-auto-rows: 100px;
gap: 10px;
grid-auto-flow: dense;
}
CSS 스펙을 읽다 보면 '익명 그리드 아이템(anonymous grid items)'이라는 녀석에 대한 언급이 나와요. 이게 뭐냐면, 그리드 부모 컨테이너 안에 어떤 div나 span 같은 태그로도 감싸여 있지 않은 덩그러니 놓여있는 텍스트(문자열) 덩어리를 말합니다.
아래 예제를 볼까요? grid라는 클래스가 있는 부모 요소에 display: grid를 줬다고 칠게요. 아이템이 3개처럼 보입니다. 첫 번째는 그냥 텍스트 덩어리인데 아무 태그도 없으니 이게 바로 '익명 아이템'이 됩니다. 이 녀석은 클래스나 ID가 없어서 우리가 CSS로 직접 타겟팅을 할 수가 없어요. 그래서 무조건 자동 배치 규칙에 따라 알아서 배치됩니다. 밑에 있는 나머지 두 개는 div 태그로 잘 감싸진 아이템이니 자동 배치되게 놔둘 수도 있고, 우리가 선택자를 써서 원하는 위치로 보낼 수도 있죠.
<div class="grid">
I am a string and will become an anonymous item
<div>A grid item</div>
<div>A grid item</div>
</div>
익명 아이템은 콕 집어서 조종할 방법이 없기 때문에 항상 자동 배치를 탈 수밖에 없어요. 그러니 실수로 그리드 태그 안에 태그로 감싸지 않은 글씨를 남겨두면, 이 글씨가 그리드의 자동 배치 규칙을 타고 아주 뚱딴지같은 곳에 튀어나와서 레이아웃을 망가뜨릴 수도 있다는 걸 잊지 마세요!
👨🏫 강사의 팁: > React나 Vue 같은 최신 프레임워크를 쓸 때
&&같은 조건부 렌더링을 하다 보면, 조건이 안 맞을 때 렌더링 된 빈 텍스트 노드나 문자열이 그리드 안에 남아버려서 뼈대가 꼬이는 경우가 종종 발생해요. "내 그리드 한 칸이 왜 엉뚱하게 비어있지?" 싶을 땐 개발자 도구를 켜서 혹시 이 '익명 그리드 아이템'이 몰래 한 자리를 차지하고 있진 않은지 꼭 확인해 보세요!
앞서 말씀드렸듯이 자동 배치는 수많은 아이템 목록을 쫙 뿌릴 때 정말 진가를 발휘합니다. 사진 갤러리나 쇼핑몰 상품 목록처럼 논리적인 순서가 굳이 필요 없는 리스트 말이에요. 그럴 땐 구멍을 꽉꽉 채워주는 dense 모드를 써주면 아주 깔끔해집니다.
제 이미지 갤러리 예제에는 가로로 긴 사진(landscape)과 세로로 긴 사진이 섞여 있어요. 가로로 긴 사진에는 landscape라는 클래스를 주고 열을 두 칸(span 2) 차지하라고 명령을 내려놨죠. 그러고 나서 grid-auto-flow: dense 한 방을 쏴주면 촘촘하게 엮인 예쁜 그리드가 짠 하고 나타납니다.
만약 코드를 테스트해 보실 수 있다면, grid-auto-flow: dense를 지워보세요. 꽉 차 있던 레이아웃이 풀리면서 여기저기 구멍 난 빈 공간이 생기는 걸 눈으로 확인하실 수 있을 거예요.
<ul class="wrapper">
<li>
<img
alt="A colorful hot air balloon against a clear sky"
src="[https://mdn.github.io/shared-assets/images/examples/balloon.jpg](https://mdn.github.io/shared-assets/images/examples/balloon.jpg)" />
</li>
<li class="landscape">
<img
alt="Three hot air balloons against a clear sky, as seen from the ground"
src="[https://mdn.github.io/shared-assets/images/examples/balloons-small.jpg](https://mdn.github.io/shared-assets/images/examples/balloons-small.jpg)" />
</li>
<li class="landscape">
<img
alt="Three hot air balloons against a clear sky, as seen from the ground"
src="[https://mdn.github.io/shared-assets/images/examples/balloons-small.jpg](https://mdn.github.io/shared-assets/images/examples/balloons-small.jpg)" />
</li>
<li class="landscape">
<img
alt="Three hot air balloons against a clear sky, as seen from the ground"
src="[https://mdn.github.io/shared-assets/images/examples/balloons-small.jpg](https://mdn.github.io/shared-assets/images/examples/balloons-small.jpg)" />
</li>
<li>
<img
alt="A colorful hot air balloon against a clear sky"
src="[https://mdn.github.io/shared-assets/images/examples/balloon.jpg](https://mdn.github.io/shared-assets/images/examples/balloon.jpg)" />
</li>
<li>
<img
alt="A colorful hot air balloon against a clear sky"
src="[https://mdn.github.io/shared-assets/images/examples/balloon.jpg](https://mdn.github.io/shared-assets/images/examples/balloon.jpg)" />
</li>
</ul>
body {
font: 1.2em sans-serif;
}
* {
box-sizing: border-box;
}
.wrapper {
list-style: none;
margin: 1em auto;
padding: 0;
max-width: 800px;
}
.wrapper li {
border: 1px solid #cccccc;
}
.wrapper li img {
display: block;
object-fit: cover;
width: 100%;
height: 100%;
}
.wrapper {
display: grid;
grid-template-columns: repeat(3, minmax(120px, 1fr));
gap: 10px;
grid-auto-flow: dense;
}
.wrapper li.landscape {
grid-column-end: span 2;
}
그리고 또 하나 재밌는 사실! 자동 배치는 무조건 뒤죽박죽 목록에만 쓰는 건 아니에요. 오히려 논리적 순서가 확고한 인터페이스를 짤 때도 엄청난 도움을 줍니다. 바로 다음 예제인 '정의 목록(dl)' 같은 경우인데요.
정의 목록(dl, dt, dd)은 구조가 완전히 납작해요(flat). dt(용어)와 dd(설명)들을 그룹으로 예쁘게 묶어주는 부모 태그가 없거든요. 그래서 예전엔 스타일 입히기가 진짜 까다로웠습니다. 하지만 Grid가 출동하면 어떨까요? 저는 이 예제에서 전반적인 흐름은 자동 배치에 맡겼습니다. 대신 클래스(요소) 선택자를 이용해서 "dt 너는 무슨 일이 있어도 첫 번째 열(column 1)에서 시작하고, dd 너는 무조건 두 번째 열(column 2)에서 시작해!"라고 규칙을 하나 박아두었어요.
이렇게만 해두면 용어나 설명이 몇 개가 추가되든 간에, 언제나 왼쪽 열에는 용어가, 오른쪽 열에는 설명이 짝을 지어 깔끔하게 떨어지는 마법을 볼 수 있습니다.
👨🏫 강사의 팁: > 이건 프론트엔드 실무에서 "표(table)"는 쓰기 싫은데, 표처럼 딱딱 맞는 디자인을 해야 할 때 (예: 제품 스펙 요약표, 정보 레이블 등) 정말 유용하게 쓰는 필살기예요. Flexbox로 하려면 너비 계산하고 줄바꿈 처리하느라 머리가 아픈데, Grid를 쓰면 코드가 이렇게나 우아해진답니다!
body {
font: 1.2em sans-serif;
}
* {
box-sizing: border-box;
}
.wrapper {
border: 2px solid #f76707;
border-radius: 5px;
background-color: #fff4e6;
}
<div class="wrapper">
<dl>
<dt>Mammals</dt>
<dd>Cat</dd>
<dd>Dog</dd>
<dd>Mouse</dd>
<dt>Birds</dt>
<dd>Pied Wagtail</dd>
<dd>Owl</dd>
<dt>Fish</dt>
<dd>Guppy</dd>
</dl>
</div>
dl {
display: grid;
grid-template-columns: auto 1fr;
max-width: 300px;
margin: 1em;
line-height: 1.4;
}
dt {
grid-column: 1;
font-weight: bold;
}
dd {
grid-column: 2;
}
완벽해 보이는 Grid지만, 사람들이 자주 물어보는 한계점들도 아직 몇 가지 존재해요. 현재로서는 "아이템들을 한 칸씩 퐁당퐁당 건너뛰면서 체크무늬처럼 배치해 줘" 같은 복잡한 설정은 불가능합니다.
그리고 만약 이전 가이드 문서에서 '이름을 지정한 그리드 라인(named lines)'을 배우셨다면 "이 기능도 되면 좋을 텐데!" 하는 아쉬움이 떠오르셨을지도 몰라요. 바로 "이 아이템들을 'n'이라고 이름 붙여둔 다음 라인에 차례대로 자동 배치해 줘"라고 규칙을 줘서, 그리드가 중간에 있는 다른 이름의 라인들은 쓱쓱 무시하고 건너뛰게 만드는 기능입니다. 이 기능과 관련해서 CSSWG(CSS 워킹 그룹) GitHub에 제기된 이슈가 이미 올라와 있어요. 여러분도 이 기능이 왜 필요한지 본인만의 사례(use case)를 이 이슈에 코멘트로 달아보시는 것도 환영입니다!
그리드 레이아웃의 자동 배치든, 다른 어떤 기능이든 코딩을 하다 보면 "이런 기능 하나만 더 있었으면 좋겠는데?" 하고 여러분만의 창의적인 아이디어가 떠오를 수 있어요. 그럴 땐 주저하지 말고 새로운 이슈를 올리거나 기존 이슈에 여러분의 사례를 보태보세요. 여러분의 이런 참여 하나하나가 모여서 미래의 CSS 명세서(스펙)를 훨씬 더 강력하고 편하게 만들어 줄 테니까요!
어떠셨나요? MDN 문서가 좀 더 편안하게 다가오셨기를 바랍니다. CSS Grid는 한 번 손에 익으면 정말 엄청난 생산성을 가져다주는 훌륭한 툴이에요. 앞으로도 파이팅입니다! 추가로 헷갈리는 부분이 있다면 언제든 강사에게 물어보세요!