목록을 정리할 때 Grid가 편한 이유
웹페이지에는 비슷한 모양의 콘텐츠가 반복되는 영역이 많습니다. 쇼핑몰의 상품 목록, 블로그 글 목록, 포트폴리오 이미지 갤러리, 팀원 소개 카드가 그렇습니다.
이런 화면은 보통 한 줄에 여러 개의 카드를 놓고, 다음 줄로 자연스럽게 이어지게 만듭니다. 이때 CSS Grid를 사용하면 코드를 길게 쓰지 않고도 깔끔한 격자 구조를 만들 수 있습니다.
Grid는 이름 그대로 화면을 격자판처럼 나누는 배치 방식입니다. 바둑판이나 달력처럼 가로줄과 세로줄이 있다고 생각하면 됩니다. 각 카드가 그 칸 안에 하나씩 들어갑니다.
이전 장에서 배운 Flexbox가 "한 줄 안에서 요소를 어떻게 정렬할까?"에 강하다면, Grid는 "여러 줄과 여러 열로 이루어진 목록을 어떻게 배치할까?"에 강합니다.
간단히 구분하면 이렇습니다.
메뉴, 버튼 묶음, 배너 안의 문구와 버튼 정렬 → Flexbox
상품 카드 목록, 블로그 글 목록, 이미지 갤러리 → Grid
물론 실제 작업에서는 둘을 함께 쓰는 경우도 많습니다. 예를 들어 상품 목록 전체는 Grid로 배치하고, 각 상품 카드 안의 가격과 버튼은 Flexbox로 정렬할 수 있습니다.
AI 생성 이미지
Grid는 부모에게 적용한다
Flexbox와 마찬가지로 Grid도 정렬하고 싶은 요소 하나하나에 적용하지 않습니다. 카드들을 감싸는 부모 요소에 적용합니다.
예를 들어 상품 카드가 여러 개 있을 때 구조는 보통 이렇게 생겼습니다.
<div class='product-grid'>
<div class='product-card'>상품 1</div>
<div class='product-card'>상품 2</div>
<div class='product-card'>상품 3</div>
<div class='product-card'>상품 4</div>
</div>여기서 실제로 Grid를 적용할 대상은 .product-card가 아니라, 카드들을 감싸는 .product-grid입니다.
.product-grid {
display: grid;
}display: grid;는 "이 안의 자식 요소들을 격자로 배치하겠다"는 뜻입니다. 아직은 큰 변화가 없어 보일 수 있습니다. 몇 열로 나눌지 정하지 않았기 때문입니다.
Grid는 보통 다음 순서로 작성합니다.
부모에
display: grid;를 쓴다.grid-template-columns로 열의 개수를 정한다.gap으로 카드 사이 간격을 정한다.
이 세 가지만 알아도 대부분의 카드 목록을 만들 수 있습니다.
grid-template-columns로 열 만들기
grid-template-columns는 Grid에서 세로 열을 어떻게 나눌지 정하는 속성입니다. 이름이 길지만 의미는 단순합니다.
grid→ 격자template→ 틀columns→ 열
즉, "격자의 열 모양을 정한다"는 뜻입니다.
상품 카드를 2열로 배치하고 싶다면 이렇게 씁니다.
.product-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
}여기서 1fr이라는 새 단위가 나왔습니다. fr은 Grid에서 사용하는 비율 단위입니다. 쉽게 말해 "남은 공간을 몇 조각으로 나눌까?"를 뜻합니다.
1fr 1fr은 가로 공간을 똑같이 2조각으로 나눈다는 의미입니다. 그래서 카드가 2열로 배치됩니다.
3열로 만들고 싶다면 이렇게 바꿉니다.
.product-grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 20px;
}1fr을 세 번 썼으므로 가로 공간이 3등분됩니다.
카드가 6개 있다면 3개는 첫 줄에, 나머지 3개는 다음 줄에 놓입니다. 카드가 8개라면 첫 줄 3개, 둘째 줄 3개, 셋째 줄 2개처럼 자연스럽게 이어집니다.
gap으로 간격을 한 번에 조정하기
카드 목록을 만들 때 자주 생기는 문제가 있습니다. 카드 사이 간격이 들쭉날쭉해지는 것입니다.
예전 방식으로는 각 카드에 margin을 주어 간격을 만들기도 했습니다. 하지만 카드가 여러 줄로 늘어나면 위아래 간격과 좌우 간격을 맞추기 번거롭습니다.
Grid에서는 부모에 gap을 쓰면 됩니다.
.product-grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 24px;
}gap: 24px;은 카드 사이의 가로 간격과 세로 간격을 모두 24px로 맞춥니다. 카드마다 따로 여백을 줄 필요가 없습니다.
가로와 세로 간격을 다르게 주고 싶다면 값을 두 개 쓸 수 있습니다.
.product-grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 32px 20px;
}이 경우 첫 번째 값 32px은 위아래 간격, 두 번째 값 20px은 좌우 간격입니다.
처음에는 값을 하나만 쓰는 방식부터 익히는 것이 좋습니다. 대부분의 카드 목록에서는 gap: 20px; 또는 gap: 24px; 정도만으로도 충분히 정돈됩니다.
repeat으로 같은 값을 짧게 쓰기
3열 목록을 만들 때 1fr 1fr 1fr처럼 같은 값을 반복해서 쓰면 조금 길어 보입니다. 이럴 때 repeat()을 사용할 수 있습니다.
.product-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 24px;
}repeat(3, 1fr)은 "1fr을 3번 반복한다"는 뜻입니다. 결과는 1fr 1fr 1fr과 같습니다.
2열이면 이렇게 씁니다.
.product-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 24px;
}4열이면 숫자만 바꾸면 됩니다.
.product-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 24px;
}비개발자가 템플릿을 수정할 때도 이 부분은 자주 보게 됩니다. repeat(3, 1fr)을 repeat(4, 1fr)로 바꾸면 한 줄에 보이는 카드 수가 3개에서 4개로 늘어납니다.
다만 열이 많아질수록 각 카드의 폭은 좁아집니다. 상품명이나 설명이 긴 카드라면 4열보다 3열이 더 읽기 좋을 수 있습니다.
minmax로 카드가 너무 작아지는 문제 막기
repeat(3, 1fr)은 간단하지만 한 가지 문제가 있습니다. 화면이 좁아져도 계속 3열을 유지하려고 합니다. 모바일 화면에서는 카드가 너무 좁아져 글자가 읽기 어려워질 수 있습니다.
이때 사용하는 패턴이 있습니다.
.product-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 24px;
}처음 보면 길어 보이지만, 의미를 나누면 이해하기 쉽습니다.
minmax(240px, 1fr)은 "카드 한 칸의 최소 크기는 240px, 가능하면 남은 공간을 나누어 넓게 쓰겠다"는 뜻입니다.
240px→ 카드가 이보다 너무 작아지지 않게 하는 최소 폭1fr→ 공간이 남으면 비율에 맞게 넓어지는 폭
auto-fit은 화면 너비에 맞춰 들어갈 수 있는 만큼 열을 자동으로 배치하라는 뜻입니다.
그래서 이 코드는 다음처럼 동작합니다.
넓은 화면에서는 3열 또는 4열로 보인다.
중간 화면에서는 2열로 줄어든다.
좁은 모바일 화면에서는 1열로 내려간다.
즉, 열 개수를 손으로 계속 바꾸지 않아도 화면 너비에 따라 카드 수가 자연스럽게 달라집니다. 반응형 웹페이지를 만들 때 자주 쓰는 Grid 패턴입니다.
지금은 이 문장을 외우기보다 결과를 기억하면 됩니다.
카드 목록이 화면 너비에 맞게 알아서 줄바꿈되게 하고 싶다면
repeat(auto-fit, minmax(240px, 1fr))를 떠올리세요.
상품 목록 카드 만들기
이제 실제 상품 목록처럼 보이는 예제를 만들어 보겠습니다. 완성 결과는 한 줄에 여러 상품 카드가 놓이고, 화면이 좁아지면 카드가 자연스럽게 다음 줄로 내려가는 구조입니다.
먼저 Grid 부모를 만듭니다.
.product-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 24px;
}이 코드만으로 상품 카드들은 격자 구조로 배치됩니다. 이제 카드 자체를 조금 꾸며 보겠습니다.
.product-card {
padding: 20px;
border: 1px solid #e5e7eb;
border-radius: 16px;
background-color: #ffffff;
}여기서는 이전 장에서 배운 속성들이 다시 등장합니다.
padding은 카드 안쪽 여백입니다.border는 카드의 경계를 보여 줍니다.border-radius는 모서리를 둥글게 만듭니다.background-color는 카드 배경색입니다.
이미지가 있는 상품 카드라면 이미지도 카드 폭에 맞춰야 합니다.
.product-card img {
width: 100%;
border-radius: 12px;
}width: 100%;는 이미지가 카드의 가로폭을 넘지 않게 해 줍니다. 상품 이미지가 카드 밖으로 삐져나오는 문제를 막을 때 자주 사용합니다.
제목과 가격도 간단히 정리해 보겠습니다.
.product-card h3 {
margin: 16px 0 8px;
font-size: 18px;
}
.product-card .price {
font-weight: 700;
color: #2563eb;
}이제 상품 목록은 단순히 아래로 쌓인 문서가 아니라 쇼핑몰에서 볼 수 있는 카드형 목록에 가까워집니다.
포트폴리오 갤러리 만들기
Grid는 이미지 갤러리에도 잘 어울립니다. 포트폴리오, 여행 사진, 인테리어 시공 사례처럼 이미지를 여러 장 보여 주는 화면에서 특히 유용합니다.
가장 기본적인 갤러리 CSS는 다음과 같습니다.
.gallery {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 16px;
}이미지는 각 칸에 꽉 차게 넣습니다.
.gallery img {
width: 100%;
height: 180px;
object-fit: cover;
border-radius: 12px;
}여기서 object-fit: cover;는 이미지를 정해진 영역에 꽉 차게 보여 주는 속성입니다. 액자에 사진을 넣는다고 생각하면 됩니다. 사진의 비율이 조금 달라도 빈 공간 없이 채워지지만, 일부 가장자리가 잘릴 수 있습니다.
이미지가 찌그러지는 것보다 자연스럽게 잘리는 편이 갤러리에서는 더 보기 좋을 때가 많습니다.
사진 전체가 잘리지 않고 모두 보여야 하는 경우에는 object-fit: contain;을 사용할 수도 있습니다. 다만 이 경우 이미지 주변에 빈 공간이 생길 수 있습니다.
Flexbox와 Grid를 고르는 간단한 기준
Flexbox와 Grid 중 무엇을 써야 할지 헷갈릴 수 있습니다. 처음에는 아래 기준으로 고르면 됩니다.
한 방향 정렬이면 Flexbox가 편합니다.
헤더에서 로고와 메뉴를 가로로 놓기
버튼 두 개를 나란히 놓기
배너 문구와 버튼을 양쪽 끝에 배치하기
카드 안에서 가격과 버튼을 한 줄로 정렬하기
여러 행과 열의 목록이면 Grid가 편합니다.
상품 카드 목록
블로그 글 카드 목록
포트폴리오 이미지 갤러리
팀원 소개 카드
후기 카드 모음
중요한 것은 둘 중 하나만 써야 한다는 생각을 버리는 것입니다. Grid는 큰 목록의 구조를 잡고, Flexbox는 그 안의 작은 정렬을 다듬는 식으로 함께 사용할 수 있습니다.
자주 하는 실수 체크리스트
Grid를 처음 사용할 때는 다음 부분에서 실수가 자주 납니다.
display: grid;를 카드 하나에 적용했다.카드들을 감싸는 부모 요소에 적용해야 합니다.
grid-template-columns를 쓰지 않았다.열 구조를 정하지 않으면 원하는 격자 모양이 나오지 않을 수 있습니다.
카드 사이 간격을 카드의
margin으로 각각 조정했다.Grid 부모에
gap을 쓰면 더 단순합니다.
repeat(4, 1fr)로 열을 너무 많이 만들었다.카드가 좁아져 글자가 답답해질 수 있습니다.
이미지에
width: 100%;를 주지 않았다.큰 이미지가 카드 밖으로 튀어나올 수 있습니다.
모바일에서 카드가 너무 작다.
repeat(auto-fit, minmax(220px, 1fr))같은 패턴을 사용해 보세요.
작은 실습: 블로그 글 목록을 Grid로 바꾸기
이번 실습에서는 블로그 글 카드 6개가 있다고 생각해 보겠습니다. 수정 전에는 카드가 아래로 길게 쌓여 있습니다.
수정 전 CSS입니다.
.post-list {
max-width: 960px;
margin: 0 auto;
}
.post-card {
padding: 20px;
margin-bottom: 20px;
border: 1px solid #e5e7eb;
border-radius: 16px;
}이 방식도 틀린 것은 아닙니다. 다만 PC 화면에서는 가로 공간이 많이 남습니다. 같은 크기의 카드가 반복된다면 Grid로 바꾸는 편이 더 정돈되어 보입니다.
수정 후 CSS입니다.
.post-list {
max-width: 960px;
margin: 0 auto;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 24px;
}
.post-card {
padding: 20px;
border: 1px solid #e5e7eb;
border-radius: 16px;
}달라진 점은 세 가지입니다.
첫째, .post-list에 display: grid;를 추가했습니다. 이제 이 안의 글 카드들이 격자로 배치됩니다.
둘째, grid-template-columns로 카드의 최소 폭을 정했습니다. 한 카드가 최소 260px은 확보하도록 했기 때문에 너무 좁아지는 일을 줄일 수 있습니다.
셋째, 카드마다 있던 margin-bottom을 없애고 부모에 gap: 24px;를 주었습니다. 이제 카드 사이 간격은 한 곳에서 관리됩니다.
수정 전후 비교
수정 전에는 카드가 한 줄에 하나씩만 보여서 PC 화면에서 목록이 길게 늘어졌습니다. 아래로 스크롤해야 하는 양도 많았습니다.
수정 후에는 화면 너비에 따라 카드가 2열 또는 3열로 정리됩니다. 같은 글 목록이라도 훨씬 가볍고 보기 좋게 느껴집니다. 모바일에서는 자동으로 1열에 가까운 구조가 되어 글을 읽기에도 편합니다.
이번 장의 핵심은 단순합니다. 반복되는 카드나 이미지가 많다면 먼저 Grid를 떠올리세요. 부모에 display: grid;, 열에는 grid-template-columns, 간격에는 gap을 사용하면 목록 화면의 절반은 이미 정리된 것입니다.
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.