Skip to main content

요소 하나를 원하는 자리에 콕 찍어 두는 데 position이 강하다면, 여러 요소를 한 줄이나 한 칸에 가지런히 정리하는 데는 Flexbox가 정말 강합니다. 버튼 여러 개를 나란히 놓고 싶을 때, 카드 목록을 일정한 간격으로 정리하고 싶을 때, 로고와 메뉴를 한 줄에 배치하고 싶을 때, 많은 사람이 가장 먼저 떠올리는 도구가 바로 Flexbox입니다.

이 장의 목표는 어렵지 않습니다. 부모가 Flexbox가 되면 자식들이 어떻게 배치되는지, 그리고 주축과 교차축을 기준으로 어떻게 정렬하는지를 익히는 것입니다. 이 감각만 잡아도 실무에서 자주 보는 화면의 절반쯤은 만들 수 있습니다.1

실무에서 가장 많이 쓰는 배치: Flexbox 입문

Flexbox는 무엇을 잘할까

Flexbox는 한 번에 한 방향, 즉 한 줄 또는 한 열을 다루는 레이아웃 방식입니다. 그래서 메뉴 바, 버튼 묶음, 카드 목록, 태그 리스트처럼 "한 방향으로 정리한 뒤 필요하면 줄바꿈하는" UI에 아주 잘 어울립니다. 큰 페이지 전체를 정교한 행과 열로 나누는 데는 Grid가 더 어울리는 경우가 많지만, 화면 안의 작은 구성 요소를 다듬을 때는 Flexbox가 훨씬 빠르고 직관적입니다.1

쉽게 말해 Flexbox는 줄 세우기 전문가입니다. 반 아이들을 운동장에 세우는 선생님처럼, 누구를 앞에 둘지, 얼마나 띄울지, 가운데로 모을지, 양쪽 끝으로 벌릴지 정해 줍니다.

가장 먼저 알아야 할 두 주인공: flex container와 flex item

Flexbox는 부모와 자식의 역할이 분명합니다.

  • display: flex를 준 부모는 flex container입니다.

  • 그 부모의 직계 자식은 flex item이 됩니다.

  • 손자 요소는 자동으로 flex item이 되지 않습니다.2

예를 들어 다음 코드에서 .list는 컨테이너이고, 그 안의 .card 세 개는 아이템입니다.

<section class="list">
  <article class="card">카드 1</article>
  <article class="card">카드 2</article>
  <article class="card">카드 3</article>
</section>
.list {
  display: flex;
}

이 한 줄만 써도 자식들이 갑자기 옆으로 서기 시작합니다. 처음 보면 "벌써 된다고?" 싶은데, 진짜 됩니다.

Flexbox는 축부터 생각하면 쉽다

Flexbox를 배울 때 가장 중요한 말은 left, right, top, bottom보다 주축과 교차축입니다.

flex-direction: row라면 주축은 가로 방향이고, 교차축은 세로 방향입니다. 반대로 flex-direction: column이라면 주축은 세로 방향, 교차축은 가로 방향이 됩니다.2

이 말을 꼭 기억해 두세요.

  • justify-content는 주축 방향 정렬

  • align-items는 교차축 방향 정렬

처음엔 헷갈리지만, "가로 정렬 속성"이라고 외우지 말고 "주축 정렬 속성"이라고 기억하면 방향이 바뀌어도 덜 헷갈립니다.

display: flex만 주면 기본적으로 무슨 일이 벌어질까

아무 설정 없이 부모에 display: flex만 주면 브라우저는 기본값으로 동작합니다.

  • 아이템은 한 줄에 놓이려고 합니다. flex-wrap의 기본값은 nowrap입니다.

  • 방향은 가로줄입니다. flex-direction의 기본값은 row입니다.

  • 남는 공간이 있어도 아이템이 저절로 커지지는 않습니다. 대신 공간이 부족하면 줄어들 수 있습니다.

  • 교차축 방향에서는 기본적으로 stretch가 적용되어 같은 줄의 아이템 높이가 비슷하게 보일 수 있습니다.2

즉, display: flex를 주는 순간 브라우저는 "좋아, 일단 한 줄로 세워 볼게"라고 반응합니다.

가장 많이 쓰는 컨테이너 속성들

이제 실전에서 가장 자주 만나는 속성을 하나씩 보겠습니다.

flex-direction: 가로로 놓을까, 세로로 놓을까

기본값은 row입니다.

.container {
  display: flex;
  flex-direction: row;
}

이렇게 하면 아이템이 가로로 놓입니다. 반대로 세로로 쌓고 싶다면 이렇게 씁니다.

.container {
  display: flex;
  flex-direction: column;
}

여기서 중요한 변화가 하나 있습니다. column이 되면 주축이 세로로 바뀌기 때문에, justify-content는 위아래 정렬에, align-items는 좌우 정렬에 관여하게 됩니다.2

이 부분에서 많은 초보자가 "왜 갑자기 가운데 정렬 방향이 바뀌었지?" 하고 놀랍니다. 원인은 대부분 축이 바뀌었기 때문입니다.

flex-wrap: 한 줄 고집을 꺾을 것인가

Flexbox는 기본적으로 "어떻게든 한 줄에 넣어 보겠다"는 성격이 있습니다. 카드가 많아지면 이 고집이 문제를 만듭니다. 이때 flex-wrap: wrap을 주면 됩니다.2

.container {
  display: flex;
  flex-wrap: wrap;
}

이제 공간이 부족하면 다음 줄로 내려갑니다. 카드 목록, 태그 목록, 썸네일 목록에서 거의 필수처럼 쓰는 속성입니다.

justify-content: 주축에서 어떻게 나눌까

justify-content는 아이템 묶음을 주축에서 어떻게 배치할지 정합니다.13

.container {
  display: flex;
  justify-content: center;
}

자주 쓰는 값은 다음과 같습니다.

  • flex-start: 시작점으로 모읍니다.

  • center: 가운데로 모읍니다.

  • flex-end: 끝으로 모읍니다.

  • space-between: 첫 아이템은 시작, 마지막 아이템은 끝에 붙이고 사이 공간을 벌립니다.

  • space-around: 아이템 주변에 여백을 둡니다.

  • space-evenly: 모든 간격을 최대한 고르게 맞춥니다.

여기서 실무 팁 하나. 일정한 간격이 필요하면 gap, 남는 공간을 분배하고 싶으면 justify-content를 먼저 떠올리면 좋습니다.

align-items: 교차축에서 어떻게 맞출까

align-items는 교차축 정렬을 담당합니다.3

.container {
  display: flex;
  align-items: center;
}

대표 값은 다음과 같습니다.

  • stretch: 기본값입니다. 교차축 방향으로 늘어납니다.

  • flex-start: 시작점으로 붙습니다.

  • center: 가운데에 맞춥니다.

  • flex-end: 끝점으로 붙습니다.

  • baseline: 글자 기준선을 맞춥니다.

버튼 높이가 서로 다르거나, 아이콘과 텍스트를 한 줄에서 반듯하게 맞추고 싶을 때 자주 씁니다.

gap: 이제는 margin 꼼수보다 이쪽

아이템 사이 간격을 만들고 싶다면 gap이 훨씬 깔끔합니다.34

.container {
  display: flex;
  gap: 20px;
}

이렇게 하면 아이템 사이에만 20px 간격이 생깁니다. 바깥쪽 가장자리까지 밀어내지 않아서 다루기 쉽습니다.

gap과 space-between은 비슷해 보여도 다릅니다. gap은 고정된 간격, space-between은 남는 공간 배분입니다. 이 차이를 알면 레이아웃이 훨씬 덜 흔들립니다.

align-content: 여러 줄일 때만 등장하는 조연

align-content는 자주 헷갈리는 속성입니다. 이 속성은 줄이 여러 개일 때, 즉 flex-wrap: wrap이 켜져 있고 실제로 여러 줄이 생겼을 때만 의미가 있습니다.3

.container {
  display: flex;
  flex-wrap: wrap;
  align-content: space-between;
}

한 줄뿐이라면 거의 아무 일도 일어나지 않습니다. 그래서 align-content가 안 먹는다고 느낄 때는 먼저 "지금 진짜 여러 줄인가?"를 확인하면 됩니다.

아이템 쪽에서 자주 쓰는 속성

이제 부모가 아니라 자식, 즉 flex item에 주는 속성을 보겠습니다.

flex: 아이템 크기를 다루는 가장 실용적인 방법

실무에서는 flex-grow, flex-shrink, flex-basis를 하나씩 따로 쓰기보다 flex 축약형을 자주 씁니다.

.card {
  flex: 1 1 240px;
}

이 한 줄은 이렇게 읽으면 됩니다.

  • 1: 남는 공간이 있으면 커질 수 있다.

  • 1: 공간이 부족하면 줄어들 수 있다.

  • 240px: 시작 크기는 240px쯤으로 본다.

카드 목록에서 특히 유용합니다. 화면이 넓으면 카드가 한 줄에 여러 개 들어가고, 좁아지면 줄바꿈되면서 자연스럽게 재배치됩니다.

초보자라면 우선 이 패턴 하나만 기억해도 좋습니다.

.item {
  flex: 1 1 200px;
}

정말 많이 씁니다.

align-self: 나만 살짝 다르게

컨테이너 전체에는 align-items를 주고, 특정 아이템만 다르게 놓고 싶다면 align-self를 씁니다.3

.item-special {
  align-self: flex-end;
}

반 친구들은 다 똑바로 서 있는데, 한 명만 살짝 아래로 내리는 느낌이라고 생각하면 쉽습니다.

실전 예제 1: 카드 목록 만들기

이제 Flexbox의 대표 메뉴인 카드 목록을 만들어 보겠습니다. 목표는 이렇습니다. 화면이 넓을 때는 카드가 여러 개 한 줄에 놓이고, 좁아지면 다음 줄로 자연스럽게 넘어가게 만드는 것입니다.

HTML

<section class="card-list">
  <article class="card">
    <h3>HTML 기초</h3>
    <p>웹 페이지의 뼈대를 만드는 법을 배웁니다.</p>
    <a href="#">자세히 보기</a>
  </article>

  <article class="card">
    <h3>CSS 기초</h3>
    <p>색, 글꼴, 여백, 배치까지 차근차근 익힙니다.</p>
    <a href="#">자세히 보기</a>
  </article>

  <article class="card">
    <h3>Flexbox 입문</h3>
    <p>카드 목록과 내비게이션을 직접 만들어 봅니다.</p>
    <a href="#">자세히 보기</a>
  </article>

  <article class="card">
    <h3>반응형 입문</h3>
    <p>화면 크기에 따라 레이아웃이 바뀌는 원리를 익힙니다.</p>
    <a href="#">자세히 보기</a>
  </article>
</section>

CSS

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family: sans-serif;
  background: #f8fafc;
}

.card-list {
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
  padding: 24px;
}

.card {
  flex: 1 1 240px;
  display: flex;
  flex-direction: column;
  padding: 20px;
  border: 1px solid #dbe2ea;
  border-radius: 16px;
  background: white;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.06);
}

.card h3 {
  margin-top: 0;
  margin-bottom: 12px;
}

.card p {
  margin-top: 0;
  margin-bottom: 16px;
  color: #475569;
  line-height: 1.6;
  flex: 1;
}

.card a {
  align-self: flex-start;
  padding: 10px 14px;
  border-radius: 10px;
  background: #2563eb;
  color: white;
  text-decoration: none;
}

이 예제에서 핵심은 세 군데입니다.

첫째, .card-list에 display: flex, flex-wrap: wrap, gap: 20px를 줬습니다. 그래서 카드가 줄바꿈되며 일정한 간격을 유지합니다.

둘째, .card에 flex: 1 1 240px를 줬습니다. 카드 하나의 시작 폭을 240px 정도로 잡고, 남는 공간이 있으면 커지고 부족하면 줄어들게 한 것입니다.

셋째, .card 자체도 다시 display: flex; flex-direction: column;을 사용했습니다. 그러면 카드 안에서도 제목, 설명, 버튼을 세로로 다룰 수 있습니다. 게다가 설명 문단에 flex: 1을 주면 버튼이 아래쪽으로 정리되어 카드 높이가 달라도 구조가 비교적 안정적으로 보입니다.

Flexbox는 이렇게 큰 줄에도 쓰고, 카드 내부의 작은 줄에도 다시 쓸 수 있습니다. 한 요소가 어떤 곳에서는 item이고, 또 다른 곳에서는 container가 될 수도 있다는 뜻입니다.

실전 예제 2: 내비게이션 바 만들기

이번에는 실무에서 정말 자주 만드는 상단 내비게이션입니다. 로고는 왼쪽, 메뉴와 로그인 버튼은 오른쪽으로 보내 보겠습니다.

HTML

<nav class="site-nav">
  <a href="#" class="logo">CSS 기초</a>

  <ul class="menu">
    <li><a href="#">소개</a></li>
    <li><a href="#">강의</a></li>
    <li><a href="#">예제</a></li>
  </ul>

  <a href="#" class="login">로그인</a>
</nav>

CSS

.site-nav {
  display: flex;
  align-items: center;
  gap: 24px;
  padding: 16px 24px;
  background: #0f172a;
}

.logo {
  color: white;
  text-decoration: none;
  font-weight: 700;
}

.menu {
  display: flex;
  gap: 16px;
  margin-left: auto;
  margin-top: 0;
  margin-bottom: 0;
  padding-left: 0;
  list-style: none;
}

.menu a,
.login {
  color: white;
  text-decoration: none;
}

.login {
  padding: 8px 12px;
  border: 1px solid rgba(255, 255, 255, 0.3);
  border-radius: 10px;
}

여기서 눈여겨볼 부분은 .menu { margin-left: auto; }입니다. 이 한 줄이 남는 공간을 먹어치우면서 메뉴 묶음을 오른쪽으로 밀어 줍니다.4

많은 초보자가 이런 순간에 "왜 justify-self는 없지?"라고 묻는데, Flexbox는 주축에서 아이템을 개별적으로 움직이기보다 그룹으로 분배하는 성격이 강합니다. 그래서 특정 아이템 하나를 밀어내고 싶을 때는 margin-left: auto 같은 패턴이 아주 실용적입니다.4

가운데 정렬은 이렇게 생각하면 쉽다

Flexbox를 배우는 사람에게 가장 인기 있는 패턴은 역시 정가운데 정렬입니다.

.center-box {
  display: flex;
  justify-content: center;
  align-items: center;
}

이 코드는 주축과 교차축 양쪽 모두 가운데로 맞춥니다.3

다만 한 가지 기억할 점이 있습니다. align-items는 교차축에 남는 공간이 있어야 체감이 잘 됩니다. 세로 가운데 정렬이 안 되는 것처럼 보인다면, 부모 높이가 너무 작거나 사실상 여유 공간이 없는 경우가 많습니다.

초보자가 자주 헷갈리는 포인트

Flexbox는 편하지만, 처음엔 비슷해 보이는 속성들 때문에 머리가 조금 꼬일 수 있습니다. 특히 아래는 꼭 구분해 두는 편이 좋습니다.

  • justify-content는 아이템 전체 묶음을 주축에서 정렬합니다.

  • align-items는 각 아이템을 교차축에서 정렬합니다.

  • align-content는 여러 줄 자체를 교차축에서 정렬합니다.3

그리고 이 문장도 꼭 기억해 두세요.

row일 때의 가로/세로 감각을 column에서도 그대로 들고 가면 거의 반드시 헷갈립니다.

방향이 바뀌면 정렬 속성이 담당하는 축도 함께 바뀝니다.

디버깅할 때 확인할 것들

Flexbox가 마음처럼 안 움직일 때는 속성만 바꿔 가며 기도하기보다, 아래 질문을 순서대로 던지는 편이 낫습니다.

  • 부모에 정말 display: flex가 들어갔는가?

  • 정렬하려는 대상이 부모의 직계 자식인가?

  • 지금 flex-direction이 row인가 column인가?

  • 간격이 필요한데 space-between을 쓴 건 아닌가, 아니면 고정 간격이 필요한데 gap을 빼먹은 건 아닌가?

  • align-content를 썼는데 실제로 여러 줄이 없는 건 아닌가?

  • 긴 텍스트 때문에 아이템이 줄지 않는다면 min-width: 0이 필요한 구조는 아닌가?5

특히 마지막 항목은 실무에서 은근히 자주 만납니다. Flex item 안에 긴 텍스트나 긴 URL이 있을 때, 생각보다 안 줄어드는 문제가 생기면 min-width: 0을 떠올려 보세요.5

일단 이 세 패턴부터 손에 익히자

처음부터 모든 속성을 다 외울 필요는 없습니다. 아래 세 패턴만 익혀도 당장 쓸 곳이 많습니다.

  • 한 줄 정렬: display: flex; align-items: center; gap: 12px;

  • 카드 목록: display: flex; flex-wrap: wrap; gap: 20px; + flex: 1 1 240px;

  • 정가운데 배치: display: flex; justify-content: center; align-items: center;

이 셋은 Flexbox의 기본 체력 같은 조합입니다.

마무리

Flexbox를 어렵게 만드는 건 속성 수보다도 축을 바꿔 생각하는 습관입니다. 하지만 한 번 감이 잡히면 정말 든든합니다.

부모를 flex container로 만들고, 자식을 flex item으로 바라보세요. 그다음에는 이렇게 묻으면 됩니다.

"주축은 어디지?"

"교차축은 어디지?"

"나는 아이템을 모으고 싶은 걸까, 벌리고 싶은 걸까, 가운데 맞추고 싶은 걸까?"

이 질문들에 답할 수 있으면 Flexbox는 더 이상 낯선 문법이 아니라, 화면을 정리하는 아주 믿음직한 도구가 됩니다.

다음 장에서는 이 감각을 바탕으로, 화면 크기가 달라져도 자연스럽게 대응하는 반응형의 첫걸음을 이어서 살펴보겠습니다.

참고

1A Complete Guide to CSS Flexbox | CSS-Tricks

2Basic concepts of flexbox - CSS | MDN

3Aligning items in a flex container | MDN

4Box alignment in flexbox - CSS | MDN

5CSS Flexbox Guide - Properties & Patterns | design.dev

책 목록

처음부터 끝까지 배우는 CSS 기초

전체 목차 보기

이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.

틸노트는 사용자가 AI를 활용해 노트를 쉽게 작성하거나 편집할 수 있는 서비스입니다.

AI 스퀘어 단톡방에 함께해요

AI 활용과 틸노트 사용 팁을 나누는 오픈채팅방입니다. 입장 비밀번호는 tilnote입니다.

참여하기