Flexbox만으로는 부족하다
flex-basis로 2차원을 흉내 내는 순간, 이미 Grid가 풀 문제입니다.
새 페이지를 짜면서 display: flex부터 칩니다. 카드 세 장이 예쁘게 줄 세워져요. 그런데 한 줄에 네 장이 넘치면서 두 번째 줄로 넘어가자 갑자기 어긋나기 시작합니다. 두 번째 줄의 카드가 위 줄과 딱 맞게 정렬되지 않아요. 어찌어찌 flex-basis: calc(25% - 16px)로 욱여넣고 나서 생각하죠. "이게 맞나?"
이 글은 그 "이게 맞나?"에 대한 이야기입니다. Flex와 Grid는 어느 쪽이 더 좋은 게 아니라, 다루는 차원이 다릅니다. 그 차이만 잡으면 고민이 절반으로 줄어요.
비슷해 보이는데 왜 둘 다 있지
둘 다 display 한 줄이면 자식이 가로로 나열됩니다. justify-content: center도 똑같이 먹어요. 처음엔 "Flex만 알면 되지 않나" 싶은 게 당연해요. 저도 그랬거든요.
결정적으로 갈리는 건 한 방향만 보느냐, 두 방향을 동시에 보느냐 입니다. MDN 스펙 문서가 딱 한 문장으로 정리해줍니다.
"flexbox deals with layout in one dimension at a time - either as a row or as a column. This can be contrasted with the two-dimensional model of CSS Grid Layout, which controls columns and rows together." - MDN
Flex는 한 번에 한 축만 봅니다. 행이면 행, 열이면 열. Grid는 행과 열을 동시에 잡아요. 이게 이론으로는 와닿지 않는데, wrap을 시켜보면 눈으로 바로 보입니다.
같은 카드, 다른 줄 정렬
카드 다섯 개를 네 칸짜리 컨테이너에 넣어봅시다. Flex는 두 번째 줄을 새 flex line으로 취급해 독립적으로 공간을 나눠요. Grid는 열이 그대로 유지됩니다.
Flex 쪽에서 다섯 번째 카드가 혼자 한 줄을 꽉 채우는 걸 보세요. 두 번째 줄이 첫 번째 줄의 열 경계를 모른다는 증거입니다. Grid 쪽은 빈 칸이 생길 뿐, 열 경계는 그대로 유지됩니다.
Content-out vs Layout-in
조금 더 깊이 들어가면, 둘의 철학 자체가 반대 방향입니다.
Flex - content out
내용물이 먼저입니다. 각 아이템이 자기가 필요한 만큼 차지하고, 남는 공간을 grow/shrink로 나눠 가져요. '콘텐츠가 크기를 정한다'는 감각입니다.
Grid - layout in
레이아웃이 먼저입니다. 트랙(행/열)을 먼저 선언하고, 아이템은 그 틀 안으로 들어갑니다. 아이템의 내용이 틀보다 커지면 오히려 트랙이 따라 늘어날 수도 있어요.
신호 포착
Flex를 쓰는데 자꾸 'width를 직접 박아서' 윗줄과 맞추고 있다면, 그건 Layout-in이 필요하다는 뜻. 이 순간이 Grid로 갈아탈 타이밍입니다.
"If you are using flexbox and find yourself disabling some of the flexibility, you probably need to use CSS grid layout." - MDN
flex-item에 고정 width를 박고 있다면, 그건 "Flex의 유연성을 꺼서" 2D를 흉내 내고 있는 겁니다. Grid 한 줄이면 끝날 일이에요.
실전 결정 트리
머릿속에 한 줄만 박아두면 대부분의 고민이 정리됩니다.
행이나 열 중 하나만 제어하면 되는가? → Flex
행 과 열을 같이 제어해야 하는가? → Grid
구체적으로 옮겨보면 이렇습니다. Flex가 어울리는 자리는 내비게이션 바, 버튼 그룹, 태그 칩, 카드 내부에서 아이콘·텍스트·버튼을 한 줄에 정렬하는 경우처럼 "한 줄에 쭉 늘어놓고 간격만 조절"하는 일입니다. Grid가 어울리는 자리는 페이지 전체 골격(헤더·사이드바·메인·푸터), 대시보드, 이미지 갤러리, 상품 목록처럼 가로 세로 모두 정렬이 맞아야 하는 일이에요.
재미있는 건 이 둘이 경쟁 관계가 아니라는 점입니다. 보통 실제 페이지는 "바깥 골격은 Grid, 그 안의 각 칸 내부는 Flex"로 섞어 씁니다.
/* 페이지 골격: 2D */
.page {
display: grid;
grid-template-columns: 240px 1fr;
grid-template-rows: 64px 1fr;
grid-template-areas:
"sidebar header"
"sidebar main";
}
/* 헤더 안쪽: 1D */
.header {
grid-area: header;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24px;
}바깥은 두 축을 한꺼번에 잡아야 하니까 Grid. 헤더 안쪽은 그냥 로고 왼쪽, 메뉴 오른쪽이니까 Flex. 둘 중 하나만 골라야 하는 싸움이 아니에요.
그래서 다음에 손이 멈추면
display: flex를 치기 전에 스스로한테 한 번만 물어보세요. "이 레이아웃에서 아래 줄과 위 줄의 열이 맞아야 하나?" 맞아야 하면 Grid입니다. 상관없으면 Flex고요. 이 질문 하나로 저는 flex-basis: calc(...)를 써 넣다가 멈추는 일이 훨씬 줄었습니다.
그리고 한 걸음 더 나아가고 싶다면, Grid의 grid-template-areas와 auto-fit/minmax()를 찾아보세요. 반응형 카드 그리드를 미디어 쿼리 없이 한 줄로 끝내는 경험을 하고 나면, 애초에 Flex로 고민하던 많은 문제가 Grid에서는 문제도 아니었다는 걸 알게 됩니다.
참고 자료
- MDN - Basic concepts of flexbox
Flex의 1차원 모델과 main/cross 축 정의
- MDN - Basic concepts of grid layout
Grid의 2차원 트랙·라인·셀 용어
- MDN - Relationship of grid layout with other layout methods
"row or column vs row and column" 결정 기준과 content-out/layout-in 철학
- W3C - CSS Grid Layout Module Level 2
Grid 공식 스펙
- W3C - CSS Flexible Box Layout Module Level 1
Flexbox 공식 스펙