본문으로 건너뛰기
Tech Blog

Flexbox만으로는 부족하다

글 복사 완료!

flex-basis로 2차원을 흉내 내는 순간, 이미 Grid가 풀 문제입니다.

·8분·

새 페이지를 짜면서 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

조금 더 깊이 들어가면, 둘의 철학 자체가 반대 방향입니다.

1

Flex - content out

내용물이 먼저입니다. 각 아이템이 자기가 필요한 만큼 차지하고, 남는 공간을 grow/shrink로 나눠 가져요. '콘텐츠가 크기를 정한다'는 감각입니다.

2

Grid - layout in

레이아웃이 먼저입니다. 트랙(행/열)을 먼저 선언하고, 아이템은 그 틀 안으로 들어갑니다. 아이템의 내용이 틀보다 커지면 오히려 트랙이 따라 늘어날 수도 있어요.

3

신호 포착

Flex를 쓰는데 자꾸 'width를 직접 박아서' 윗줄과 맞추고 있다면, 그건 Layout-in이 필요하다는 뜻. 이 순간이 Grid로 갈아탈 타이밍입니다.

MDN이 이 신호를 아주 분명하게 적어뒀어요.

"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-areasauto-fit/minmax()를 찾아보세요. 반응형 카드 그리드를 미디어 쿼리 없이 한 줄로 끝내는 경험을 하고 나면, 애초에 Flex로 고민하던 많은 문제가 Grid에서는 문제도 아니었다는 걸 알게 됩니다.

참고 자료

관련 글