본문으로 건너뛰기

버그 제보

이상한 점을 알려 주시면 고칠게요. 지금 보고 있는 페이지와 브라우저 정보가 함께 담겨요.

페이지:

Frontend

Float와 Flexbox 레이아웃

작성 2026. 10. 21.추가
목차12개 항목
출처와 참고

Rachel Andrew의 「플렉스박스 플렉스 컨테이너를 만들 때 무슨 일이 벌어질까?」(2018.09, 웹액츄얼리 번역)과 곽빵의 「flexbox에서 가변요소의 자식 width가 부모 width를 넘을 때」(2021.09)를 바탕으로 재구성하고, web.dev 「Learn CSS」 강좌의 Layout·Flexbox 모듈로 보강한 노트다. float가 놓이는 규칙과 옛 float 해제법은 「Frontend_TIL: Float 배치되는 과정」·「float 해제 방법」·「float으로 레이아웃 구성 및 이해」을 CSS 명세로 확인해 보강했다.

블록은 위에서 아래로, 인라인은 왼쪽에서 오른쪽으로 쌓이는 Normal Flow만으로는 "옆으로 나란히", "가운데 정렬", "남은 공간 나눠 갖기" 같은 레이아웃을 만들기 어렵다. 예전에는 float로 이를 해결했고, 지금은 대부분 Flexbox와 Grid로 처리한다.

Float

박스를 왼쪽 또는 오른쪽으로 띄워, 텍스트 같은 인라인 요소들이 그 주변을 감싸도록(wrapping) 한다. 원래 신문처럼 이미지 옆에 글이 흐르게 하려고 만든 속성이다.

float 예시

.left { float: left; } /* none(기본) | left | right */
  • float된 박스는 display가 블록 계열로 바뀌고(inline이어도 width·height가 먹는다), 너비는 내용만큼 줄어든다.
  • 블록 박스는 float를 못 본 척 그 아래로 깔리지만, 그 안의 줄 상자(글)만 float를 피해 옆으로 흐른다. 그래서 float 뒤 블록의 배경은 float 밑으로 들어가고 글자만 비켜 선다.

float가 놓이는 규칙

float는 「같은 방향으로 가능한 한 위, 가능한 한 바깥쪽」에 놓이지만 몇 가지 제약을 받는다.

규칙결과
먼저 나온 float보다 위로 올라가지 못한다소스 순서가 뒤인 float의 윗변은 앞선 float의 윗변보다 높을 수 없다
남은 줄 폭에 안 들어가면 아래로 밀린다폭 300px 컨테이너에 100px float 다음 210px float가 오면 두 번째는 첫째 아래 줄로 내려간다
밀려난 자리 위로는 아무것도 못 간다위 예에서 뒤따르는 20px짜리 float: right는 첫 줄 오른쪽에 빈 공간이 있어도 그 줄로 못 올라가고, 밀려난 float와 같은 줄 오른쪽에 붙는다
왼쪽 float와 오른쪽 float는 서로 겹치지 않는다한 줄에 둘이 다 안 들어가면 뒤에 나온 쪽이 아래로 밀린다

float 배치 예

float로 여러 칸을 늘어놓으면 높이가 다른 칸 하나가 뒤의 칸들을 엉뚱한 줄에 걸리게 만든다. 칸 배치는 플렉스·그리드로 한다.

float를 부모 안에 담기

float된 요소는 Normal Flow에서 벗어나 떠 있기 때문에, 이후 요소가 그 밑으로 파고들거나 부모가 높이를 잃는다(float만 담은 부모의 높이는 0이다). 부모에 display: flow-root를 주면 부모가 새 블록 서식 맥락(BFC)을 만들어 float를 안에 담는다. 높이가 float까지 늘어나고, 바깥 요소와도 섞이지 않는다.

.media { display: flow-root; } /* 안의 float를 품는다 */
.after-float { clear: both; }   /* 특정 요소만 float 아래로 내릴 때 */

flow-root 전에는 여러 우회법을 썼다. 모두 부작용이 있어 새 코드에는 쓰지 않는다.

옛 방법원리부작용
부모도 floatfloat는 BFC를 만들어 자식 float를 감싼다부모가 흐름에서 빠지고 너비가 줄어 문제가 한 단계 위로 옮겨 간다
부모에 display: inline-blockinline-block도 BFC를 만든다부모가 내용 폭으로 줄고 인라인처럼 놓인다
부모에 overflow: hiddenvisible이 아닌 overflow가 BFC를 만든다밖으로 나가야 할 그림자·드롭다운이 잘린다
마지막 자식으로 빈 <div style="clear: both">빈 요소가 float 아래로 내려가며 부모를 늘린다의미 없는 마크업이 남는다
clearfix(::after)가상 요소를 빈 블록으로 만들어 clear동작은 맞지만 flow-root 한 줄로 대체된다
/* 과거의 clearfix. ::after는 기본이 인라인이라 clear가 먹지 않아 block으로 바꾸고, content가 있어야 생긴다 */
.clearfix::after { content: ""; display: block; clear: both; }

Flexbox·Grid 등장 이후 float로 레이아웃을 짜는 일은 크게 줄었고, 지금은 본래 용도(글 감싸기)에 주로 쓴다. BFC가 무엇이고 어떤 값이 만드는지는 박스 모델의 BFC에 있다.

Flexbox

Flexbox 구조

행 또는 열 한 방향으로 아이템을 배치하는 1차원 레이아웃 모델이다. 수동으로 값을 계산하지 않아도 정렬과 공간 분배를 해 준다.

  • Flex Container(부모): display: flex 또는 inline-flex를 준 요소
  • Flex Item(자식): 컨테이너 안의 박스들
  • main axis(주축): 아이템이 나열되는 방향 / cross axis(교차축): 주축에 수직인 방향

display: flex는 display: block flex의 줄임이다. 바깥쪽은 블록이라 컨테이너 자신은 일반 블록처럼 놓이고, 안쪽은 flex라 자식이 플렉스 아이템이 된다. inline-flex는 inline flex다. 아무것도 더 쓰지 않으면 아래 표의 초깃값이 동작한다.

컨테이너 속성

분류속성초깃값설명
배치flex-directionrow주축 방향: row, row-reverse, column, column-reverse. row는 인라인 방향이라 가로쓰기·왼→오면 왼쪽부터, RTL이면 오른쪽부터, 세로쓰기면 위부터 나열된다
배치flex-wrapnowrap넘칠 때 줄바꿈 여부. nowrap은 한 줄에 욱여넣는다
배치flex-flowflex-direction과 flex-wrap의 축약. 예: flex-flow: row wrap;
공간 나누기justify-contentflex-start주축 방향 공간 분배. 남는 공간이 없으면 어떤 값이든 효과가 없다
공간 나누기align-contentnormal교차축 방향 공간 분배(여러 줄일 때만 의미 있음)
정렬align-itemsstretch모든 아이템을 교차축 기준으로 정렬. 기본은 가장 높은 아이템에 맞춰 모두 늘어난다
간격gap (row-gap, column-gap)normal(0)아이템 사이 간격. 모든 주요 브라우저가 플렉스에서도 지원해, 아이템마다 margin을 주고 마지막 것만 빼는 우회가 필요 없다

flex-direction

row-reverse, column-reverse는 순서를 바꾸는 것이 아니라 주축의 시작점(main-start)을 반대편으로 옮길 뿐이다. 화면 순서가 HTML 선언 순서와 달라져도 스크린 리더·키보드 탐색 순서는 소스 그대로라 어긋날 수 있다(접근성 주의). 행과 열이 쓰기 모드를 따르는 원리는 쓰기 모드와 논리적 속성에 있다.

justify-content / align-content 값

justify-content

값의미
flex-start / flex-end축의 시작점 / 끝으로 모음
center가운데로 모음
space-between양 끝을 붙이고 아이템 사이 간격을 균등하게
space-around각 아이템 둘레에 같은 여백(양 끝 여백은 사이 간격의 절반)
space-evenly양 끝을 포함한 모든 간격을 균등하게

align-items / align-self 값

align-items

값의미
stretch교차축 방향으로 컨테이너를 가득 채움
flex-start / flex-end교차축의 시작 / 끝
center가운데
baseline텍스트 기준선을 맞춤

아이템 속성

속성초깃값설명
align-selfauto개별 아이템의 교차축 정렬. 컨테이너가 아니라 아이템에 준다
flex-grow0남은 공간을 나눠 받는 비율. 초깃값이면 공간이 남아도 커지지 않는다
flex-shrink1공간이 부족할 때 줄어드는 비율. 초깃값이면 모자랄 때 줄어든다
flex-basisauto늘거나 줄기 전의 기본 크기. 크기를 정하지 않았으면 max-content(한 줄로 늘였을 때 크기)에서 출발
flex0 1 autogrow shrink basis 축약. 예: flex: 1(= 1 1 0%)
order0배치 순서(작을수록 앞). HTML 순서는 그대로다

flex 축약형에는 자주 쓰는 키워드가 있다.

값풀어 쓰면결과
flex: initial0 1 auto초깃값. 늘지 않고, 모자라면 줄어든다
flex: auto1 1 auto내용 크기(max-content)로 먼저 놓은 뒤 남는 공간을 나눈다. 내용이 큰 아이템이 더 커진다
flex: 11 1 0%기본 크기를 0으로 보고 전체 공간을 나눈다. 내용과 상관없이 같은 크기
flex: none0 0 auto늘지도 줄지도 않는다. 정렬 기능만 쓰고 싶을 때
flex: 22 1 0%다른 아이템이 flex: 1이면 두 배를 받는다. 1·2·3이면 공간을 6으로 나눠 1:2:3

줄어들 때는 flex-shrink 값에 기본 크기를 곱한 비율로 줄어든다. 내용이 많은 아이템은 덜 줄고 짧은 아이템이 먼저 사라지지 않는다. 그래서 따로 크기를 주지 않아도 내용에 비례해 공간을 나눠 갖는다. 이것이 플렉스박스를 쓰는 핵심 이유다.

플렉스 줄과 정렬

  • flex-wrap: wrap으로 줄이 여러 개가 되면 각 줄이 독립된 플렉스 컨테이너처럼 공간을 나눈다. 2번째 줄 아이템을 1번째 줄 아이템과 세로로 맞출 수 없다. 이것이 플렉스박스가 1차원인 이유이고, 행·열을 함께 맞추려면 그리드를 쓴다.
  • 감싸기 전 아이템은 min-content 크기까지 줄어든 뒤에야 넘친다.
  • align-content의 초깃값은 normal(플렉스에서는 stretch처럼 동작)이라 여러 줄이 컨테이너 높이를 나눠 갖는다. 남는 공간이 있어야 효과가 있으니 컨테이너에 block-size를 준다.
  • place-content: <align-content> <justify-content>는 두 값을 한 줄로 쓴다. 값 하나면 둘 다 같다.
  • flex-direction: column에서 justify-content가 동작하려면 컨테이너에 높이가 있어야 한다.
  • 플렉스에는 justify-self가 없다. 주축에서 아이템은 묶음으로 움직이기 때문이다. 그룹을 나누려면 auto margin을 쓴다. auto margin은 그 방향의 남는 공간을 모두 흡수한다.
.toolbar { display: flex; gap: .5rem; }
.toolbar .spacer-start { margin-inline-start: auto; }   /* 이 아이템부터 오른쪽(인라인 끝)으로 밀린다 */

자주 쓰는 패턴: 가운데 정렬

.center {
  display: flex;
  justify-content: center; /* 가로(주축) */
  align-items: center;     /* 세로(교차축) */
}

박스 정렬 속성은 플렉스 전용이 아니다. 블록 컨테이너에서도 align-content: center로 자식을 세로 가운데 놓을 수 있다. Chrome 123, Firefox 125, Safari 17.4부터 모든 주요 브라우저가 지원한다(Baseline 2024). 과거에는 정렬만 하려고 컨테이너를 플렉스로 바꿨다.

.hero { min-block-size: 50vh; align-content: center; }   /* display: flex 없이 세로 가운데 */

함정: flex 아이템이 컨테이너를 넘칠 때

flex: 1인 아이템 안에 긴 한 줄 텍스트를 넣고 말줄임(…)을 걸었는데, 말줄임은 안 되고 텍스트가 컨테이너 밖으로 삐져나오는 경우가 있다.

.container { display: flex; width: 540px; }
.col-1 { width: 200px; }
.col-2 {
  flex: 1;
  min-width: 0;                /* 이 줄이 없으면 아래 말줄임이 동작하지 않는다 */
}
.col-2 p {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
증상원인해결
overflow: hidden·text-overflow: ellipsis가 안 먹고 아이템이 컨테이너보다 넓어진다flex 아이템의 min-width 기본값이 0이 아니라 auto다. auto는 "콘텐츠 최소 크기보다 작아지지 않는다"는 뜻이라, 줄바꿈 없는 긴 텍스트만큼 아이템이 늘어난다. 이 최소 크기 제약이 flex-basis보다 우선한다아이템에 min-width: 0

말줄임이 안 된다 = 요소 너비가 제한되지 않았다는 신호다. 다른 해법과 비교하면 다음과 같다.

방법평가
아이템에 min-width: 0권장. 원인을 직접 고친다
컨테이너에 overflow: hidden넘친 부분을 잘라 숨길 뿐이라 의도와 다른 결과가 나올 수 있다
아이템에 max-width: calc(100vw - 고정폭)레이아웃 수치에 묶여 깨지기 쉽다. 비추천

flex-direction: column이면 같은 문제가 높이에서 생기므로 min-height: 0을 준다.

다단 레이아웃

국가 목록처럼 긴 목록이나 신문 기사처럼 글이 한 단에서 다음 단으로 이어 흘러야 하면 다단(multicolumn) 레이아웃을 쓴다. 플렉스·그리드로 만든 열은 서로 독립된 칸이라 내용이 이어 흐르지 않는다.

.countries { column-count: 2; column-gap: 1em; }       /* 단 개수 고정 */
.articles  { column-width: 260px; column-gap: 1em; }   /* 최소 단 너비: 공간에 따라 단 수가 자동으로 바뀐다 */
.articles h2 { column-span: all; }                     /* 제목은 모든 단을 가로질러 */
.articles figure { break-inside: avoid; }              /* 그림이 단 사이에서 쪼개지지 않게 */

column-width만 주면 미디어 쿼리 없이 반응형이 된다. 단 높이가 화면보다 길면 독자가 위아래로 오가며 읽어야 하므로, 화면에서는 짧은 목록에 주로 쓴다.

Flexbox와 Grid

Flexbox는 한 방향, Grid는 행과 열을 동시에 다룬다. 어느 쪽을 고를지는 CSS 그리드 레이아웃의 선택 기준표에 있다. React Native의 레이아웃도 Flexbox 기반이다(React Native 스타일링 참고).

핵심 정리

  • float는 Normal Flow를 벗어난다. 부모에 display: flow-root를 주면 float를 담는다. 레이아웃 용도로는 거의 쓰지 않는다.
  • Flexbox: 부모에 display: flex, 주축은 justify-content, 교차축은 align-items.
  • align-self, flex-grow, order는 아이템에 주는 속성이다.
  • 초깃값만으로도 아이템은 내용에 비례해 공간을 나눠 갖는다. -reverse는 시작점만 옮긴다.
  • 같은 크기로 나누려면 flex: 1, 내용 크기 기반이면 flex: auto, 고정이면 flex: none.
  • 아이템 하나를 떼어 놓을 땐 justify-self 대신 auto margin.
  • 글을 여러 단으로 이어 흘리려면 다단 레이아웃(column-width).
  • flex 아이템의 min-width 기본값은 auto다. 긴 텍스트 때문에 아이템이 넘치면 min-width: 0을 준다.

관련 노트: CSS 그리드 레이아웃, 쓰기 모드와 논리적 속성, 박스 모델과 display, CSS 오버플로와 스크롤: 넘침 처리와 스크롤 스냅, CSS가 동작하는 방식: 핵심 개념과 픽셀이 되기까지, DevTools로 DOM과 CSS 다루기

더 읽을거리: 게임과 실험 도구로 익히기, Flexbox Froggy, flexbox.help(속성을 바꿔 보며 결과 확인)

원본 기록10

댓글

GitHub 계정으로 댓글을 남길 수 있어요.