웹 레이아웃을 짤 때 Grid를 써야 하나 Flexbox를 써야 하나 고민한 적 있을까. 대부분의 개발자들은 둘 다 '정렬된 박스들'이라고 생각해서 그냥 익숙한 것만 쓰거나, 구글에서 답을 찾아서 복사-붙여넣기한다. 다만 두 방식은 근본적으로 다른 목적을 가지고 있고, 어떤 상황에서 어느 것을 쓰느냐에 따라 코드 복잡도와 유지보수성이 완전히 달라진다. 이번에는 Grid와 Flexbox의 정확한 차이를 이해하고, 실제 프로젝트에서 올바른 선택을 할 수 있도록 정리해보겠다.
Grid는 2차원(두 방향) 레이아웃을 위한 도구다. 행과 열을 동시에 정의해서 마치 엑셀 스프레드시트처럼 칸을 만들고, 그 칸에 요소들을 배치한다. Grid는 전체 구조를 먼저 정의한 후 각 아이템이 어느 칸에 들어갈지 명확하게 지정한다. 예를 들어 웹사이트의 헤더, 사이드바, 메인 콘텐츠, 푸터를 한 번에 배치하는 레이아웃은 Grid가 훨씬 깔끔하다.
| 특성 | Flexbox | Grid |
|---|---|---|
| 방향 | 1차원(가로 또는 세로) | 2차원(가로 + 세로) |
| 구조 정의 | 컨테이너만 정의, 아이템은 자동 배치 | 행과 열을 명시적으로 정의 |
| 콘텐츠 중심 | 아이템에 따라 크기 변함(Content-first) | 레이아웃에 따라 아이템 배치(Layout-first) |
| 복잡도 | 간단한 1줄 정렬에 최적 | 복잡한 다중 영역 레이아웃에 최적 |
| 예시 | 네비게이션, 버튼 그룹 | 대시보드, 웹사이트 전체 레이아웃 |
✗ 잘못된 예시(Flexbox 오남용)
<style>
.page-layout {
display: flex;
flex-wrap: wrap;
gap: 20px;
}
.header { width: 100%; }
.sidebar { width: 20%; }
.main { width: 60%; }
.footer { width: 100%; }
</style>
<div class="page-layout">
<div class="header">헤더</div>
<div class="sidebar">사이드바</div>
<div class="main">메인</div>
<div class="footer">푸터</div>
</div>
이 방식은 작동하지만 문제가 있다. Flexbox는 wrap을 쓸 때 자동으로 다음 줄로 넘어가는데, footer가 sidebar와 main 옆에 나타날 수 있다. 너비 백분율도 수동으로 계산해야 하고 반응형 대응이 까다롭다.
✓ 올바른 Flexbox 사용(1줄 정렬)
<style>
.navbar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 20px;
background: #333;
}
.nav-item {
color: white;
cursor: pointer;
}
.nav-item:hover {
opacity: 0.7;
}
</style>
<div class="navbar">
<div>로고</div>
<div class="nav-item">홈</div>
<div class="nav-item">상품</div>
<div class="nav-item">연락처</div>
</div>
이것이 Flexbox의 진정한 용도다. 한 줄에 여러 아이템을 배치하고, justify-content로 좌우 배치를 제어하며, align-items로 수직 정렬을 한다. 코드가 간결하고 반응형 대응도 쉽다.
✓ Grid로 전체 페이지 레이아웃 구성
<style>
.page-layout {
display: grid;
grid-template-columns: 200px 1fr 200px;
grid-template-rows: auto 1fr auto;
gap: 20px;
height: 100vh;
}
.header {
grid-column: 1 / -1;
background: #333;
color: white;
padding: 20px;
}
.sidebar {
background: #f0f0f0;
padding: 20px;
}
.main {
background: white;
padding: 20px;
}
.footer {
grid-column: 1 / -1;
background: #333;
color: white;
padding: 20px;
}
@media (max-width: 768px) {
.page-layout {
grid-template-columns: 1fr;
grid-template-rows: auto auto auto auto;
}
.header { grid-column: 1; }
.sidebar { grid-column: 1; }
.main { grid-column: 1; }
.footer { grid-column: 1; }
}
</style>
<div class="page-layout">
<div class="header">헤더</div>
<div class="sidebar">사이드바</div>
<div class="main">메인 콘텐츠</div>
<div class="footer">푸터</div>
</div>
Grid는 레이아웃의 의도가 명확하다. grid-template-columns로 3개 열(200px, 1fr, 200px)을 정의하고, grid-template-rows로 3개 행을 정의했다. grid-column: 1 / -1은 첫 번째 열부터 마지막 열까지 span한다는 뜻이다. 반응형으로 변경할 때도 grid-template-columns만 바꾸면 된다.
✓ Grid로 상품 카드 그리드 구성
<style>
.product-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
padding: 20px;
}
.product-card {
background: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.product-card img {
width: 100%;
height: 200px;
object-fit: cover;
}
.product-card p {
padding: 10px;
}
</style>
<div class="product-grid">
<div class="product-card">
<img src="product1.jpg" alt="상품1" />
<p>상품1</p>
</div>
<div class="product-card">
<img src="product2.jpg" alt="상품2" />
<p>상품2</p>
</div>
<div class="product-card">
<img src="product3.jpg" alt="상품3" />
<p>상품3</p>
</div>
</div>
repeat(auto-fit, minmax(250px, 1fr))은 GridTemplate의 강력한 기능이다. 화면 크기에 맞춰 자동으로 열 개수를 조절하면서도, 최소 250px 너비를 유지한다. 미디어 쿼리 없이도 완벽한 반응형이 된다.
✗ 잘못된 중첩(Grid 안에 Flex 오남용)
<style>
.layout {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
}
.card {
display: flex;
flex-direction: column;
justify-content: space-between;
}
</style>
<div class="layout">
<div class="card">
<h3>제목</h3>
<p>내용</p>
<button>버튼</button>
</div>
</div>
이것도 작동하지만 불필요하게 복잡하다. 카드 내부 요소들을 세로로 정렬하려면 Grid로도 충분하다.
✓ 올바른 중첩(Grid 안에 Flex 필요할 때)
<style>
.dashboard {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
}
.stat-box {
background: white;
padding: 20px;
border-radius: 8px;
}
.stat-box-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
</style>
<div class="dashboard">
<div class="stat-box">
<div class="stat-box-header">
<span>매출</span>
<span>↑ 12%</span>
</div>
<p>$1,234,567</p>
</div>
</div>
이것이 올바른 조합이다. 대시보드 전체는 Grid(2열 레이아웃)로, 각 카드 내부의 헤더는 Flexbox(타이틀과 수치를 좌우 정렬)로 처리한다. 계층이 명확하고 각 도구가 자기 역할을 한다.
Flexbox를 쓰는 경우: 한 줄의 요소들을 정렬하고 싶을 때. 네비게이션 바, 버튼 그룹, 상단 탭 메뉴 등이 전형적인 예다. 아이템 개수가 정해지지 않았거나 동적으로 변할 때도 Flexbox가 낫다.
Grid를 쓰는 경우: 2차원 레이아웃이 필요할 때. 웹사이트 전체 구조(헤더/사이드바/메인/푸터), 대시보드, 상품 갤러리, 데이터 테이블 같은 복잡한 레이아웃에 최적이다.
둘 다 쓰는 경우: Grid로 큰 틀을 잡고, Flexbox로 세부 정렬을 한다. 특히 카드형 레이아웃이나 대시보드에서 이 패턴이 자주 나타난다.
레이아웃은 성능과 유지보수성을 크게 좌우한다. Flexbox의 간결함과 Grid의 명확성 중 상황에 맞는 것을 골라 사용하면, 코드 복잡도를 줄이면서도 반응형 대응을 깔끔하게 할 수 있다. 이 글의 테이블과 예제들을 참고해서 다음 프로젝트에서는 어느 상황에 무엇을 쓸지 확신 있게 선택해보자.