기본 콘텐츠로 건너뛰기

CSS Flexbox와 Grid, 무엇이 다르고 언제 선택해야 할까

CSS Flexbox와 Grid, 무엇이 다르고 언제 선택해야 할까

빠른 답

  • 한 줄 정렬, 버튼 그룹, 메뉴 바처럼 콘텐츠 흐름이 먼저 보이는 화면은 Flexbox 쪽이 더 단순하게 풀리는 경우가 많습니다.
  • 페이지 골격, 카드 목록, 대시보드처럼 행과 열 규칙을 먼저 정해야 하는 화면은 Grid가 더 읽기 쉽고 배치도 안정적으로 유지됩니다.
  • 둘은 대체재라기보다 함께 쓰는 경우가 많습니다. 바깥 구조는 Grid로, 카드나 헤더 내부 정렬은 Flexbox로 나누면 역할이 또렷해집니다.
  • 1차원2차원이라는 구분만 외우기보다, 콘텐츠 크기 변화가 먼저인지 배치 규칙이 먼저인지로 보면 선택이 한결 쉬워집니다.

Flexbox와 Grid는 둘 다 CSS 레이아웃 도구이지만, 브라우저가 레이아웃을 계산하는 출발점이 다릅니다. 브라우저는 DOM과 CSSOM을 합쳐 렌더 트리를 만든 뒤 레이아웃 단계에서 박스의 크기와 위치를 정하는데, 이때 Flexbox는 아이템 흐름을 중심에 두고, Grid는 트랙 구조를 중심에 둡니다.

한눈에 비교

출발점
Flexbox는 아이템의 순서와 크기를 따라 남는 공간을 분배하고, Grid는 컨테이너의 행과 열을 먼저 선언한 뒤 아이템을 그 위에 올립니다.
계산 축
Flexbox는 주 축과 교차 축이라는 한 축 중심 계산이 기본이고, Grid는 행과 열을 함께 다루는 2차원 배치가 기본입니다.
줄바꿈 이후
Flexbox는 flex-wrap 이 걸려도 각 줄이 독립적으로 공간을 나누고, Grid는 같은 열 트랙을 공유하므로 열 폭이 더 일정하게 유지됩니다.
제어 위치
Flexbox는 flex-grow , flex-shrink , flex-basis 처럼 아이템 쪽 선언 비중이 크고, Grid는 grid-template-columns , grid-auto-rows , minmax() 처럼 컨테이너 쪽 선언 비중이 큽니다.
화면 변화 대응
Flexbox는 콘텐츠 길이와 아이템 수 변화에 민감하게 반응하고, Grid는 열 수와 영역 구조를 유지하면서 뷰포트 변화에 대응합니다.
잘 맞는 장면
Flexbox는 툴바, 태그 목록, 버튼 정렬에 잘 맞고, Grid는 카드 목록, 갤러리, 페이지 골격에 잘 맞습니다.

시간 흐름으로 이해하기

HTML과 CSS를 읽는 시점
브라우저는 DOM과 CSSOM을 각각 만들고, 두 정보가 모여야 실제 화면 배치를 계산할 준비가 됩니다.
렌더 트리를 만드는 시점
보이는 요소만 추려 렌더 트리를 구성하므로, 이 단계에서 레이아웃 대상이 정리됩니다.
첫 레이아웃 계산 시점
Flexbox는 주 축을 따라 아이템 크기와 남는 공간을 나누고, Grid는 행과 열 트랙을 계산합니다.
콘텐츠나 창 크기가 바뀌는 시점
Flexbox는 줄 단위로 다시 분배하고, Grid는 트랙 규칙을 유지한 채 열 수와 배치를 다시 맞춥니다.
페인트와 컴포지팅 직전
위치나 크기가 바뀌면 레이아웃 뒤에 페인트가 이어지고, 변경 속성에 따라 컴포지팅 단계까지 영향이 갈 수 있습니다.

브라우저 안에서 실제로 무슨 순서로 일어날까

  • HTML 파싱
  • DOM 생성
  • CSSOM 생성
  • 렌더 트리 구성
  • 레이아웃 계산
  • 페인트와 컴포지팅

이 순서는 MDN의 Critical Rendering Path에서 설명하는 브라우저 렌더링의 기본 흐름과 맞닿아 있습니다. Flexbox와 Grid의 차이도 이 가운데 레이아웃 계산 단계에서 가장 선명하게 드러납니다.

Flexbox는 아이템이 어느 방향으로 흐르는지, 기본 크기가 얼마인지, 남는 공간을 누가 가져갈지를 계산합니다. 반대로 Grid는 컨테이너의 행과 열 트랙을 먼저 정하고, 각 아이템이 어느 셀이나 영역에 들어갈지를 계산합니다. 같은 화면이라도 어떤 모델을 쓰느냐에 따라 브라우저가 다시 계산해야 하는 범위와 읽기 방식이 달라집니다.

왜 둘 다 정렬 도구처럼 보여 헷갈릴까

두 도구 모두 justify-content, align-items, gap 같은 속성을 공유합니다. 이 공통점은 MDN의 flexbox와 다른 레이아웃 방식의 관계 문서에서 설명하듯 CSS Box Alignment 계열 개념을 함께 쓰기 때문입니다.

다만 정렬 속성이 비슷하다고 계산 모델까지 같지는 않습니다. Flexbox는 “이 아이템들이 이 방향으로 어떻게 흐를까”에 가까운 도구이고, Grid는 “이 화면을 어떤 칸 구조로 나눌까”에 가까운 도구입니다. 둘 다 요소를 가지런히 놓아주지만, 하나는 콘텐츠에서 시작하고 다른 하나는 배치 구조에서 시작합니다.

선택 기준 매트릭스

상황
상단 내비게이션, 버튼 그룹, 태그 목록이라면 Flexbox를 먼저 떠올릴 만합니다. 텍스트 길이와 아이템 수가 달라져도 한 축 정렬과 간격 조절이 비교적 간결합니다.
상황
카드 목록, 상품 그리드, 갤러리라면 Grid가 더 잘 맞는 경우가 많습니다. 카드 수보다 열 규칙을 먼저 정하는 편이 폭 흔들림을 줄여줍니다.
상황
헤더, 사이드바, 본문처럼 페이지 골격을 나누는 화면이라면 Grid가 더 읽기 쉬운 선언이 됩니다. 영역과 트랙 크기를 먼저 잡을 수 있기 때문입니다.
상황
카드 내부의 제목, 메타 정보, 버튼 정렬처럼 작은 컴포넌트 안쪽 정렬이라면 Flexbox가 보통 더 짧고 분명한 코드를 만듭니다.
조건
콘텐츠 길이 변화가 크고 한 줄 또는 한 열 안에서 공간 분배가 중요하면 Flexbox 쪽이 가깝고, 같은 열 폭과 명시적인 배치 규칙이 중요하면 Grid 쪽이 더 잘 맞습니다.

Flexbox가 먼저 떠오르는 장면

툴바, 필터 바, 메뉴, 배지 목록처럼 한 줄의 흐름이 중심인 UI는 Flexbox가 편하게 느껴집니다. 요소 수가 달라져도 주 축 기준으로 남는 공간을 다시 나누기 때문에, 레이아웃 의도가 비교적 그대로 유지됩니다.

.toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
}

.toolbar .search {
  flex: 1 1 240px;
  min-width: 0;
}

.toolbar .actions {
  display: flex;
  gap: 8px;
  margin-inline-start: auto;
}

이 패턴은 검색창 폭이 유동적이고 버튼 개수도 달라질 수 있는 화면에 잘 맞습니다. 다만 flex-wrap으로 여러 줄이 되면 각 줄이 독립적으로 계산된다는 점은 기억해 둘 만합니다. 카드 목록처럼 열 정렬의 일관성이 중요한 화면에서는 이 특성이 오히려 어색하게 보일 수 있습니다.

Grid가 먼저 떠오르는 장면

카드 목록, 대시보드, 갤러리처럼 “어느 칸에 무엇을 놓을지”를 먼저 정하는 화면은 Grid가 더 직접적인 표현을 제공합니다. 콘텐츠 길이가 조금씩 달라도 열 구조를 유지하기 쉬워서 화면 리듬이 무너지지 않는 편입니다.

.card-list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 24px;
}

.card {
  min-width: 0;
}

.card.featured {
  grid-column: span 2;
}

repeat(auto-fit, minmax(240px, 1fr))는 “카드는 최소 240px을 확보하고, 남는 공간은 균등하게 분배한다”는 규칙을 먼저 세웁니다. 이런 선언은 반복되는 카드 UI나 대시보드 위젯처럼 구조 일관성이 중요한 화면에서 읽기 쉽습니다. MDN의 Grid 문서도 이런 트랙 중심 사고방식을 Grid의 강점으로 다룹니다.

함께 쓰는 구성이 더 자연스러운 경우

많은 화면은 둘 중 하나로만 끝나지 않습니다. 페이지 전체 구조는 Grid로 나누고, 각 영역 내부의 제목 정렬이나 버튼 묶음은 Flexbox로 처리하면 역할이 자연스럽게 분리됩니다.

.app {
  display: grid;
  grid-template-columns: 280px minmax(0, 1fr);
  min-height: 100vh;
}

.main {
  display: grid;
  grid-template-rows: auto 1fr;
}

.card-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 12px;
}

여기서 minmax(0, 1fr)는 긴 텍스트나 넓은 콘텐츠 때문에 트랙이 예상보다 커지며 넘치는 상황을 줄이는 데 도움이 됩니다. 바깥은 칸 구조, 안쪽은 콘텐츠 흐름이라는 식으로 책임을 나누면 CSS를 읽거나 수정할 때도 맥락이 분명해집니다.

DevTools로 확인하는 포인트

레이아웃 문제가 생기면 문법만 보지 말고 브라우저가 무엇을 다시 계산했는지 확인하는 편이 좋습니다. Chrome DevTools에서는 Flexbox와 Grid 모두 오버레이를 켜서 정렬 축, 라인, 트랙 크기를 시각적으로 볼 수 있습니다. 관련 기능은 Flexbox 디버깅 문서Grid 디버깅 문서에서 확인할 수 있습니다.

성능 패널에서는 어떤 상호작용이 LayoutPaint를 얼마나 일으켰는지 볼 수 있습니다. 같은 사이드 패널 토글이라도 width를 바꾸는 방식은 레이아웃 계산을 다시 요구하기 쉽고, transform 중심 전환은 경우에 따라 컴포지팅 단계에서 더 가볍게 처리됩니다. 또 Rendering 패널의 Paint flashing, Layout Shift Regions, Layer Borders를 켜 보면 화면 어디가 다시 칠해졌는지와 레이어 분리가 어떻게 되었는지 더 분명하게 보입니다. 자세한 항목은 Chrome DevTools의 rendering 성능 가이드에서 다룹니다.

상호작용에 따른 레이아웃 변화를 직접 측정해 보고 싶다면, 아래처럼 클래스 토글 전후에 performance.mark()requestAnimationFrame()을 붙여 간단히 시간을 기록할 수 있습니다.

const toggle = document.querySelector('.toggle');
const sidebar = document.querySelector('.sidebar');

toggle.addEventListener('click', () => {
  performance.mark('toggle-start');
  sidebar.classList.toggle('is-collapsed');

  requestAnimationFrame(() => {
    sidebar.getBoundingClientRect();
    performance.mark('toggle-end');
    performance.measure('sidebar-layout', 'toggle-start', 'toggle-end');
    console.table(performance.getEntriesByName('sidebar-layout').slice(-1));
  });
});

이 코드는 DevTools의 Performance 기록을 대신하지는 않지만, 어떤 상호작용이 레이아웃 읽기와 맞물리는지 빠르게 확인하는 데는 도움이 됩니다. Flexbox와 Grid 자체보다도, 그 레이아웃을 어떤 속성으로 계속 흔들고 있는지가 체감 비용에 더 큰 영향을 주는 경우도 적지 않습니다.

자주 헷갈리는 지점

  • flex-wrap이 있으니 Grid가 필요 없다고 보기는 어렵습니다. Flexbox의 각 줄은 독립적으로 계산되므로, 여러 줄 카드 목록에서 열 폭이 일정하게 유지되지는 않습니다.
  • Grid가 항상 더 무겁다고 보기도 어렵습니다. 실제 비용은 DOM 크기, 변경 범위, 애니메이션 속성, 페인트 영역에 더 크게 좌우됩니다.
  • justify-contentalign-items를 둘 다 쓴다고 해서 같은 레이아웃 모델로 보면 설명이 자주 꼬입니다. 정렬 속성은 비슷해도 크기와 위치를 계산하는 출발점은 다릅니다.
  • “한 줄이면 Flexbox, 여러 줄이면 Grid”처럼 외우는 방식도 자주 빗나갑니다. 여러 줄이어도 흐름 중심이면 Flexbox가 맞을 수 있고, 한 화면이어도 구조 중심이면 Grid가 더 잘 맞을 수 있습니다.

더 읽을 거리

원문 참고

https://www.maeil-mail.kr/question/51

댓글

이 블로그의 인기 게시물

아이콘 폰트 (icomoon 사용법)

 장난감 프로젝트를 만들다 보면, 아이콘이 필요한 경우가 있다. 간단하게 아이콘을 인터넷에서 검색하여, 이미지로 넣어두고 이미지 태그를 이용하여, 사용하는 경우가 일반적이였지만...  요즘에는 대부분 폰트를 이용하여 아이콘을 노출 한다. 나 같은 경우에도 기본적으로  https://material.io/resources/icons 를 참고하여 아이콘 폰트를 이용할 수 있도록 처리하고, 추가적으로 필요한 아이콘이고, 일상적으로 사용 되지 않는 아이콘의 경우에는  https://icomoon.io 에서 제작하여, 아이콘 폰트로 이용 하곤 한다.  그래서 이번에는 아이콘  https://icomoon.io 의 사용법을 간단히 공유하고자 한다.   들어가자 마자 위의 icoMoonApp버튼을 누르면 아래와 같은 화면이 나타난다.  icomoon에서 무료로 제공하는 아이콘들이 보이면 위에 파란색으로 표시 되어있는 집 모양 세가지를 선택한 후, 아래의 빨간색으로 표시되어있는 Generate Font를 눌러보자.  그리고 나서 바로 다운로드를 요청해보자. icomoon.zip이 다운로드가 될텐데, 압축을 해제해 보면, 아래의 폴더 및 파일들이 있다. 아래에서 중요한 것은 font 폴더와 style.css이다. demo-files fonts demo.html Read Me.txt selection.json style.css <!doctype html > <html> <head> <link rel ="stylesheet" href ="style.css" ></head> </head> <body> <span class ="icon-home" ></span> <span class ="icon-home2" ></span> <span class ="icon-hom...

Chart js와 amchart 비교

Chart js 특징은 위의 그림으로 대체 할 수 있을 듯 하다. 오픈 소스이고, 기본으로 제공하는 차트 종류가 8가지 Canv a s를 이용해서 차트를 그리고, 반응형을 지원한다. amchart amchart는 기본적으로 유료이며, 기본으로 제공하는 차트 종류가 기본적인 차트 + 주식 처럼 보이는 차트 + 지도에 관련된 차트(?) 까지 하면, 기본 제공 하는 종류가 20개 내외 이려나, 일일이 세기에는 양이 좀 많아 보인다. 렌더링은 svg를 통하여 그려지고, 당연 반응형도 지원이 된다. 그러면, 이 둘중에 어떤것이 내 프로젝트에 적합 하냐는 것이 문제이다. 일단, 주식 처럼 보이는 차트나 지도에 관련된 차트(?)가 필요하면, amchart를 선택해야 되는 것은 맞다. 그건 당연한 것이니 빼고 얘기 해보자! 여러 종류의 차트가 필요하다면, 일단은 amchart를 염두해 두는 것이 좋다. 돈 낸 만큼은 하는 듯 하다. 하지만, 기본적인 막대 그래프, 도넛 차트 등, 아주 기본적인 차트들인데, Chart js도 amchart도 그러한 차트가 없을 때가 문제가 된다. 그렇다면, 조금이라도 커스텀이 용이한 것을 찾는 것이 좋을 것이다.  일단 amchart에서 custom이라고 검색 하였을 때, 검색 결과가 61가지가 나온다. 차트의 종류도 많고, 각 차트마다 들어가는 속성이 매우 많기 때문에, 웬만한 내용들은 속성 값을 어떻게 주느냐에 따라서 변경이 가능 하게 된다. 커스텀의 예를 들면, 기본적으로 도넛 파이의 형태를 띄면서, 화살표로 목표를 표시해주는 차트가 필요하다고 생각 해보자. 이것은 amchart로 만든 그래프이고 이것은 chart js로 만든 그래프이다. 모양이 살짝 다르긴 하지만, 완벽하게 똑같이 구현 할 수도 있다. amchart로 만든 그래프의 경우, 저것은 도넛그래프가 아닌 guage 그래프이다. 원래 게이지 그래프는 이와 같...

javascript 압축 파일 다운로드

이번에는 전 게시글의 응용판? 이라고 해야하나....? 어쨋든! 우리는 각각의 파일들을 다운로드 해보았다. 그런데 생각보다 귀찮음?을 느꼇을 것이다. 파일을 각각 다운 받아야 한다는 현실때문에! 그래 파일 두개야 뭐 그렇다 치지... 하지만, 개발자도 사용자도 게으름뱅이이다. 자 결국, 우리가 해야 하는 것은 파일을 한 번에 둘다! 다운 받는 것이다. 물론, 클릭 한번에 여러개의 함수를 엮어서 다운받게 하면 되지만! 크롬에서 자주 봤듯이, 여러개의 파일을 다운로드를 시도하면 <- 여러개의 파일을 다운로드 합니다. 허용 합니까? 하고 물어보는 것을 볼 수 있다. 게다가 다운로드 한 파일들을 찾기도 귀찮다는 것. 자 해결책을 제시해보자면, https://github.com/Stuk/jszip 클라이언트 단에서 파일을 zip파일로 압축을 할 수가 있다! 필요한 작업은 아래와 같다. 0. 데이터 준비 1. BLOB(binary large object)를 만든다. 2. Blob을 URL.createObjectURL을 사용하여, 해당 binary의 주소를 생성. 3. 다운로드가 필요한 파일들을 Zip 객체에 셋팅! 4. a태그를 이용하여, 해당 url 셋팅 하고, 다운로드. 전 게시물과 별로 달라진게 없네... 자 그럼 샘플! 샘플을 보자! http://embed.plnkr.co/NMprnRxqYG0fkHa2J55D/ var util = {} function fixBinary(bin) { //binary to arrayBuffer var length = bin.length var buf = new ArrayBuffer(length) var arr = new Uint8Array(buf) for (var i = 0; i < length; i++) { arr[i] = bin.charCodeAt(i) } return buf } window.onload = function() { ...