칸을 끌어 그리는 CSS Grid 생성기
CSS Grid 생성기의 무대에 놓인 상자는 실제 display: grid 컨테이너입니다. 위와 왼쪽의 자에는 열·행 트랙마다 적은 값과 브라우저가 계산한 px, 그리고 선 번호가 붙어 있습니다. 빈 칸을 끌면 그 범위를 차지하는 영역이 생기고, 영역을 끌면 칸 단위로 옮겨지며, 고른 영역의 오른쪽 아래 모서리를 끌면 늘어나거나 줄어듭니다. 자의 칩을 누르면 그 트랙의 크기를 1fr·200px·minmax(120px, 1fr)처럼 바꿀 수 있습니다.
무대 아래 문장은 지금 1fr이 몇 px인지를 계산 순서대로 풀어서 보여 주고, 폭에 맞춰 자동으로 열을 만들 때에는 열이 몇 개 들어가는지 식으로 보여 줍니다. 완성한 배치는 grid-template-areas를 쓴 영역 이름 방식이나 선 번호 방식의 CSS로 복사하고, HTML까지 한 번에 가져갈 수도 있습니다. 링크 복사를 누르면 지금 배치가 주소에 담깁니다.
Grid가 알맞은 배치: 행과 열을 함께 맞출 때
CSS Grid는 열과 행의 경계를 먼저 정하고 항목을 그 칸에 놓는 2차원 배치입니다. 칸의 크기를 항목의 내용이 아니라 트랙이 정하므로, 여러 줄에 걸쳐 있어도 위아래 칸의 경계가 정확히 맞습니다. 다음과 같은 화면에서 특히 쓸모가 큽니다.
- 페이지 뼈대: 머리글·사이드바·본문·바닥글처럼 자리가 정해진 구역을 나눌 때 씁니다. 영역 이름으로 적어 두면 CSS만 읽어도 배치가 보입니다(예시 '기본 페이지'·'성배 레이아웃').
- 대시보드: 작은 지표 칸 여러 개와 두 칸 이상을 차지하는 큰 차트를 같은 격자에 놓을 때 씁니다(예시 '대시보드').
- 카드 목록: 화면 폭에 따라 열 수가 바뀌어도 모든 줄의 카드 폭을 똑같이 유지합니다. Flexbox로 줄을 감싸면 마지막 줄의 카드가 넓어지기 쉽지만, Grid의 카드는 트랙 폭을 따르므로 그렇지 않습니다(예시 '자동 맞춤 카드').
- 크기가 다른 사진 모음: 두 칸, 네 칸을 차지하는 사진을 섞고
dense로 빈칸을 메웁니다(예시 '사진 모자이크'). - 겹쳐 놓기: 두 영역에 같은 칸을 주면 그림 위에 글자를 올리는 배치를 위치 계산 없이 만들 수 있습니다. 무대에서 영역을 다른 영역 위로 끌어 보면 겹친 상태를 확인할 수 있습니다.
메뉴나 버튼 묶음처럼 한 방향으로 늘어놓고 그 줄 안에서만 공간을 나누는 배치에는 Flexbox가 더 단순합니다.
쓰는 법
- 예시를 고릅니다. 맨 위 타일 9개 가운데 하나를 누르면 트랙·간격·영역이 한꺼번에 바뀝니다. 바뀌는 영역은 원래 자리에서 새 자리로 미끄러져 움직입니다.
- 트랙을 정합니다. 위 줄의 열·행 단추로 트랙을 더하거나 빼고, 자의 칩을 눌러 크기를 바꿉니다. 오른쪽 조작판에서 값을 직접 쓰거나 자주 쓰는 값을 고르고, 고른 트랙의 앞뒤에 트랙을 넣을 수 있습니다.
- 영역을 그립니다. 빈 칸을 끌면 영역이 생기고, 영역을 끌면 옮겨지고, 오른쪽 아래 모서리를 끌면 크기가 바뀝니다. 키보드로는 영역에 초점을 두고 방향키로 한 칸씩 옮기고, Shift와 방향키로 늘이거나 줄이며, Delete 키로 지웁니다. 영역 이름은 조작판에서 바꿉니다.
- 폭을 확인합니다. 위 줄의 전체·768·480·360 단추나 무대 오른쪽 아래 손잡이로 컨테이너 폭을 바꾸면 fr 트랙이 어떻게 줄어드는지, 자동 열이 몇 개로 바뀌는지 바로 보입니다.
- 복사합니다. 영역 이름과 선 번호 가운데 적는 방식을 고르고 'CSS 복사' 또는 'HTML + CSS 복사'를 누릅니다. '미리 보기 꾸밈 포함'을 켜면 테두리와 배경까지 넣어, 빈 파일에 붙여 넣어도 칸이 보입니다.
트랙 값 읽는 법
grid-template-columns와 grid-template-rows에는 트랙마다 크기를 하나씩 적습니다. 같은 값이 이어지면 repeat(3, 1fr)처럼 줄여 쓸 수 있고, 이 도구도 모든 트랙이 같을 때 그렇게 적습니다.
| 값 | 크기가 정해지는 방식 | 이런 때 씁니다 |
|---|---|---|
200px | 컨테이너 폭과 상관없이 고정 | 사이드바, 아이콘 칸 |
25% | 컨테이너 안쪽 폭의 비율, 간격은 따로 더함 | 비율을 정확히 지켜야 할 때(합 100%에 간격이 있으면 넘침) |
1fr | 다른 트랙과 간격을 뺀 남는 공간의 몫 | 남는 자리를 모두 채울 때 |
auto | 내용 크기, fr 트랙이 없으면 남는 공간까지 늘어남 | 높이가 내용을 따라야 하는 머리글·바닥글 행 |
min-content | 내용이 가장 좁아질 수 있는 폭(가장 긴 낱말) | 라벨처럼 최대한 좁게 둘 칸 |
max-content | 내용을 줄바꿈 없이 한 줄로 둔 폭 | 줄바꿈하면 안 되는 짧은 항목 |
minmax(120px, 1fr) | 최소 120px부터 최대 1fr까지 | 너무 좁아지면 안 되는 칸 |
minmax(0, 1fr) | 최소 0부터 최대 1fr까지 | 긴 낱말이 있어도 열 폭을 똑같이 나눌 때 |
fit-content(240px) | 내용 크기를 따르되 240px을 넘지 않음 | 내용이 짧으면 좁게, 길면 상한까지 |
1fr은 몇 px인가: 남는 공간을 나누는 순서
fr은 고정된 길이가 아니라 남는 공간을 나누는 배수입니다. 브라우저는 다음 순서로 1fr의 크기를 정합니다.
- 안쪽 폭에서 간격(트랙 수 − 1개)과 fr이 아닌 트랙의 크기를 뺍니다. 이것이 남는 공간입니다.
- 남는 공간을 fr 배수의 합으로 나누면 1fr의 크기가 나옵니다. 배수의 합이 1보다 작으면 1로 나누므로 남는 공간 일부가 비어 남습니다.
- fr 트랙 가운데 최소 크기가 자기 몫보다 큰 트랙은 그 최소 크기로 고정하고, 나머지 fr 트랙끼리 다시 나눕니다.
1fr의 최소 크기는 auto라서 칸 안의 긴 낱말이나 그림의 폭이 최소 크기가 됩니다.
아래 표는 안쪽 폭 800px, 열 간격 16px인 컨테이너(마지막 줄만 안쪽 600px, 간격 0)에서 Chromium 151이 계산한 열 너비입니다. 무대에 같은 값을 넣으면 아래 문장에 같은 식이 나옵니다.
| grid-template-columns | 남는 공간 | 1fr | 열 너비 |
|---|---|---|---|
1fr 1fr 1fr | 768px | 256px | 256 · 256 · 256 |
200px 1fr | 584px | 584px | 200 · 584 |
1fr 2fr 1fr | 768px | 192px | 192 · 384 · 192 |
240px 1fr 2fr | 528px | 176px | 240 · 176 · 352 |
25% 1fr | 584px | 584px | 200 · 584 |
0.5fr 0.25fr | 784px | 784px(배수 합 0.75 → 1로 나눔) | 392 · 196(196px은 비움) |
minmax(240px, 1fr) 1fr 1fr | 600px → 360px | 180px | 240 · 180 · 180 |
마지막 줄은 처음에 600 ÷ 3 = 200px로 나누지만, 첫 열의 최소 크기 240px이 몫 200px보다 커서 첫 열을 240px로 고정합니다. 나머지 360px을 두 열이 180px씩 나눕니다. 2fr 열이 1fr 열의 정확히 두 배가 되는 것도 두 열 모두 몫이 최소 크기보다 클 때만입니다.
영역 이름과 선 번호
열이 n개인 격자에는 세로선이 1번부터 n + 1번까지 있습니다. 무대의 자 위에 붙은 작은 숫자가 이 선 번호입니다. grid-column: 1 / 3은 1번 선에서 3번 선까지, 곧 열 두 개를 차지한다는 뜻이고, grid-row: 2 / 4는 행 두 개를 차지합니다. 마지막 선은 -1로도 적을 수 있어 1 / -1은 열 수와 상관없이 전체 폭입니다.
grid-template-areas는 행마다 따옴표 한 쌍을 쓰고 칸마다 영역 이름을 적습니다. 빈칸은 마침표(.)로 둡니다. 예시 '기본 페이지'를 영역 이름 방식으로 적으면 다음과 같습니다.
.grid {
display: grid;
grid-template-columns: 180px 1fr;
grid-template-rows: 56px 1fr 48px;
grid-template-areas:
"header header"
"side main"
"footer footer";
gap: 12px;
}
.header { grid-area: header; }
.side { grid-area: side; }
같은 이름을 적은 칸들은 하나의 직사각형을 이뤄야 합니다. ㄱ자처럼 꺾이거나 떨어져 있으면 선언 전체가 무효가 됩니다. 그러면 grid-area: 이름을 준 항목들이 그 이름의 영역을 찾지 못해 명시 격자 바깥에 생긴 같은 칸에 모두 겹쳐 놓입니다. 문자열마다 칸 수도 같아야 합니다. 두 영역이 같은 칸을 겹쳐 쓰는 배치는 이름으로 적을 수 없으므로, 이 도구는 겹친 영역이 생기면 선 번호 방식으로 바꿔 적고 그 이유를 코드 위에 알립니다.
영역 이름 방식은 CSS만 보고도 배치를 떠올릴 수 있고, 미디어 쿼리에서 문자열만 바꿔 화면 폭별 배치를 다시 짤 수 있다는 장점이 있습니다. 선 번호 방식은 12열 그리드처럼 칸이 많거나 영역을 겹쳐야 할 때 더 짧게 적힙니다.
auto-fit과 auto-fill: 폭에 맞춰 열 수 정하기
'폭에 맞춰 자동'을 고르면 열 정의가 repeat(auto-fill, minmax(min(100%, 160px), 1fr)) 꼴이 됩니다. 브라우저는 넘치지 않는 범위에서 가장 많은 열을 만들며, 그 수는 ⌊(안쪽 폭 + 간격) ÷ (최소 폭 + 간격)⌋입니다. min(100%, 160px)은 부모가 160px보다 좁을 때 최소 폭을 부모 폭으로 낮춰 열 하나가 옆으로 넘치지 않게 합니다.
auto-fit과 auto-fill은 영역 수가 열 수보다 적을 때만 다르게 보입니다. auto-fill은 빈 열을 자리로 남겨 두고, auto-fit은 빈 열의 크기를 0으로 접고 그 양옆 간격도 함께 접어서 남은 영역이 넓어집니다. 최소 폭 160px, 간격 16px에 영역 3개를 둔 결과는 다음과 같습니다.
| 안쪽 폭 | 열 수 | auto-fill 칸 폭 | auto-fit 칸 폭 |
|---|---|---|---|
| 360px | 2 | 172px | 172px |
| 600px | 3 | 189.3px | 189.3px |
| 800px | 4 | 188px | 256px |
| 1000px | 5 | 187.2px | 322.7px |
360px에서는 열이 2개라 세 번째 영역이 다음 줄로 내려가고, 600px에서는 영역 수와 열 수가 같아 두 방식이 같습니다. 카드가 한두 장일 때 지나치게 넓어지는 것이 싫다면 auto-fill을, 있는 카드로 폭을 꽉 채우고 싶다면 auto-fit을 고릅니다.
명시한 격자 밖: 암시적 행과 dense
grid-template-rows로 정한 행보다 영역이 많으면 브라우저가 행을 더 만듭니다. 이렇게 생긴 행을 암시적 행이라 하고, 그 크기는 grid-auto-rows가 정합니다(기본값 auto). 무대에서는 암시적 트랙의 칩과 칸을 점선으로 그립니다. 카드 목록은 행을 아예 정하지 않고 grid-auto-rows만 주는 경우가 많습니다.
위치를 정하지 않은 영역은 HTML 순서대로 들어갈 칸을 찾습니다. 두 칸짜리 영역이 줄 끝에 들어가지 못하면 다음 줄로 넘어가면서 앞에 빈칸을 남깁니다. grid-auto-flow: dense를 주면 뒤에 오는 작은 영역이 앞의 빈칸을 메웁니다. 예시 '사진 모자이크'에서 빈칸 메우기를 끄면 1행 4열이 비고, 켜면 p5가 그 자리로 올라갑니다. 다만 dense는 화면에 보이는 순서만 바꾸고, Tab 키 이동과 화면 낭독 순서는 HTML 순서 그대로입니다.
칸 안에서의 정렬
영역은 기본값 stretch로 칸을 가득 채웁니다. justify-items는 칸 안에서 가로 위치를, align-items는 세로 위치를 정하며, start·center·end로 두면 영역이 내용 크기로 줄어 그 자리에 놓입니다. 둘 다 center로 두는 것은 place-items: center 한 줄과 같고, 예시 '가운데 정렬'이 이 배치입니다. 영역 하나만 따로 정렬하려면 그 영역에 justify-self·align-self를 줍니다.
트랙 크기의 합이 컨테이너보다 작을 때 트랙 묶음 전체는 시작 쪽(왼쪽 위)에 붙습니다. 무대 아래 문장이 '오른쪽이 비어 있습니다'라고 알리는 경우인데, 묶음을 가운데로 옮기려면 컨테이너에 justify-content: center를 더합니다.
브라우저 지원
아래는 MDN 브라우저 호환성 자료에 적힌 첫 지원 버전입니다. 이 도구가 만드는 코드는 subgrid를 쓰지 않습니다.
| 기능 | Chrome | Firefox | Safari |
|---|---|---|---|
display: grid, grid-template-columns | 57 | 52 | 10.1 |
grid-template-areas | 57 | 52 | 10.1 |
gap(그리드) | 66 | 61 | 12 |
min() 함수 | 79 | 75 | 11.1 |
| subgrid(참고) | 117 | 71 | 16 |
자주 틀리는 경우
- 1fr 열의 폭이 서로 다릅니다. 한 칸에 긴 URL이나 큰 그림이 있어 최소 크기가 몫보다 큰 경우입니다.
minmax(0, 1fr)로 바꾸고 넘치는 글자는 따로 줄바꿈합니다. - 25% 네 개가 넘칩니다. % 트랙에는 간격이 포함되지 않습니다. 간격 16px이면 48px이 넘치므로
repeat(4, 1fr)을 씁니다. - 1fr 행이 늘어나지 않습니다. 컨테이너 높이가 auto이면 나눌 남는 높이가 없습니다.
height나min-height: 100dvh처럼 높이를 줍니다. - grid-template-areas가 통째로 무시됩니다. 문자열마다 칸 수가 다르거나, 같은 이름이 직사각형을 이루지 않는 경우입니다.
- 좁은 화면에서 카드가 옆으로 넘칩니다.
minmax(240px, 1fr)은 부모가 240px보다 좁아도 240px을 지킵니다.minmax(min(100%, 240px), 1fr)로 바꿉니다. - 열이 하나 더 생겨 가로 스크롤이 납니다. span이 열 수보다 크면 암시적 열이 생깁니다. 자동 열에서 좁은 폭일 때 특히 잘 생기므로 360px 단추로 확인합니다.
- dense로 채웠더니 읽는 순서가 어긋납니다. 화면 순서만 바뀐 것입니다. 순서가 중요한 목록이면 dense를 끄거나 HTML 순서를 고칩니다.
한계와 주의
- 무대는 왼쪽에서 오른쪽으로 가로쓰기 하는 방향만 보여 줍니다.
- 무대의 영역에는 이름 글자만 들어 있고, 글자가 트랙을 넓히지 않도록
min-width: 0과overflow: hidden을 줘 두었습니다. 배치 속성만 담은 기본 코드에는 이 두 줄이 없어서, 같은 이름 글자라도 최소 크기가 auto인 1fr 열은 글자 폭만큼 넓어질 수 있습니다. '미리 보기 꾸밈 포함'을 켜면 이 두 줄과 무대의 안쪽 여백·테두리·글자 크기가 함께 들어가 무대와 같은 크기로 그려집니다. 실제 내용이 크면 auto·min-content·fr 트랙의 크기는 무대와 달라집니다. - 명시 트랙은 열과 행 각각 12개, 영역은 24개까지 만듭니다. 이름 있는 선, 음수 선 번호, subgrid,
grid-auto-columns,justify-content·align-content, 영역별justify-self·align-self, 미디어 쿼리는 코드에 넣지 않습니다. - 높이 칸의 값은 안쪽 여백을 뺀 내용 높이입니다. 코드의
height도 같은 뜻(content-box)으로 적습니다. - 768·480·360 단추는 컨테이너의 폭을 바꿉니다. 화면(뷰포트) 폭을 기준으로 하는 미디어 쿼리와는 다릅니다.
- fr 풀이 문장은 브라우저가 그린 트랙 크기로 계산을 다시 해 봅니다. 0.6px 넘게 어긋나면 그 식이 어림값이라고 알립니다.
- 링크 복사는 영역 이름까지 주소에 담습니다.
기준일 W3C CSS Grid Layout Module Level 2(Candidate Recommendation Draft 2025-03-26)와 MDN 브라우저 호환성 자료는 2026-10-21에 원문으로 확인했습니다. 표의 열 너비는 Chromium 151에서 실제 grid 컨테이너로 잰 값입니다.
출처
- 01W3C: CSS Grid Layout Module Level 2Candidate Recommendation Draft 2025-03-26. §6.6 그리드 항목의 자동 최소 크기, §7.2.1 트랙 크기(minmax() 밖의 fr은 최소 auto), §7.2.3.2 auto-fill·auto-fit 반복 수와 빈 트랙 접기, §7.3 grid-template-areas(직사각형이 아니면 선언 무효), §7.5 암시적 격자와 grid-auto-rows, §7.7 grid-auto-flow의 dense, §11.1 간격(접힌 트랙의 간격), §12.7.1 fr 크기 찾기(배수 합 1 미만, 최소 크기가 큰 트랙 제외 후 다시 계산)
- 02MDN: grid-template-columns트랙 값 문법, 브라우저 호환성 표: Chrome 57·Firefox 52·Safari 10.1, subgrid Chrome 117·Firefox 71·Safari 16
- 03MDN: grid-template-areas영역 이름 문법, 브라우저 호환성 표: Chrome 57·Firefox 52·Safari 10.1
- 04MDN: gap브라우저 호환성 표: 그리드 레이아웃에서 Chrome 66·Firefox 61·Safari 12
- 05MDN: min()브라우저 호환성 표: Chrome 79·Firefox 75·Safari 11.1
자주 묻는 질문
CSS Grid로 같은 폭의 3열을 만들려면 어떻게 하나요?
컨테이너에 display: grid와 grid-template-columns: repeat(3, 1fr)을 줍니다. 안쪽 폭 800px, 열 간격 16px이면 간격 32px을 뺀 768px을 셋으로 나눠 각 열이 256px이 됩니다. 칸 안에 긴 URL이나 그림이 있어도 폭을 똑같이 지키려면 repeat(3, minmax(0, 1fr))로 적습니다.
fr과 %는 무엇이 다른가요?
%는 컨테이너 안쪽 폭에 대한 비율이라 간격이 따로 더해지고, fr은 간격과 다른 트랙을 먼저 뺀 남는 공간을 나눕니다. 간격 16px에서 25%를 네 번 쓰면 48px이 넘치지만 1fr 네 개는 넘치지 않습니다.
auto-fit과 auto-fill은 무엇이 다른가요?
둘 다 넘치지 않는 범위에서 가장 많은 열을 만들지만, 영역 수가 열 수보다 적을 때 auto-fill은 빈 열을 자리로 남기고 auto-fit은 빈 열을 0으로 접어 남은 영역을 넓힙니다. 안쪽 800px, 최소 160px, 간격 16px에 영역 3개를 두면 auto-fill은 188px, auto-fit은 256px입니다.
grid-template-areas를 적었는데 적용되지 않아요.
문자열마다 칸 수가 같은지, 같은 이름을 적은 칸들이 하나의 직사각형을 이루는지 확인합니다. 둘 중 하나라도 어기면 선언 전체가 무효가 됩니다. 항목에는 grid-area: 이름을 따로 주어야 하고, 이름에는 따옴표를 쓰지 않습니다.
화면 폭에 따라 열 수가 바뀌게 하려면 어떻게 하나요?
grid-template-columns: repeat(auto-fit, minmax(min(100%, 160px), 1fr))처럼 적으면 미디어 쿼리 없이도 폭에 맞춰 열 수가 바뀝니다. 열 수는 (안쪽 폭 + 간격) ÷ (최소 폭 + 간격)을 내림한 값입니다. 이 도구에서 '폭에 맞춰 자동'을 고르고 손잡이로 폭을 바꾸면 열이 바뀌는 지점을 볼 수 있습니다.
Grid 안의 항목을 가운데에 놓으려면 어떻게 하나요?
컨테이너에 place-items: center를 주거나 justify-items와 align-items를 모두 center로 둡니다. 세로로 가운데에 오려면 칸에 남는 높이가 있어야 하므로 행이나 컨테이너의 높이도 정합니다. 예시 '가운데 정렬'을 누르면 바로 볼 수 있습니다.
1fr로 정한 행이 늘어나지 않아요.
컨테이너 높이가 auto이면 나눌 남는 높이가 없어서 fr 행이 내용 높이만큼만 커집니다. height나 min-height: 100dvh처럼 컨테이너 높이를 정하면 1fr 행이 남는 높이를 가져갑니다.
Grid와 Flexbox 중 무엇을 써야 하나요?
행과 열의 경계를 함께 맞춰야 하는 페이지 뼈대·대시보드·카드 목록에는 Grid가, 한 방향으로 늘어놓고 그 줄 안에서 공간을 나누는 메뉴·버튼 묶음에는 Flexbox가 알맞습니다. 두 방식은 함께 쓰는 경우가 많아, Grid로 구역을 나누고 각 구역 안의 버튼 줄은 Flexbox로 정렬합니다.