display: flex가 하는 일
부모에 display: flex를 주면 직계 자식들이 한 행에 나란히 서고 같은 높이로 늘어나요. 이 선언 하나가 여러 항목을 한 줄로 배치할 때 쓰던 float, inline-block, 대부분의 위치 지정 트릭을 대신해요.
첫 번째 상자에서는 각 div가 한 줄씩 차지해요. 두 번째 상자에서는 세 항목이 한 행에 놓이고, 각각 콘텐츠만큼만 넓으며, One과 Three가 Two의 높이로 늘어나요. 바뀐 건 부모뿐이에요. display: flex는 항목이 아니라 컨테이너에 줘요.
Flexbox 플레이그라운드
값을 고르고 항목이 움직이는 걸 보세요. 상자 아래의 코드 줄은 보이는 모습의 CSS이고, 그 아래 목록은 브라우저가 실제로 각 항목을 놓은 위치를 getBoundingClientRect()로 측정한 값이에요.
해 볼 것들이에요.
justify-content: space-between: 항목 1이 x 0에 놓이고 항목 3이 반대쪽 가장자리에 닿아요.align-items: center: 세 항목이 자기 높이를 유지하고 중심이 한 줄로 맞춰져요. 기본값stretch에서는 자기 높이가 없는 항목 1이 컨테이너의 170px 전체를 채워요.flex-direction: column: 이제justify-content는 항목을 세로로,align-items는 가로로 움직여요. 두 속성은 화면이 아니라 축을 따라가요.
주축과 교차축
모든 flex 컨테이너에는 축이 두 개 있어요. 주축은 항목이 흐르는 방향이고, flex-direction으로 정해요. 교차축은 그 방향을 가로질러요.
flex-direction | 주축 | 교차축 | justify-content가 항목을 움직이는 방향 | align-items가 항목을 움직이는 방향 |
|---|---|---|---|---|
row (기본값) | 가로, 읽는 방향 | 세로 | 가로 | 세로 |
row-reverse | 가로, 반대로 | 세로 | 가로 | 세로 |
column | 세로, 위에서 아래로 | 가로 | 세로 | 가로 |
column-reverse | 세로, 아래에서 위로 | 가로 | 세로 | 가로 |
오른쪽에서 왼쪽으로 쓰는 페이지(dir="rtl")에서는 주축이 글쓰기 방향을 따르기 때문에 row가 오른쪽 가장자리에서 시작해요.
컨테이너 속성
display: flex가 있는 요소에 줘요.
| 속성 | 하는 일 | 기본값 |
|---|---|---|
flex-direction | 행인지 열인지, 그리고 방향 | row |
flex-wrap | 공간이 모자랄 때 항목이 새 줄로 넘어가는지 | nowrap |
flex-flow | flex-direction과 flex-wrap의 단축 속성 | row nowrap |
justify-content | 주축을 따른 배치와 간격 | normal (flex-start처럼 동작) |
align-items | 교차축을 따른 항목 배치 | normal (stretch처럼 동작) |
align-content | 항목이 줄바꿈될 때 줄 사이 간격 | normal |
gap | 항목 사이 공간(row-gap, column-gap) | 0 |
justify-content와 align-items는 모든 값을 담은 페이지가 따로 있어요. justify-content와 align-items를 보세요. 줄바꿈과 방향은 flex-wrap 페이지에 있어요.
항목 속성: flex-grow, flex-shrink, flex-basis
자식에 주는 속성이고, 컨테이너의 너비를 어떻게 나눌지 정해요.
flex-basis: 빈 공간을 나누기 전에 항목이 시작하는 크기예요(auto는 "내 width나 콘텐츠를 써"라는 뜻이에요).flex-grow: 남는 공간을 몇 몫 가져갈지예요.0이면 절대 커지지 않아요.flex-shrink: 공간이 모자랄 때 얼마나 양보할지예요.0이면 절대 줄어들지 않아요.
flex 단축 속성은 셋을 모두 지정해요.
| 쓰는 값 | 뜻 | 동작 |
|---|---|---|
flex: initial (기본값) | 0 1 auto | 콘텐츠 크기, 줄어들 수 있고, 커지지 않아요 |
flex: 1 | 1 1 0% | 0에서 시작해 공간을 똑같이 나눠 가져요 |
flex: auto | 1 1 auto | 콘텐츠 크기에 남는 공간의 몫을 더해요 |
flex: none | 0 0 auto | 정확히 콘텐츠나 width만큼, 커지지도 줄어들지도 않아요 |
flex: 2 | 2 1 0% | flex: 1인 항목의 두 배 몫 |
각 행은 padding 안쪽 너비가 480px이고, 8px 간격 두 개를 빼면 항목에 464px이 남아요. 출력된 너비를 보면 어떻게 나뉘었는지 알 수 있어요. 첫 번째 행은 각각 약 155px, 두 번째 행은 116 + 232 + 116(flex: 2 항목이 이웃 각각의 두 배 몫을 가져가요), 세 번째 행은 100 + 372로 flex: none 항목이 자기 너비를 유지하고 다른 항목이 나머지를 모두 가져가요.
flexbox로 만드는 실제 레이아웃
시작 쪽에 로고, 끝 쪽에 링크가 있는 헤더가 가장 흔한 flex 레이아웃이에요. 한 항목에 margin-inline-start: auto를 주면 그 앞의 빈 공간을 모두 차지해서, 그 항목과 뒤의 모든 것을 끝으로 밀어요.
align-items: center가 더 큰 로고 텍스트와 링크를 한 줄로 맞추고, gap은 마지막 링크에서 되돌릴 margin 없이 링크 사이 간격을 줘요.
두 번째로 흔한 패턴은 미디어 행이에요. 고정 크기 아이콘과, 나머지를 차지하면서 자기 열 안에서 줄바꿈되는 텍스트예요.
flex: none이 보장해 줘요. flex 항목은 기본적으로 줄어들기 때문에, 원에서 이걸 지우고 텍스트에서 flex: 1도 지우면 문단이 원을 약 23px 너비로 짓눌러서 타원이 돼요. 상자 안에서 무언가 하나를 가운데 정렬하는 건 flexbox로 두 줄이에요(justify-content: center; align-items: center). div 가운데 정렬 페이지에서 다른 방법들과 비교해요.
order와 접근성
order 속성과 -reverse 방향은 항목이 HTML 안에 있는 위치가 아니라 보이는 위치를 바꿔요. 스크린 리더와 Tab 키는 여전히 HTML 순서를 따르기 때문에, 키보드 사용자는 화면의 첫 링크에서 마지막 링크로 건너뛰게 될 수 있어요. 장식용으로만 쓰고, 순서에 의미가 있다면 HTML을 바꾸세요.
flexbox와 grid
flexbox는 한 번에 한 방향으로 동작해요. 한 행의 항목은 콘텐츠로 크기가 정해지고, 두 번째 줄(줄바꿈 시)은 첫 줄과 맞춰지지 않아요. grid는 두 방향으로 동작해요. 열과 행을 정의하면 항목이 거기에 맞춰 들어가요. 내비게이션 바, 툴바, 폼 행은 flexbox이고, 갤러리나 사이드바가 있는 페이지는 grid예요.
자주 하는 실수
- 부모가 아니라 항목에
display: flex주기. 컨테이너가display: flex를 받고, 그 자식이 항목이에요. - 높이 없이
align-items가 항목을 세로로 움직일 거라고 기대하기. 컨테이너가 가장 높은 항목만큼만 높으면 움직일 공간이 없어요. 컨테이너에 높이를 주세요. 아니면 동작은 하는데 할 일이 없는 거예요. - 줄어들지 않고 넘치는 텍스트. flex 항목은
min-width: auto예요. 줄어들어야 하는 항목에min-width: 0을 추가하세요. flex: 1을 썼는데 너비가 다른 이유 궁금해하기.flex: 1항목은 공간을 똑같이 나누지만, 콘텐츠(긴 URL, 이미지)가 들어가지 않는 항목은 그보다 작게 줄어들지 않아요. 이것도min-width: 0으로 해결돼요.- 항목 사이에 margin. 컨테이너에
gap을 쓰세요. 항목 사이에만 들어가요. - 불필요한 flex 중첩. 직계 자식만 flex 항목이에요. 손자 요소는 자기 부모도
display: flex가 아니면 일반 레이아웃을 따라요.
자주 묻는 질문
display: flex는 무엇을 하나요?
요소를 flex 컨테이너로 만들어요. 직계 자식은 flex 항목이 되어 기본적으로 한 행에 나란히 놓이고, 같은 높이로 늘어나며, justify-content, align-items, gap 같은 속성이 공간을 나누는 방식을 제어해요.
flex: 1은 무슨 뜻인가요?
flex: 1은 flex: 1 1 0%의 줄임이에요. 항목이 커질 수 있고(flex-grow: 1), 줄어들 수 있고(flex-shrink: 1), 크기 0에서 시작해요(flex-basis: 0%). 모두 flex: 1인 항목들은 빈 공간을 똑같이 나눠서, 콘텐츠가 들어가지 않는 경우가 아니면 같은 너비가 돼요.
justify-content와 align-items는 무엇이 다른가요?
justify-content는 주축(행에서는 가로)을 따라, align-items는 교차축(행에서는 세로)을 따라 항목을 배치해요. flex-direction: column이면 두 축이 바뀌어서 justify-content가 세로가 돼요.
flexbox와 grid 중 무엇을 써야 하나요?
항목이 콘텐츠에 따라 크기가 정해지는 한 행이나 한 열에는 flexbox를 쓰세요. 내비게이션 바, 버튼 묶음, 입력 요소 옆의 레이블이에요. 카드 격자나 페이지 레이아웃처럼 서로 맞춰지는 행과 열이 필요하면 grid를 쓰세요.
flex 항목이 컨테이너를 넘치는 이유는 무엇인가요?
flex 항목은 기본적으로 min-width: auto라서 가장 긴 단어나 가장 넓은 자식의 너비보다 작게 줄어들지 않아요. 항목에 min-width: 0(또는 overflow: hidden)을 주면 줄어들 수 있어요. 예를 들어 text-overflow: ellipsis를 동작하게 할 때 필요해요.
flexbox는 span 같은 인라인 요소에서도 동작하나요?
네. display: flex인 요소는 무엇이든 flex 컨테이너이고, 자식은 자기 display 값과 상관없이 flex 항목이 돼요. 그래서 <span> 자식도 인라인이 아니게 되어 block처럼 너비, 높이, margin을 받아요.