한 줄 정답 — 부모에
display: flex를 주면 자식들이 한 줄로 늘어선다. 이때 주축(기본 가로) 은justify-content로, 교차축(기본 세로) 은align-items로 정렬한다.flex-direction으로 방향을 바꾸면 이 두 축의 역할도 함께 바뀐다. 그래서 "가로/세로"가 아니라 "주축/교차축"으로 외우는 게 핵심이다.
핵심 요약#
- flex는 부모(컨테이너)에
display: flex를 주는 데서 시작한다. 그러면 자식들이 flex 아이템이 된다. - 주축 정렬은
justify-content, 교차축 정렬은align-items. 기본 방향(row)에서 주축은 가로, 교차축은 세로다. flex-direction: column으로 바꾸면 주축이 세로가 되어, 두 속성이 맡는 방향이 서로 뒤바뀐다.- 줄바꿈은
flex-wrap: wrap, 간격은gap. 아이템의 늘고 줄어듦은flex(grow·shrink·basis)로 조절한다. - 정렬이 "안 되는" 대부분은 축을 반대로 줬거나, 아이템이
flex-shrink때문에 줄어든 경우다.
CSS flexbox 정렬, 주축과 교차축부터#
flexbox가 헷갈리는 단 하나의 이유는 축(axis) 을 안 잡고 속성부터 외워서다. flex에는 두 개의 축이 있다.
- 주축(main axis) — 아이템이 늘어서는 방향.
flex-direction이 정한다(기본row= 가로). - 교차축(cross axis) — 주축에 수직인 방향(기본은 세로).
정렬 속성은 이 두 축에 각각 붙는다.
justify-content→ 주축 방향 정렬align-items→ 교차축 방향 정렬
기본 상태(flex-direction: row)에서는 주축이 가로라서 justify-content가 좌우, align-items가 위아래를 맡는다. 이 대응만 머릿속에 있으면 나머지는 값을 고르는 일이다.
컨테이너(부모)에 주는 속성#
정렬과 배치의 대부분은 부모에 준다.
display: flex — 시작점#
.container {
display: flex; /* 직계 자식들이 flex 아이템이 되어 한 줄로 늘어선다 */
}
이 한 줄만으로 자식들이 가로로 나란히 배치된다(블록 요소라도). 여기서부터 정렬을 얹는다.
flex-direction — 주축의 방향#
.container {
display: flex;
flex-direction: row; /* row(기본) | row-reverse | column | column-reverse */
}
row는 가로로, column은 세로로 쌓는다. 이 값이 주축을 정하기 때문에, 뒤에 나오는 justify-content·align-items가 어느 방향을 맡는지가 여기서 결정된다.
justify-content — 주축 정렬#
.container {
display: flex;
justify-content: center;
/* flex-start(기본) | center | flex-end | space-between | space-around | space-evenly */
}
자주 쓰는 값은 이렇다.
flex-start/center/flex-end— 시작/가운데/끝으로 모음space-between— 양 끝에 붙이고 사이를 균등하게(내비게이션 바에 자주 쓴다)space-around— 각 아이템 양옆에 같은 여백space-evenly— 아이템 사이·양 끝 여백을 모두 똑같이
align-items — 교차축 정렬#
.container {
display: flex;
align-items: center;
/* stretch(기본) | flex-start | center | flex-end | baseline */
}
justify-content와 align-items의 차이가 바로 이것이다. justify-content는 주축(기본 가로), align-items는 교차축(기본 세로) 을 정렬한다. 기본 동작인 stretch는 아이템에 높이가 따로 없을 때(교차 크기 auto) 교차축을 가득 채운다. 높이가 제각각인 카드를 한 줄 안에서 같은 높이로 맞추고 싶을 때 유용하다.
flex-wrap — 한 줄을 넘으면 줄바꿈#
.container {
display: flex;
flex-wrap: wrap; /* nowrap(기본) | wrap | wrap-reverse */
}
기본값 nowrap은 공간이 모자라도 한 줄을 유지한다. 그래서 아이템이 flex-shrink로 줄어들고, 더는 못 줄면(내용 크기 한계) 컨테이너 밖으로 넘친다. wrap을 주면 넘치는 아이템이 다음 줄로 내려가, 반응형 레이아웃의 기본이 된다.
align-content — 여러 줄 사이 정렬#
.container {
display: flex;
flex-wrap: wrap;
align-content: center;
/* flex-start | center | flex-end | space-between | space-around | stretch(기본) */
}
flex-wrap으로 여러 줄이 됐을 때, 줄 묶음 전체를 교차축에서 정렬·분배하는 건 align-items가 아니라 align-content다. align-items는 한 줄 안에서만 동작하므로, 줄과 줄 사이 간격이 의도와 다르면 이 속성을 본다. 단, 한 줄(또는 nowrap)일 때는 효과가 없다.
gap — 아이템 사이 간격#
.container {
display: flex;
gap: 16px; /* 아이템 사이 간격. row-gap / column-gap로 따로도 가능 */
}
예전엔 margin으로 간격을 줬지만, 이제는 gap 한 줄이 가장 깔끔하다. 양 끝에는 여백이 안 생기고 아이템 사이에만 간격이 들어간다.
아이템(자식)에 주는 속성#
각 아이템이 공간을 어떻게 차지할지는 자식에 준다.
flex — 늘고, 줄고, 기본 크기#
flex는 세 속성을 한 번에 적는 단축이다.
.item {
flex: 1 1 240px; /* flex-grow flex-shrink flex-basis */
}
- flex-grow(기본
0) — 남는 공간을 얼마나 차지할지.0이면 안 늘어난다. - flex-shrink(기본
1) — 공간이 모자랄 때 얼마나 줄지.1이면 줄어든다. - flex-basis(기본
auto) — 늘고 줄기 전의 기본 크기.
자주 쓰는 flex: 1은 1 1 0%과 같아서, 형제들과 남는 공간을 똑같이 나눠 갖는다. 컬럼을 균등 분할할 때 쓴다.
align-self — 이 아이템만 교차축 정렬 바꾸기#
.item {
align-self: flex-end; /* 부모의 align-items를 무시하고 이 아이템만 따로 */
}
order — 시각적 순서 바꾸기#
.item {
order: -1; /* 기본 0. 작을수록 앞으로. HTML 순서는 그대로 두고 보이는 순서만 변경 */
}
실전 예제#
내비게이션 바 — 로고는 왼쪽, 메뉴는 오른쪽#
<nav class="nav">
<div class="logo">LOGO</div>
<ul class="menu">
<li>홈</li>
<li>소개</li>
<li>연락</li>
</ul>
</nav>
.nav {
display: flex;
justify-content: space-between; /* 로고와 메뉴를 양 끝으로 */
align-items: center; /* 세로 가운데로 높이 맞춤 */
}
.menu {
display: flex; /* 메뉴 항목도 가로로 */
gap: 16px;
list-style: none;
}
space-between이 로고와 메뉴를 양 끝으로 밀어내고, align-items: center가 둘의 높이를 맞춘다. 메뉴 안쪽도 flex로 만들어 항목을 가로로 늘어놓는 점에 주목하자.
반응형 카드 — 좁아지면 자동 줄바꿈#
.cards {
display: flex;
flex-wrap: wrap; /* 좁아지면 다음 줄로 */
gap: 16px;
}
.card {
flex: 1 1 240px; /* 기본 240px, 공간이 남으면 늘고 모자라면 줄어든다 */
}
flex: 1 1 240px는 카드를 240px 기준으로 두되, 한 줄에 남는 공간은 나눠 갖고, 폭이 좁아지면 wrap으로 다음 줄에 떨어뜨린다. 대부분은 미디어쿼리 없이도 자연스러운 반응형이 되고, 특정 폭에서 컬럼 수를 강제하는 등 세밀한 제어가 필요할 때만 미디어쿼리 글을 더하면 된다.
flex 정렬이 안 될 때#
대부분 다음 네 가지 중 하나다.
1. 축을 반대로 줬다#
justify-content로 세로를, align-items로 가로를 맞추려 한 경우다. 기본 방향에서는 justify-content가 가로, align-items가 세로다. 둘을 바꿔 적지 않았는지 먼저 본다.
2. flex-direction이 column이라 축이 뒤바뀌었다#
flex-direction: column이면 주축이 세로가 된다. 그래서 justify-content가 세로, align-items가 가로를 맡는다. 방향을 바꿨다면 두 속성의 역할도 바뀐 것이다.
3. 아이템이 너무 줄거나, 안 줄어서 넘친다#
비슷해 보이지만 원인이 다른 두 상황이 섞이기 쉽다.
- 너무 줄어든다 —
flex-shrink기본값이1이라 공간이 모자라면 아이템이 내용보다 작아진다. 줄지 않게 하려면flex-shrink: 0을 주거나min-width로 하한을 정한다. - 안 줄어서 넘친다 — flex 아이템은 묵시적으로
min-width: auto(내용 최소 크기)라, 긴 텍스트가 내용보다 작게는 안 줄어 컨테이너 밖으로 넘치거나text-overflow: ellipsis가 안 먹는다. 이때는 그 아이템(또는 중첩된 flex 자식)에min-width: 0을 줘야 더 줄어든다.
4. 세로 가운데가 안 맞는다 (기본 방향 기준)#
기본 방향(row)에서 align-items: center를 줬는데 그대로라면, 보통 부모의 교차축(세로)에 여유 공간이 없어서다. 부모에 height나 min-height로 높이를 확보하면 가운데가 보인다. 가운데 정렬의 여러 방법과 실패 원인은 CSS 가운데 정렬 글에 따로 정리해 두었다.
자주 묻는 질문#
Q. justify-content와 align-items 차이가 뭔가요?
A. 정렬하는 축이 다릅니다. justify-content는 주축(기본 방향 row에서는 가로), align-items는 교차축(기본 세로)을 정렬합니다. flex-direction을 바꾸면 두 축의 방향이 서로 뒤바뀌므로, "가로/세로"가 아니라 "주축/교차축"으로 기억하는 편이 안전합니다.
Q. flexbox로 가로·세로 가운데 정렬은 어떻게 하나요?
A. 부모에 display: flex; justify-content: center; align-items: center를 주면 됩니다(세로가 보이려면 부모에 높이 확보). 다른 방법과 실패 원인까지는 CSS 가운데 정렬 글에 정리해 두었습니다.
Q. flex-direction을 column으로 바꿨더니 정렬이 반대로 돼요. 왜인가요?
A. 축이 바뀌었기 때문입니다. column이면 주축이 세로가 되어 justify-content가 세로, align-items가 가로를 맡습니다. 정렬이 의도와 반대로 보인다면 방향을 먼저 확인하세요.
Q. flex 아이템이 자꾸 줄어들어요.
A. flex-shrink 기본값이 1이라 공간이 모자라면 아이템이 줄어듭니다. 줄지 않게 하려면 해당 아이템에 flex-shrink: 0 또는 min-width를 지정하세요.
관련 글#
flex는 결국 "박스를 어떻게 배치하는가"의 한 방법이다. 토대가 되는 개념과, 정렬의 한 갈래인 가운데 정렬을 함께 보면 응용이 쉬워진다.