안녕하세요! 프론트엔드 개발자 양성 과정 강사입니다. MDN 공식 문서를 통해 CSS Grid를 깊이 있게 공부하시려는 모습이 정말 멋집니다! 현업 프론트엔드 개발자가 되기 위해 꼭 필요한 훌륭한 학습 방법이에요.
요청하신 대로 요약 없이 원본의 모든 내용을 구어체로 친절하게 번역해 드리고, 제 실무 경험을 바탕으로 이해를 돕기 위한 보충 설명과 팁들도 듬뿍 담아보았습니다. 그럼 바로 시작해 볼까요?
그리드 레이아웃의 기본 개념 가이드에서 우리는 라인 번호(Line numbers)를 사용하여 그리드 상에 아이템을 배치하는 방법을 아주 잠깐 살펴보았어요. 이번 가이드에서는 이 명세서의 가장 핵심적인 기능이 정확히 어떻게 작동하는지 완전히 파헤쳐 보겠습니다.
숫자로 매겨진 라인과 함께 그리드 탐험을 시작하는 것이 가장 논리적인 접근 방법이에요. 왜냐하면 여러분이 그리드 레이아웃을 사용할 때면 언제나 이 번호가 매겨진 라인들이 존재하기 때문이죠. 이 라인들은 열(Columns)과 행(Rows)을 위해 번호가 매겨지며, 인덱스는 1번부터 시작합니다.
여기서 주의할 점은, 그리드의 인덱스는 문서의 '쓰기 모드(writing mode)'에 따라 방향이 결정된다는 거예요. 영어 텍스트처럼 왼쪽에서 오른쪽으로 쓰는 언어(Left-to-Right)에서는 1번 라인이 그리드의 맨 왼쪽에 위치합니다. 반면에 아랍어처럼 오른쪽에서 왼쪽으로 쓰는 언어(Right-to-Left)에서 작업한다면, 1번 라인은 그리드의 맨 오른쪽에 위치하게 됩니다. 쓰기 모드와 그리드 간의 상호 작용에 대해서는 그리드, 논리적 값, 그리고 쓰기 모드 가이드에서 더 자세히 배워볼 거예요.
💡 강사의 팁:
처음 그리드를 접하실 때 0이 아닌 1부터 시작한다는 점을 꼭 기억해 주세요! 자바스크립트 배열은 0부터 시작하지만 CSS 그리드 라인은 1부터 시작합니다. 이것을 머릿속에 시각화해 두시면 앞으로의 과정이 훨씬 편해집니다.
가장 기본적인 예제로, 우리는 3개의 열 트랙(column tracks)과 3개의 행 트랙(row tracks)을 가진 그리드를 만들어 볼 거예요. 이렇게 하면 가로와 세로 각 방향(dimension)으로 총 4개의 라인이 생기게 됩니다.
* {
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);
grid-template-rows: repeat(3, 100px);
}
이 그리드 컨테이너 안에는 4개의 자식 요소들을 포함시켜 보겠습니다.
<div class="wrapper">
<div class="box1">One</div>
<div class="box2">Two</div>
<div class="box3">Three</div>
<div class="box4">Four</div>
</div>
만약 우리가 이 아이템들을 그리드 상에 어떤 방식으로든 직접 배치하지 않는다면, 아이템들은 자동 배치 규칙(auto-placement rules)에 따라 첫 번째 행의 네 개의 셀에 각각 하나씩 차례대로 배치될 거예요. 브라우저의 개발자 도구를 사용하여 이 그리드를 검사해 보시면, 그리드가 열과 행을 어떻게 정의하고 있는지 직접 눈으로 확인할 수 있답니다.

💡 강사의 팁:
현업에서 그리드로 레이아웃을 짤 때 크롬이나 파이어폭스의 개발자 도구(F12)는 필수입니다! 요소 탭에서grid배지(badge)를 클릭하면 화면에 실제 그리드 라인 번호가 오버레이되어 나타납니다. 코드를 작성하면서 항상 화면의 라인 번호를 켜두고 작업하는 습관을 들이세요.
우리는 라인 기반 배치(line-based placement)를 사용하여 이 아이템들이 그리드 위 어디에 위치할지 제어할 수 있어요. grid-column-start와 grid-column-end 속성을 사용하면 첫 번째 아이템이 그리드의 맨 왼쪽에서 시작해서 하나의 열 트랙을 차지하도록 만들 수 있습니다. 그리고 grid-row-start와 grid-row-end를 사용하면, 그 아이템이 그리드 맨 위쪽의 첫 번째 행 라인에서 시작해서 네 번째 행 라인까지 이어지도록 만들 수 있죠.
.box1 {
grid-column-start: 1;
grid-column-end: 2;
grid-row-start: 1;
grid-row-end: 4;
}
여러분이 몇몇 아이템들을 직접 배치하더라도, 그리드 상의 나머지 아이템들은 계속해서 자동 배치 규칙(auto-placement rules)을 사용하여 레이아웃이 잡히게 됩니다. 이러한 동작 방식은 그리드 레이아웃의 자동 배치 가이드에 잘 설명되어 있어요. 일단 지금은, 그리드가 어떻게 아직 배치되지 않은 아이템들을 빈 셀에 알아서 척척 배치하는지 관찰해 보세요.
이제 각 아이템을 개별적으로 선택해서 동일한 속성들을 사용하되 각기 다른 값들을 주어 보겠습니다. 행과 열 트랙을 넘나들며 네 개의 아이템을 모두 꼼꼼하게 배치해 볼까요?
<div class="wrapper">
<div class="box1">One</div>
<div class="box2">Two</div>
<div class="box3">Three</div>
<div class="box4">Four</div>
</div>
.box1 {
grid-column-start: 1;
grid-column-end: 2;
grid-row-start: 1;
grid-row-end: 4;
}
.box2 {
grid-column-start: 3;
grid-column-end: 4;
grid-row-start: 1;
grid-row-end: 3;
}
.box3 {
grid-column-start: 2;
grid-column-end: 3;
grid-row-start: 1;
grid-row-end: 2;
}
.box4 {
grid-column-start: 2;
grid-column-end: 4;
grid-row-start: 3;
grid-row-end: 4;
}
우리가 원한다면 셀을 빈 공간으로 남겨둘 수도 있다는 점을 눈여겨보세요. 그리드 레이아웃이 가진 정말 멋진 장점 중 하나는, 이상한 편법(hacks)을 쓰지 않고도 디자인 상에 여백(white space)을 아주 쉽게 만들어낼 수 있다는 것입니다.
💡 강사의 팁:
과거에 Float나 Flexbox만으로 레이아웃을 잡을 때는 의도적으로 텅 빈 공간을 만들기 위해 투명한 더미 요소를 억지로 넣거나 복잡한 마진 계산을 해야 했어요. 하지만 Grid에서는 그럴 필요가 전혀 없습니다! 특정 영역에 요소를 배치하지 않으면 그곳은 자연스럽게 여백으로 남게 되거든요. 유지보수 측면에서 정말 혁명적인 기능이죠.
grid-column 과 grid-row 단축 속성 (The grid-column and grid-row shorthands)앞선 예제에서는 각각의 아이템을 배치하기 위해 꽤 많은 양의 코드를 작성해야 했어요. 눈치채셨겠지만, 이를 줄여주는 단축 속성(shorthand)이 당연히 존재합니다.
grid-column-start와 grid-column-end 속성은 grid-column 하나로 결합할 수 있고, grid-row-start와 grid-row-end는 grid-row 하나로 결합할 수 있습니다. 슬래시(/)를 기준으로 앞은 시작 라인, 뒤는 끝 라인을 의미하죠. 이번 예제에서는 이 단축 속성들을 사용하여 방금 위에서 만든 레이아웃을 똑같이 다시 만들어 볼게요.
<div class="wrapper">
<div class="box1">One</div>
<div class="box2">Two</div>
<div class="box3">Three</div>
<div class="box4">Four</div>
</div>
.box1 {
grid-column: 1 / 2;
grid-row: 1 / 4;
}
.box2 {
grid-column: 3 / 4;
grid-row: 1 / 3;
}
.box3 {
grid-column: 2 / 3;
grid-row: 1 / 2;
}
.box4 {
grid-column: 2 / 4;
grid-row: 3 / 4;
}
위의 예제들에서는 각 속성들이 어떻게 동작하는지 확실하게 보여드리기 위해 시작과 끝에 해당하는 모든 행 및 열 라인을 일일이 명시했어요. 하지만 실제로 작업하실 때, 어떤 아이템이 단 하나의 트랙만 차지한다면(즉, 1칸만 차지한다면) grid-column-end나 grid-row-end 값을 과감히 생략하셔도 됩니다. 그리드는 기본적으로 하나의 트랙을 차지(span)하도록 되어있기 때문이죠.
이 말은 즉, 우리가 처음에 길게 풀어서 썼던(long-hand) 예제를 다음과 같이 훨씬 간결하게 작성할 수 있다는 뜻이에요.
<div class="wrapper">
<div class="box1">One</div>
<div class="box2">Two</div>
<div class="box3">Three</div>
<div class="box4">Four</div>
</div>
.box1 {
grid-column-start: 1;
grid-row-start: 1;
grid-row-end: 4; /* 이건 3칸을 차지하니까 명시해줘야 해요! */
}
.box2 {
grid-column-start: 3;
grid-row-start: 1;
grid-row-end: 3; /* 이건 2칸을 차지하니까 명시해줘야 해요! */
}
.box3 {
grid-column-start: 2;
grid-row-start: 1;
/* 1칸만 차지하므로 끝 라인 속성 생략 가능 */
}
.box4 {
grid-column-start: 2;
grid-column-end: 4; /* 2칸을 차지하니까 명시 */
grid-row-start: 3;
/* 1칸만 차지하므로 끝 라인 속성 생략 가능 */
}
이를 단축 속성(shorthand)에 적용해보면 아래 코드처럼 작성할 수 있습니다. 단 1개의 트랙만 차지하는 아이템들의 경우, 슬래시(/)와 두 번째 값을 아예 적지 않아도 완벽하게 동작합니다.
<div class="wrapper">
<div class="box1">One</div>
<div class="box2">Two</div>
<div class="box3">Three</div>
<div class="box4">Four</div>
</div>
.box1 {
grid-column: 1; /* 1 / 2 대신 1만 작성! */
grid-row: 1 / 4;
}
.box2 {
grid-column: 3; /* 3 / 4 대신 3만 작성! */
grid-row: 1 / 3;
}
.box3 {
grid-column: 2;
grid-row: 1;
}
.box4 {
grid-column: 2 / 4;
grid-row: 3;
}
💡 강사의 팁:
코드가 훨씬 깔끔해졌죠? 실무에서도 불필요하게 코드를 길게 쓰는 것보다 이렇게 생략 가능한 기본값은 생략하는 편이 가독성에 훨씬 좋습니다.
grid-area 속성 (The grid-area property)여기서 한 걸음 더 나아가 볼까요? 단 하나의 속성인 grid-area를 사용하면 전체 영역을 한 번에 정의할 수 있습니다. grid-area에 들어가는 값의 순서는 다음과 같아요. 이 순서가 매우 중요합니다!
– grid-row-start
– grid-column-start
– grid-row-end
– grid-column-end
<div class="wrapper">
<div class="box1">One</div>
<div class="box2">Two</div>
<div class="box3">Three</div>
<div class="box4">Four</div>
</div>
.box1 {
grid-area: 1 / 1 / 4 / 2;
}
.box2 {
grid-area: 1 / 3 / 3 / 4;
}
.box3 {
grid-area: 1 / 2 / 2 / 3;
}
.box4 {
grid-area: 3 / 2 / 4 / 4;
}
이 grid-area 속성에 값을 넣는 순서가 어쩌면 좀 낯설게 느껴지실 수도 있어요. 예를 들어 우리가 흔히 마진(margin)이나 패딩(padding)의 단축 속성에서 사용하는 방향(위-오른쪽-아래-왼쪽, Top-Right-Bottom-Left)과는 정반대인 것처럼 보이니까요.
하지만 CSS 그리드 레이아웃이 CSS 쓰기 모드(CSS writing modes)에 정의된 흐름 상대적(flow-relative) 방향을 사용한다는 사실을 알면 이해가 조금 더 쉬워집니다. 쓰기 모드 환경에서 그리드가 어떻게 동작하는지는 그리드, 논리적 값, 그리고 쓰기 모드 가이드에서 다룰 예정이에요. 지금은 일단 4가지의 흐름 상대적(flow-relative) 방향 개념이 있다는 것만 생각해 보세요.
block-start (블록 시작)block-end (블록 끝)inline-start (인라인 시작)inline-end (인라인 끝)우리는 왼쪽에서 오른쪽으로 읽는 영어(또는 한국어) 환경에서 작업하고 있습니다. 이 경우 우리의 block-start는 그리드 컨테이너의 맨 위 행 라인이 되고, block-end는 컨테이너의 맨 마지막 행 라인이 됩니다. 그리고 inline-start는 맨 왼쪽 열 라인이 되죠. (현재 쓰기 모드에서 텍스트가 작성되기 시작하는 기준점이 항상 inline-start가 되기 때문이에요.) 반대로 inline-end는 우리 그리드의 맨 마지막 열 라인이 됩니다.
결국 우리가 grid-area 속성을 사용해 그리드 영역을 지정할 때, 먼저 두 개의 시작 라인(block-start와 inline-start)을 차례대로 정의하고, 그다음에 두 개의 끝 라인(block-end와 inline-end)을 정의하는 구조인 거예요. top, right, bottom, left와 같은 물리적 속성(physical properties)에 익숙한 우리에게는 처음엔 어색할 수 있지만, 웹사이트가 다양한 쓰기 모드에 따라 다방향(multi-directional)으로 동작해야 한다는 점을 생각하기 시작하면 훨씬 더 말이 되는 구조랍니다.
💡 강사의 팁:
처음grid-area를 쓰면 순서가 정말 헷갈립니다. 가장 쉽게 외우는 팁을 드릴게요! "상-좌-하-우" 순서입니다.
1. 위쪽 줄(row-start)
2. 왼쪽 줄(column-start)
3. 아래쪽 줄(row-end)
4. 오른쪽 줄(column-end)시작점(상, 좌)의 좌표를 먼저 찍고, 그 다음 끝점(하, 우)의 좌표를 찍어서 네모 박스를 그린다~ 라고 상상하시면 절대 안 까먹으실 거예요!
영어처럼 왼쪽에서 오른쪽으로 읽는 환경의 경우, 그리드의 맨 오른쪽 열 라인이나 맨 아래쪽 행 라인에서부터 거꾸로 역추적하여 숫자를 셀 수도 있습니다! 명시적 그리드(explicit grid)의 맨 마지막 라인은 -1로 나타낼 수 있고, 거기서부터 거꾸로 세어 나갈 수 있어요. 즉, 뒤에서 두 번째 라인은 -2가 되는 식이죠.
여기서 꼭 주의하셔야 할 점은, 이 음수 값들은 오직 '명시적 그리드(explicit grid)'에만 적용된다는 것입니다. 즉, 여기서 말하는 마지막 라인이란 grid-template-columns와 grid-template-rows에 의해 우리가 직접 정의한 그리드의 마지막 라인을 뜻해요. 그 범위를 벗어나서 암시적 그리드(implicit grid)에 의해 자동으로 추가된 행이나 열은 계산에 포함되지 않는다는 점을 꼭 기억하세요!
아래의 다음 예제를 보시면, 아이템들을 배치할 때 그리드의 오른쪽과 아래쪽에서부터 거꾸로 라인 번호를 사용하여 기존에 작업하던 레이아웃을 뒤집어서 만들어 보았습니다.
<div class="wrapper">
<div class="box1">One</div>
<div class="box2">Two</div>
<div class="box3">Three</div>
<div class="box4">Four</div>
</div>
.box1 {
grid-column-start: -1;
grid-column-end: -2;
grid-row-start: -1;
grid-row-end: -4;
}
.box2 {
grid-column-start: -3;
grid-column-end: -4;
grid-row-start: -1;
grid-row-end: -3;
}
.box3 {
grid-column-start: -2;
grid-column-end: -3;
grid-row-start: -1;
grid-row-end: -2;
}
.box4 {
grid-column-start: -2;
grid-column-end: -4;
grid-row-start: -3;
grid-row-end: -4;
}
그리드의 시작 라인과 끝 라인을 이렇게 양방향에서 지정할 수 있다는 것은 무척 유용합니다. 왜냐하면 다음과 같이 코드를 작성하면 아이템을 그리드 전체를 쫙 가로지르도록 한 번에 늘릴 수 있기 때문이에요.
.item {
grid-column: 1 / -1;
}
💡 강사의 팁:
이1 / -1테크닉은 프론트엔드 실무에서 정말 밥 먹듯이 사용합니다! 화면의 윗부분을 꽉 채우는 내비게이션 바(GNB)나 사이트 맨 아래 푸터(Footer)를 만들 때 "시작부터 끝까지 전부 다 차지해라!"라는 의미로 이보다 완벽한 코드가 없거든요. 아주아주 중요한 꿀팁이니 메모해 두세요!
CSS 그리드에는 열 트랙과 행 트랙 사이에 여백(gutter)을 추가할 수 있는 기능이 내장되어 있습니다. column-gap과 row-gap 속성, 또는 단축 속성인 gap을 사용하면 됩니다.
이 간격(Gap)은 그리드의 트랙들 사이에만 나타날 뿐, 컨테이너의 맨 위나 아래, 왼쪽이나 오른쪽 바깥 가장자리에는 여백을 추가하지 않아요. (바깥 여백은 컨테이너의 padding으로 해결해야 합니다.) 위에서 만들었던 예제의 그리드 컨테이너에 이 속성들을 추가해서 간격을 한 번 넣어볼까요?
<div class="wrapper">
<div class="box1">One</div>
<div class="box2">Two</div>
<div class="box3">Three</div>
<div class="box4">Four</div>
</div>
.box1 {
grid-column: 1;
grid-row: 1 / 4;
}
.box2 {
grid-column: 3;
grid-row: 1 / 3;
}
.box3 {
grid-column: 2;
grid-row: 1;
}
.box4 {
grid-column: 2 / 4;
grid-row: 3;
}
.wrapper {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(3, 100px);
column-gap: 20px; /* 열 간격 */
row-gap: 1em; /* 행 간격 */
}
gap 단축 속성 (The gap shorthand)이 두 가지 속성은 하나의 단축 속성인 gap으로도 표현할 수 있습니다. 만약 gap에 값을 딱 하나만 주면, 그 값이 행 간격과 열 간격 모두에 똑같이 적용됩니다. 하지만 만약 값을 두 개 주게 되면, 첫 번째 값은 row-gap으로 사용되고 두 번째 값은 column-gap으로 사용됩니다.
.wrapper {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(3, 100px);
gap: 1em 20px; /* row-gap: 1em, column-gap: 20px */
}
라인을 기반으로 아이템을 배치하는 관점에서 볼 때, 이 간격(gap)은 마치 해당 라인이 원래보다 더 두꺼워진 것처럼 동작합니다. 특정 라인에서 시작하는 아이템은 무조건 간격이 끝난 다음부터 시작하게 되며, 여러분이 임의로 이 간격 안에 무언가를 배치하거나 간격 자체를 지정할 수는 없어요. 만약 일반적인 트랙처럼 컨트롤할 수 있는 공간이 필요하다면, 간격(gap)을 쓰지 말고 빈 트랙을 하나 별도로 정의해서 사용하는 것이 좋습니다.
💡 강사의 팁:
gap속성은 예전에grid-gap이라는 이름으로 쓰였지만, Flexbox에서도 동일하게 쓸 수 있도록 이름이gap으로 통일되었습니다.margin을 써서 컴포넌트 간격을 띄우다 보면 맨 마지막 요소의 여백을 빼기 위해:last-child { margin: 0; }같은 번거로운 코드를 썼어야 했죠? 이제는 부모 컨테이너에gap만 주면 그런 고생을 싹 덜 수 있답니다. 정말 편해요!
span 키워드 사용하기 (Using the span keyword)시작 라인과 끝 라인을 번호로 직접 명시하는 방법 외에도, 시작 라인을 지정한 뒤에 span 키워드를 사용하여 해당 영역이 몇 개의 트랙을 뻗어나가며 차지할지 그 개수를 지정할 수도 있습니다.
<div class="wrapper">
<div class="box1">One</div>
<div class="box2">Two</div>
<div class="box3">Three</div>
<div class="box4">Four</div>
</div>
.box1 {
grid-column: 1;
grid-row: 1 / span 3; /* 1번 라인에서 시작해서 3칸을 차지하라! */
}
.box2 {
grid-column: 3;
grid-row: 1 / span 2; /* 1번 라인에서 시작해서 2칸을 차지하라! */
}
.box3 {
grid-column: 2;
grid-row: 1;
}
.box4 {
grid-column: 2 / span 2; /* 2번 라인에서 시작해서 2칸을 차지하라! */
grid-row: 3;
}
이 span 키워드는 grid-row-start나 grid-row-end, 그리고 grid-column-start나 grid-column-end의 값으로 모두 사용할 수 있어요. 아래의 두 예제는 결과적으로 완전히 똑같은 그리드 영역을 만들어냅니다.
첫 번째 예제에서는 시작 행 라인(start row line)을 설정하고, 끝 라인(end line) 부분에 3개의 트랙을 차지하도록(span 3) 지정했습니다. 영역은 1번 라인에서 시작해서 3개 트랙만큼 뻗어나가게 되므로, 최종적으로 4번 라인에서 끝나게 됩니다.
.box1 {
grid-column-start: 1;
grid-row-start: 1;
grid-row-end: span 3;
}
두 번째 예제에서는 아이템이 끝날 행 라인(end row line)을 먼저 4로 명시하고, 시작 라인(start row line) 부분에 span 3을 지정했습니다. 이 말은 아이템이 끝 라인에서 위로 거슬러 올라가며 3개의 트랙을 차지해야 한다는 의미가 됩니다. 따라서 이 영역은 4번 라인에서 끝나며, 위로 3개의 라인을 거슬러 올라가 1번 라인에서 시작하게 되는 것이죠.
.box1 {
grid-column-start: 1;
grid-row-start: span 3;
grid-row-end: 4;
}
💡 강사의 팁:
"몇 번부터 몇 번까지"가 직관적일 땐 라인 번호를 쓰고, "여기서부터 몇 칸짜리"가 더 직관적일 때는span을 씁니다. 실무에서는 화면 구성이 바뀌어 전체 라인 수가 변경될 때 덜 취약한span을 애용하는 개발자들이 아주 많습니다.
그리드에서 라인을 기반으로 배치하는 방식에 익숙해지려면, 다양한 개수의 열을 가진 그리드를 만들고 거기에 아이템들을 직접 이리저리 배치해보면서 흔히 쓰이는 레이아웃들을 직접 만들어 보시는 것을 강력히 추천합니다!
명시적으로 배치하지 않은 나머지 아이템들은 '자동 배치 규칙'에 의해 빈 공간에 알아서 들어간다는 점 잊지 마시고요. 이게 여러분이 의도한 결과라면 다행이지만, 만약 뭔가 엉뚱한 곳에 아이템이 나타났다면 해당 아이템의 위치를 여러분이 정확히 지정했는지 한 번 더 확인해 보시기 바랍니다.
또한, 여러분이 아이템을 이렇게 명시적으로 배치하다 보면 아이템끼리 서로 겹치게(overlap) 만들 수도 있다는 점을 기억하세요. 의도적으로 아이템을 겹치게 배치하면 꽤 멋진 시각적 효과를 만들어낼 수도 있습니다. 하지만 반대로, 시작이나 끝 라인 번호를 잘못 지정하는 바람에 의도치 않게 레이아웃이 깨지고 겹쳐버릴 수도 있겠죠. 그럴 때는 브라우저 개발자 도구를 열어서 그리드를 직접 뜯어보는 것이 원인을 찾는 가장 빠르고 훌륭한 방법입니다. 특히 그리드가 꽤 복잡해질수록 더더욱 개발자 도구와 친해지셔야 해요!
MDN 문서 개선에 참여하기 (Help improve MDN)
강사의 입장에서 풀어본 번역이 CSS Grid 학습에 도움이 되셨기를 바랍니다. 계속해서 멋진 프론트엔드 개발자로 성장하시길 응원할게요! 추가로 궁금한 점이 있으시다면 언제든 질문해주세요!