display: flex만 붙이면 뭔가 되긴 하는데, 왜 원하는 대로 정렬이 안 될까요?

Flexbox 완전 정복 — 축, 정렬, 그리고 실전 레이아웃 패턴

Flexbox를 처음 쓸 때 가장 혼란스러운 부분은 "어떤 속성이 가로 정렬이고 어떤 속성이 세로 정렬인지"입니다. 사실 Flexbox에는 가로/세로 개념이 없어요. 주축(main axis) 과 교차축(cross axis) 이라는 개념이 있을 뿐이고, flex-direction에 따라 축의 방향이 바뀝니다. 이 글에서는 축 개념부터 시작해서 실전 레이아웃 패턴까지 한 번에 정리해 보겠습니다.


1. Flexbox가 뭔가 — 1차원 레이아웃 모델

Flexbox(Flexible Box Layout)는 한 방향으로 아이템을 배치하고 정렬하는 1차원 레이아웃 모델 입니다.

  • float이나 inline-block으로 레이아웃을 잡던 시절의 고통을 해결하기 위해 등장했습니다
  • "1차원"이라는 건 행(row) 또는 열(column) 중 하나의 축만 다룬다는 뜻입니다
  • 행과 열을 동시에 제어하고 싶다면 Grid를 사용합니다
CSS
/* Flexbox 활성화 */
.container {
  display: flex; /* 자식 요소들이 flex 아이템이 된다 */
}

Flexbox는 컨테이너(부모)에 display: flex를 선언하는 것으로 시작합니다. 속성의 대부분은 컨테이너에 작성하고, 일부만 아이템(자식)에 작성합니다.


2. 주축과 교차축 — 모든 혼란의 원인

Flexbox를 제대로 이해하려면 축(axis) 개념을 먼저 잡아야 합니다.

  • ** 주축(Main Axis)**: 아이템이 배치되는 방향
  • ** 교차축(Cross Axis)**: 주축에 수직인 방향

핵심은 flex-direction에 따라 축이 바뀐다는 것입니다.

flex-direction주축 방향교차축 방향
row (기본값)가로 (→)세로 (↓)
row-reverse가로 (←)세로 (↓)
column세로 (↓)가로 (→)
column-reverse세로 (↑)가로 (→)

공부하다 보니 여기서 정말 많이 헷갈렸습니다. justify-content는 항상 "가로 정렬"이 아니라, ** 주축 정렬 **이에요. flex-direction: column이면 justify-content가 세로 방향으로 동작합니다.


3. 컨테이너 속성 — 부모가 레이아웃을 결정한다

flex-direction

아이템의 배치 방향(주축)을 결정합니다.

CSS
.container {
  flex-direction: row;            /* 기본값: 가로 배치 */
  flex-direction: column;         /* 세로 배치 */
}

flex-wrap

아이템이 한 줄을 넘길 때 줄바꿈 여부를 결정합니다.

CSS
.container {
  flex-wrap: nowrap;   /* 기본값: 한 줄에 모두 (넘치면 축소) */
  flex-wrap: wrap;     /* 넘치면 다음 줄로 */
}

justify-content — 주축 정렬

CSS
.container {
  justify-content: flex-start;    /* 시작점 정렬 (기본값) */
  justify-content: center;        /* 가운데 정렬 */
  justify-content: flex-end;      /* 끝점 정렬 */
  justify-content: space-between; /* 양 끝 붙이고 사이 균등 */
  justify-content: space-around;  /* 아이템 양쪽에 균등 여백 */
  justify-content: space-evenly;  /* 모든 간격 동일 */
}

align-items — 교차축 정렬 (한 줄)

CSS
.container {
  align-items: stretch;    /* 기본값: 교차축 꽉 채움 */
  align-items: flex-start; /* 교차축 시작점 */
  align-items: center;     /* 교차축 가운데 */
  align-items: flex-end;   /* 교차축 끝점 */
  align-items: baseline;   /* 텍스트 기준선 정렬 */
}

align-content — 교차축 정렬 (여러 줄)

flex-wrap: wrap으로 여러 줄이 생겼을 때만 효과가 있습니다.

CSS
.container {
  flex-wrap: wrap;
  align-content: flex-start;    /* 줄들을 시작점으로 */
  align-content: center;        /* 줄들을 가운데로 */
  align-content: space-between; /* 줄 사이 균등 배치 */
}

align-items는 "줄 안에서" 아이템의 교차축 위치를, align-content는 "줄과 줄 사이"의 간격을 제어합니다. 이 차이를 구분하는 게 중요합니다.

gap

아이템 사이의 간격을 설정합니다. margin으로 간격을 잡는 것보다 훨씬 깔끔합니다.

CSS
.container {
  gap: 16px;          /* 행·열 간격 동일 */
  gap: 16px 24px;     /* 행 간격 16px, 열 간격 24px */
}

4. 아이템 속성 — 개별 아이템의 행동을 제어한다

flex-grow — 남은 공간 분배

CSS
.item {
  flex-grow: 0; /* 기본값: 남은 공간을 가져가지 않음 */
  flex-grow: 1; /* 남은 공간을 비율대로 가져감 */
}

예를 들어 A에 flex-grow: 1, B에 flex-grow: 2를 주면, 남은 공간을 1:2로 나눠 갖습니다.

flex-shrink — 공간 부족 시 축소 비율

CSS
.item {
  flex-shrink: 1; /* 기본값: 공간 부족하면 축소됨 */
  flex-shrink: 0; /* 절대로 줄어들지 않음 */
}

사이드바처럼 고정 너비가 필요한 요소에 flex-shrink: 0을 자주 사용합니다.

flex-basis — 초기 크기

주축 방향의 초기 크기를 설정합니다. width/height 대신 사용한다고 생각하면 됩니다.

CSS
.item {
  flex-basis: auto; /* 기본값: 콘텐츠 크기 또는 width/height 값 */
  flex-basis: 200px; /* 200px에서 시작 */
  flex-basis: 0%;    /* 콘텐츠 크기 무시, grow 비율로만 결정 */
}

flex shorthand — 이것만 기억하세요

flexflex-grow, flex-shrink, flex-basis의 축약 속성입니다.

CSS
.item {
  flex: 0 1 auto;  /* 기본값 (안 늘어남, 줄어듦, 콘텐츠 기반) */
  flex: 1;         /* = flex: 1 1 0% (늘어남, 줄어듦, 비율 기반) */
  flex: auto;      /* = flex: 1 1 auto (늘어남, 줄어듦, 콘텐츠 기반) */
  flex: none;      /* = flex: 0 0 auto (고정 크기) */
}

align-self — 개별 아이템의 교차축 정렬

컨테이너의 align-items를 개별 아이템에서 덮어쓸 수 있습니다.

CSS
.special-item {
  align-self: center; /* 이 아이템만 교차축 가운데 */
}

order — 시각적 순서 변경

HTML 순서를 바꾸지 않고 시각적 순서만 변경합니다. 기본값은 0이고, 값이 작을수록 앞에 배치됩니다.

CSS
.first-visually {
  order: -1; /* 맨 앞으로 */
}

5. flex: 1의 진짜 의미

flex: 1은 아마 Flexbox에서 가장 많이 쓰는 선언일 겁니다. 정확히 풀어쓰면 이렇습니다.

CSS
/* flex: 1은 아래와 같다 */
.item {
  flex-grow: 1;    /* 남은 공간을 가져간다 */
  flex-shrink: 1;  /* 공간이 부족하면 줄어든다 */
  flex-basis: 0%;  /* 콘텐츠 크기를 무시하고 비율로만 계산한다 */
}

flex-basis: 0%가 핵심입니다. 이게 auto가 아니라 0%이기 때문에 모든 아이템이 콘텐츠 크기와 관계없이 동일한 비율로 공간을 나눠 갖습니다.

flex: 1flex: auto의 차이가 헷갈리면 이렇게 기억하세요. flex: 1은 "비율로 나눠", flex: auto는 "콘텐츠 크기 존중하면서 나눠"입니다.


6. 실전 레이아웃 패턴

완벽한 가운데 정렬

CSS
/* 수직·수평 가운데 정렬 — 이 2줄이면 끝 */
.center {
  display: flex;
  justify-content: center; /* 주축 가운데 */
  align-items: center;     /* 교차축 가운데 */
}

네비게이션 바

CSS
/* 로고는 왼쪽, 메뉴는 오른쪽 */
.navbar {
  display: flex;
  justify-content: space-between; /* 양쪽 끝 배치 */
  align-items: center;            /* 세로 가운데 */
  padding: 0 24px;
}

Holy Grail Layout

헤더, 푸터, 사이드바 2개, 메인 콘텐츠로 구성된 클래식 레이아웃입니다.

CSS
/* 전체 구조 */
.page {
  display: flex;
  flex-direction: column;
  min-height: 100vh; /* 화면 꽉 채우기 */
}

/* 중간 영역: 사이드바 + 메인 */
.body {
  display: flex;
  flex: 1; /* 남은 세로 공간 모두 차지 */
}

.sidebar-left {
  flex: 0 0 200px;  /* 고정 너비 200px, 줄어들지 않음 */
}

.main {
  flex: 1; /* 남은 가로 공간 전부 */
}

.sidebar-right {
  flex: 0 0 200px;
}

카드 그리드 (Flex + Wrap)

CSS
/* 반응형 카드 목록 */
.card-grid {
  display: flex;
  flex-wrap: wrap;  /* 넘치면 줄바꿈 */
  gap: 16px;
}

.card {
  flex: 1 1 300px;  /* 최소 300px, 남으면 늘어남 */
}

카드 그리드는 Grid의 auto-fill/auto-fit이 더 깔끔한 경우가 많습니다. 하지만 아이템 수가 유동적이고 단순한 구조라면 Flex + Wrap으로도 충분합니다.


7. Flexbox vs Grid — 언제 무엇을 쓰는가

기준FlexboxGrid
차원1차원 (행 또는 열)2차원 (행과 열 동시)
적합한 상황네비게이션, 버튼 그룹, 카드 행페이지 전체 레이아웃, 대시보드
아이템 크기콘텐츠 기반으로 유연하게트랙(행/열) 기반으로 엄격하게
정렬한 축의 정렬이 핵심양 축 동시 정렬

둘은 대체 관계가 아닙니다. 실무에서는 페이지 전체 구조는 Grid, ** 컴포넌트 내부 정렬은 Flexbox**로 쓰는 경우가 많습니다.

간단한 판단 기준은 이렇습니다.

  • "한 줄(또는 한 열)에 아이템을 나열한다" → Flexbox
  • "행과 열을 동시에 정의해야 한다" → Grid
  • 잘 모르겠으면 → Flexbox부터 시도하고, 한계가 느껴지면 Grid로 전환

마무리

Flexbox를 정리하면서 느낀 건, 결국 ** 축 개념 **만 확실히 잡으면 나머지는 자연스럽게 따라온다는 것입니다.

  • justify-content는 주축, align-items는 교차축
  • flex-direction이 바뀌면 축도 바뀐다
  • flex: 1flex-grow:1 flex-shrink:1 flex-basis:0%
  • align-items는 줄 안, align-content는 줄 사이

이 네 가지만 기억하면, 대부분의 Flexbox 레이아웃은 머릿속에서 바로 그려질 겁니다.

댓글 로딩 중...