Flexbox가 어떻게 동작하는지에 대한 직관을 쌓고, 정렬, 축, 크기 조정, 줄바꿈까지 핵심 개념을 인터랙티브하게 이해하는 가이드.
Introduction Flexbox는 놀라울 만큼 강력한 레이아웃 모드입니다. 이것이 어떻게 동작하는지 진짜로 이해하면, 필요에 따라 스스로 재배치되며 자동으로 반응하는 동적인 레이아웃을 만들 수 있습니다.
예를 들어, 이것을 보세요:
컨테이너 너비: 600
나를 드래그하세요!
이 데모는 Adam Argyle의 놀라운 “4 layouts for the price of 1”(opens in new tab) codepen에서 큰 영감을 받았습니다. 이 데모는 media/container query를 전혀 사용하지 않습니다. 임의의 브레이크포인트를 설정하는 대신, 유동적인 원리 를 사용해 끊김 없이 자연스럽게 흐르는 레이아웃을 만듭니다.
관련 CSS는 다음과 같습니다:
form {
display: flex;
align-items: flex-end;
flex-wrap: wrap;
gap: 16px;
}
.name {
flex-grow: 1;
flex-basis: 160px;
}
.email {
flex-grow: 3;
flex-basis: 200px;
}
button {
flex-grow: 1;
flex-basis: 80px;
}
저는 이런 데모를 보고 완전히 어리둥절했던 기억이 납니다. Flexbox의 기본은 알고 있었지만, 이것은 정말 마법처럼 느껴졌거든요!
이 글에서는 Flexbox에 대한 여러분의 멘털 모델을 더 정교하게 다듬고 싶습니다. 각 속성을 하나씩 살펴보면서, Flexbox 알고리즘이 어떻게 동작하는지에 대한 직관을 함께 만들어 보겠습니다. CSS 초보자이든, 몇 년째 Flexbox를 사용해 왔든, 분명 꽤 많은 것을 배우게 될 거라고 생각합니다!
시작해 봅시다!
CSS는 공식적으로 “layout modes”라고 불리는 여러 가지 서로 다른 레이아웃 알고리즘으로 이루어져 있습니다. 각 레이아웃 모드는 CSS 안의 작은 하위 언어와 같습니다. 기본 레이아웃 모드는 Flow layout 이지만, 부모 컨테이너의 display 속성을 바꾸면 Flexbox를 사용할 수 있습니다:
Hello
to
the
world
주축
주축
display:
display를 flex로 바꾸면 “flex formatting context”가 만들어집니다. 이것은 기본적으로 모든 자식 요소가 Flexbox 레이아웃 알고리즘에 따라 배치된다는 뜻입니다.
각 레이아웃 알고리즘은 특정한 문제를 해결하도록 설계되어 있습니다. 기본 “Flow” 레이아웃은 디지털 문서를 만들기 위한 것이며, 본질적으로 Microsoft Word 의 레이아웃 알고리즘이라고 볼 수 있습니다. 제목과 문단은 블록으로 세로 방향으로 쌓이고, 텍스트·링크·이미지 같은 것들은 이 블록 안에 자연스럽게 들어갑니다.
그렇다면 Flexbox는 어떤 문제를 해결할까요? Flexbox는 한 줄 또는 한 열 안에서 여러 아이템을 배치하고, 그 아이템들의 분배와 정렬을 엄청나게 세밀하게 제어하는 데 초점이 맞춰져 있습니다. 이름에서 알 수 있듯, Flexbox의 핵심은 유연성 입니다. 아이템이 늘어날지 줄어들지, 남는 공간을 어떻게 분배할지 등을 제어할 수 있습니다.
앞서 언급했듯, Flexbox의 핵심은 행 또는 열에서 요소의 분포를 제어하는 것입니다. 기본적으로 아이템은 한 행에서 나란히 배치되지만, flex-direction 속성을 사용하면 열 방향으로 바꿀 수 있습니다:
주축 표시
Hello
to
the
world
주축
주축
flex-direction:
flex-direction: row에서는 주축 이 왼쪽에서 오른쪽으로 가로 방향으로 흐릅니다. flex-direction: column으로 바꾸면 주축은 위에서 아래로 세로 방향으로 흐릅니다.
Flexbox에서는 모든 것이 주축을 기준으로 합니다. 알고리즘은 세로/가로나 행/열 자체에는 관심이 없습니다. 모든 규칙은 이 주축과, 이에 수직으로 교차하는 교차축 을 중심으로 구성됩니다.
이 점이 꽤 멋집니다. Flexbox의 규칙을 배우면 가로 레이아웃과 세로 레이아웃을 매끄럽게 오갈 수 있습니다. 모든 규칙이 자동으로 적응하기 때문입니다. 이 특징은 Flexbox 레이아웃 모드만의 독특한 장점입니다.
자식 요소는 기본적으로 다음 두 가지 규칙에 따라 배치됩니다:
주축: 자식 요소는 컨테이너의 시작점 쪽에 바짝 모입니다.
교차축: 자식 요소는 컨테이너 전체를 채우도록 늘어납니다.
이 규칙을 간단히 시각화하면 다음과 같습니다:
Flexbox에서는 주축이 가로로 흐를지 세로로 흐를지를 우리가 결정합니다. 이것이 모든 Flexbox 계산이 매달려 있는 기준점입니다.
justify-content 속성을 사용하면 주축을 따라 자식 요소가 분배되는 방식을 바꿀 수 있습니다:
주축 표시
Hello
to
the
World
주축
주축
flex-direction:
row
justify-content:
flex-start
주축에 관해서는, 보통 하나의 자식을 정렬한다고 생각하지 않습니다. 대신 핵심은 그룹 전체의 분배 입니다.
모든 아이템을 특정 위치에 몰아넣을 수도 있고(flex-start, center, flex-end), 서로 떨어뜨려 배치할 수도 있습니다(space-between, space-around, space-evenly).
교차축에서는 조금 다릅니다. 여기서는 align-items 속성을 사용합니다:
주축 표시
Hello
to
the
World
주축
주축
flex-direction:
row
justify-content:
flex-start
align-items:New
stretch
흥미롭죠… align-items에는 justify-content와 일부 같은 옵션이 있지만, 완전히 일치하지는 않습니다.
justify-content
align-items
flex-start center flex-end
왜 같은 옵션을 공유하지 않을까요? 이 수수께끼는 곧 풀어보겠지만, 먼저 정렬과 관련된 속성을 하나 더 소개해야 합니다: align-self 입니다.
justify-content와 align-items와는 달리, align-self는 컨테이너가 아니라 자식 요소 에 적용됩니다. 이 속성을 사용하면 특정 자식 하나의 교차축 정렬을 바꿀 수 있습니다:
주축 표시
Hello
to
the
world
주축
주축
flex-direction:
row
align-self:
stretch
align-self는 align-items와 완전히 같은 값을 가집니다. 사실 둘은 정확히 같은 것을 바꿉니다. align-items는 문법적 설탕 으로, 모든 자식의 정렬을 한 번에 설정해 주는 편리한 축약형일 뿐입니다.
justify-self는 없습니다.적어도 Flexbox에는 없습니다. 이 속성은 Grid 레이아웃 모드에는 구현되어 있습니다. 왜 그런지 이해하려면, Flexbox 알고리즘을 더 깊이 들여다봐야 합니다.
지금까지 배운 것만 보면, Flexbox가 꽤 제멋대로처럼 보일 수도 있습니다. 왜 justify-content와 align-items일까요? 왜 justify-items나 align-content가 아닐까요?
게다가 왜 align-self는 있는데 justify-self는 없을까요??
이 질문들은 Flexbox에서 가장 중요하면서도 가장 많이 오해되는 개념 중 하나를 건드립니다. 설명을 위해 비유를 하나 써보겠습니다.
Flexbox에서는 아이템이 주축을 따라 분배됩니다. 기본적으로 아이템은 옆으로 가지런히 늘어서 있습니다. 저는 모든 자식 요소를 한 번에 꿰뚫는 곧은 수평선을 그릴 수 있습니다. 마치 꼬치에 꽂힌 고기처럼요. AKA skewers, souvlaki, shish-kabob:

하지만 교차축은 다릅니다. 곧은 수직선은 자식 요소 중 하나 하고만 교차합니다.
이것은 꼬치보다는, 각각 이쑤시개가 꽂힌 한입 크기의 소시지 묶음에 더 가깝습니다:

여기에는 중요한 차이가 있습니다. 이 작은 소시지들에서는 각 아이템이 자기 꼬치를 따라 움직여도 다른 아이템에 방해를 주지 않습니다 :



나를 드래그하세요!
반대로, 주축에서는 한 개의 꼬치가 모든 형제 요소를 함께 꿰고 있기 때문에, 한 아이템이 자기 꼬치를 따라 움직이려 하면 형제 요소와 부딪히게 됩니다! 가운데 조각을 좌우로 드래그해 보세요:
이것이 주축과 교차축의 근본적인 차이입니다. 교차축 에서의 정렬을 이야기할 때는 각 아이템이 제각각 원하는 대로 움직일 수 있습니다. 하지만 주축 에서는 그룹 을 어떻게 분배할지만 생각할 수 있습니다.
그래서 justify-self가 없는 것입니다. 가운데 조각이 justify-self: flex-start를 설정한다는 게 무슨 뜻일까요? 거기엔 이미 다른 조각이 있으니까요!
이 모든 맥락을 바탕으로, 지금까지 이야기한 4개의 용어를 제대로 정의해 봅시다:
justify — 주축 을 따라 무언가를 배치하는 것.
align — 교차축 을 따라 무언가를 배치하는 것.
content — 분배될 수 있는 “무언가들의 집합”.
items — 개별적으로 위치를 정할 수 있는 단일 아이템.
따라서: 주축을 따라 그룹의 분배를 제어하기 위해 justify-content가 있고, 교차축을 따라 각 아이템을 개별적으로 배치하기 위해 align-items가 있습니다. 이 둘이 Flexbox 레이아웃을 관리할 때 사용하는 핵심 속성입니다.
justify-items가 없는 이유도 justify-self가 없는 이유와 같습니다. 주축에서는 아이템을 그룹으로 생각해야 하고, 분배 가능한 content로 다뤄야 하기 때문입니다.
그렇다면 align-content는 어떨까요? 사실 이것은 Flexbox 안에도 존재합니다 ! 조금 뒤에 flex-wrap 속성을 이야기할 때 다뤄보겠습니다.
제가 Flexbox에 대해 깨달았던 것 중 가장 눈이 번쩍 뜨였던 사실 하나를 이야기해 봅시다.
다음과 같은 CSS가 있다고 가정해 봅시다:
.item {
width: 2000px;
}
보통 사람이라면 이것을 보고 이렇게 생각할 겁니다. “좋아, 너비가 2000픽셀인 아이템이 나오겠네.” 하지만 그것이 항상 사실일까요?
시험해 봅시다:
Code Playground
Code editor:
Result
흥미롭지 않나요?
두 아이템에는 정확히 같은 CSS가 적용되어 있습니다. 둘 다 width: 2000px입니다. 그런데 첫 번째 아이템이 두 번째보다 훨씬 더 넓습니다!
차이는 레이아웃 모드 입니다. 첫 번째 아이템은 Flow layout으로 렌더링되고 있는데, Flow layout에서 width는 강한 제약 입니다. width: 2000px를 설정하면 뷰포트 옆면을 뚫고 나가야 하더라도 2000픽셀 너비의 요소가 됩니다. 마치 벽을 뚫고 나오는 것으로 유명한 북미의 무시무시한 만화 캐릭터처럼요.
하지만 Flexbox의 행에서는 width 속성이 다르게 작동합니다. 강한 제약이라기보다는 하나의 제안에 가깝습니다.
명세에서는 이것을 가상 크기(hypothetical size) 라고 부릅니다. 방해 요소가 전혀 없는 완벽한 이상 세계에서 요소가 되었을 크기입니다.
하지만 아쉽게도, 현실은 그렇게 단순하지 않습니다. 이 경우 제약 요인은 부모에게 2000px 너비의 자식을 담을 공간이 없다는 것 입니다. 그래서 자식의 크기가 줄어들어 들어맞게 됩니다.
이것은 Flexbox 철학의 핵심적인 부분입니다. 모든 것은 유동적이고 유연하며, 세상의 제약에 맞춰 조정될 수 있습니다.
지금까지 가상 크기 덕분에 Flexbox 알고리즘에 어느 정도의 유연성이 내장되어 있다는 것을 봤습니다. 하지만 Flexbox가 얼마나 유동적일 수 있는지 정말로 보려면, 세 가지 속성을 이야기해야 합니다: flex-grow, flex-shrink, flex-basis입니다.
각 속성을 하나씩 살펴봅시다.
flex-basis고백하자면, 저는 오랫동안 flex-basis가 도대체 왜 필요한지 잘 이해하지 못했습니다. 😅
간단히 말하면: Flexbox의 행에서는 flex-basis가 width와 같은 역할을 합니다. Flexbox의 열에서는 flex-basis가 height와 같은 역할을 합니다.
배운 것처럼, Flexbox에서는 모든 것이 주축/교차축 에 연결되어 있습니다. 예를 들어 justify-content는 자식 요소를 주축을 따라 분배하며, 주축이 가로든 세로든 정확히 같은 방식으로 동작합니다.
하지만 width와 height는 그렇지 않습니다! width는 언제나 가로 크기에 영향을 줍니다. flex-direction을 row에서 column으로 바꾼다고 해서 갑자기 height가 되지 않습니다.
그래서 Flexbox의 설계자들은 flex-basis라는 범용 “크기” 속성을 만들었습니다. 이것은 width나 height와 비슷하지만, 다른 모든 것처럼 주축 에 연결되어 있습니다. 이를 통해 가로 방향이든 세로 방향이든 상관없이, 주축 방향에서 요소의 가상 크기 를 설정할 수 있습니다.
여기서 직접 시도해 보세요. 각 자식에는 flex-basis: 50px가 주어져 있지만, 첫 번째 자식은 여러분이 바꿀 수 있습니다:
주축 표시
주축
주축
flex-direction:
row column
flex-basis: 50
width에서 보았듯, flex-basis 역시 강한 제약이라기보다 제안에 가깝습니다. 어느 시점이 되면 모든 요소를 지정된 크기로 배치할 공간이 충분하지 않게 되고, 넘침을 피하기 위해 서로 양보해야 합니다.
flex-grow기본적으로 Flexbox 컨텍스트 안의 요소는 주축을 따라 자신이 편안하다고 느끼는 최소 크기까지 줄어듭니다. 그러면 종종 여분의 공간이 생깁니다.
이 공간을 어떻게 소비할지 flex-grow 속성으로 지정할 수 있습니다:
여분의 공간
주축
주축
flex-direction:
row column
flex-grow:
0 (default) 1
flex-grow의 기본값은 0이며, 즉 늘어나는 것은 opt-in입니다. 컨테이너의 남는 공간을 자식이 차지하게 하려면, 그것을 명시적으로 알려줘야 합니다.
여러 자식이 모두 flex-grow를 설정하면 어떻게 될까요? 이 경우 남는 공간은 각 자식의 flex-grow 값에 비례하여 나뉩니다.
시각적으로 보는 편이 더 쉬울 것 같습니다. 각 자식 값을 늘리거나 줄여 보세요:
flex-grow:
1
2
flex-grow:
1
2
주축
주축
첫 번째 자식은 여분의 공간 1단위를 원하고, 두 번째 자식도 1단위를 원합니다. 즉 총 단위 수는 2입니다(1 + 1). 각 자식은 남는 공간에서 비례한 몫을 가져갑니다.
flex-shrink지금까지 본 예제 대부분에서는 여분의 공간이 있었습니다. 하지만 자식 요소들이 컨테이너에 비해 너무 크다면 어떨까요?
시험해 봅시다. 어떤 일이 일어나는지 보려면 컨테이너를 줄여 보세요:
flex-basis 300 px Actual size:300px Reduced by:0%
flex-basis 150 px Actual size:150px Reduced by:0%
주축
주축
Container width: 600
흥미롭죠? 두 아이템 모두 줄어들지만, 비례해서 줄어듭니다. 첫 번째 자식은 항상 두 번째 자식보다 2배 넓습니다.픽셀 하나 정도 어긋나 보이는 반올림 문제를 볼 수도 있습니다. 아마 제가 지나치게 공들여 만든 데모의 한계일 겁니다!
친절한 복습 차원에서 말하자면, flex-basis는 width와 같은 목적을 가집니다. 관례상 flex-basis를 쓰겠지만, width를 사용해도 정확히 같은 결과 를 얻습니다!
flex-basis와 width는 요소의 가상 크기 를 설정합니다. Flexbox 알고리즘은 요소를 이 원하는 크기보다 더 줄일 수 있지만, 기본적으로는 항상 함께 비례해서 스케일되며 두 요소 사이의 비율을 유지합니다.
그런데 요소가 비례해서 줄어들지 않게 하고 싶다면 어떨까요? 바로 그때 flex-shrink 속성이 등장합니다.
이 데모를 몇 분 정도 직접 만져 보세요. 여기서 무슨 일이 일어나는지 스스로 파악해 보세요. 아래에서 함께 살펴보겠습니다.
flex-basis 250 px flex-shrink 1 Actual size:250px Reduced by:0%
flex-basis 250 px flex-shrink 1 Actual size:250px Reduced by:0%
주축
주축
flex-shrink: 1
Container width: 600
좋습니다. 지금 자식 요소는 두 개이고, 각각의 가상 크기는 250px입니다. 이 자식들을 가상 크기 그대로 담으려면 컨테이너는 최소 500px 너비여야 합니다.
컨테이너를 400px로 줄인다고 가정해 봅시다. 400px짜리 가방에 500px 분량의 내용을 넣을 수는 없겠죠! 100px의 부족분이 생깁니다. 자식 요소들이 들어맞으려면 총 100px를 포기해야 합니다.
flex-shrink 속성은 그 부담을 어떻게 나눌지 결정하게 해 줍니다.
flex-grow와 마찬가지로 이것도 비율입니다. 기본적으로 두 자식 모두 flex-shrink: 1이므로, 각 자식이 부족분의 ½씩 부담합니다. 둘 다 50px씩 포기하고, 실제 크기는 250px에서 200px로 줄어듭니다.
이제 첫 번째 자식을 flex-shrink: 3으로 올려 보겠습니다:

총 부족분은 100px 입니다. 원래라면 각 자식이 ½씩 부담하겠지만, flex-shrink를 조정했기 때문에 첫 번째 요소는 ¾(75px)를 부담하고, 두 번째 요소는 ¼(25px)을 부담하게 됩니다.
절대값 자체는 중요하지 않다는 점에 주목하세요. 핵심은 비율입니다. 두 자식 모두 flex-shrink: 1이면 각 자식은 총 부족분의 ½을 부담합니다. 두 자식 모두 flex-shrink: 1000으로 올려도 각 자식은 총 부족분의 1000/2000을 부담합니다. 어느 쪽이든 결과는 같습니다.
얼마 전 저는 flex-shrink에 대해 번뜩이는 깨달음을 얻었습니다. 이것을 flex-grow의 “역”이라고 생각할 수 있습니다. 둘은 같은 동전의 양면입니다:
flex-grow는 아이템이 컨테이너보다 작을 때 남는 공간이 어떻게 분배되는지 를 제어합니다.
flex-shrink는 아이템이 컨테이너보다 클 때 공간이 어떻게 제거되는지 를 제어합니다.
이 말은 곧 한 번에 이 둘 중 하나만 활성화될 수 있다 는 뜻입니다. 남는 공간이 있으면 아이템이 줄어들 필요가 없으므로 flex-shrink는 효과가 없습니다. 반대로 자식 요소가 컨테이너보다 너무 크면 나눌 여분의 공간이 없으므로 flex-grow는 효과가 없습니다.
저는 이것을 두 개의 별도 세계로 생각하는 편입니다. 지구에 있거나, 아니면 Stranger Things의 사악한 대체 차원에 있거나 둘 중 하나죠. 각 세계에는 각자의 규칙이 있습니다.
때로는 Flexbox 자식 중 일부가 줄어드는 것을 원하지 않을 때가 있습니다.
저는 특히 SVG 아이콘이나 도형에서 이 문제를 자주 봅니다. 단순화된 예를 하나 보죠:
주축
주축
Container width: 600
컨테이너가 좁아지면 두 원이 찌그러져 보기 흉한 타원이 됩니다. 원형을 그대로 유지하고 싶다면 어떻게 해야 할까요?
flex-shrink: 0을 설정하면 됩니다:
주축
주축
flex-shrink:
0 1 (default)
Container width: 600
flex-shrink를 0으로 설정하면 사실상 줄어드는 과정에서 완전히 빠지게 됩니다. Flexbox 알고리즘은 flex-basis(또는 width)를 강한 최소 한계로 취급합니다.
궁금하다면 이 데모의 전체 코드는 다음과 같습니다:
Flex Shrink ball demo
Code editor:
Result
여기서 한 가지 더 이야기해야 할 것이 있는데, 정말 중요합니다. 어쩌면 이 글 전체에서 가장 도움이 되는 내용일 수도 있습니다!
전자상거래 스토어를 위한 유동적인 검색 폼을 만든다고 가정해 봅시다:
Container width: 500
컨테이너가 어느 지점 이하로 줄어들면 내용이 넘쳐흐릅니다!
그런데 왜일까요?? flex-shrink의 기본값은 1이고, 우리는 그것을 제거하지도 않았으므로 검색 입력창은 필요한 만큼 줄어들 수 있어야 합니다! 그런데 왜 줄어들기를 거부하는 걸까요?
이유는 이렇습니다: 가상 크기 외에도, Flexbox 알고리즘이 중요하게 여기는 또 다른 크기가 있습니다. 바로 최소 크기 입니다.
Flexbox 알고리즘은 자식 요소를 최소 크기보다 더 작게 줄이는 것을 거부합니다. flex-shrink 값을 아무리 크게 올려도, 더 이상 줄어들기보다는 내용이 넘쳐흐르게 됩니다!
텍스트 입력창에는 기본 최소 크기가 170px-200px 정도 있습니다(브라우저마다 다릅니다). 위 예제에서 부딪히는 제한이 바로 이것입니다.
다른 경우에는 요소의 내용이 제한 요인이 될 수도 있습니다. 예를 들어, 이 컨테이너의 크기를 바꿔 보세요:
이 아이템에서 가장 긴 단어는 “sesquipedalian”입니다.
이쪽에는 긴 단어가 없습니다.
주축
주축
Container width: 600
텍스트를 포함한 요소의 경우, 최소 너비는 줄바꿈할 수 없는 가장 긴 문자열 의 길이입니다.
좋은 소식은: min-width 속성으로 최소 크기를 다시 정의할 수 있다는 점입니다.
min-width:
auto (default) 0px
Container width: 500
Flexbox 자식에 직접 min-width: 0px를 설정하면, Flexbox 알고리즘에게 내장된 최소 너비를 덮어쓰라고 말하는 셈입니다. 0px로 설정했기 때문에 요소는 필요한 만큼 줄어들 수 있습니다.
같은 요령은 Flexbox 열에서도 min-height 속성과 함께 사용할 수 있습니다(다만 이 문제는 그렇게 자주 나타나지는 않는 것 같습니다).
최근 몇 년 사이 Flexbox의 사용성을 크게 높여 준 것 중 하나가 바로 gap 속성입니다:
주축
주축
flex-direction:
row column
gap: 4px
gap은 각 Flexbox 자식 사이 에 공간을 만들 수 있게 해 줍니다. 이런 점은 내비게이션 헤더 같은 것을 만들 때 아주 유용합니다:
주축
주축
justify-content:
flex-start center flex-end
gap: 16px
gap은 Flexbox 언어에 비교적 최근 추가된 기능이지만, 2021년 초부터 모든 최신 브라우저에서 구현되었습니다(opens in new tab).
공간과 관련해서 공유하고 싶은 요령이 하나 더 있습니다. Flexbox 초창기부터 있었던 기능이지만 비교적 잘 알려져 있지 않고, 저도 처음 발견했을 때 정말 충격적이었습니다.
margin 속성은 특정 요소 주변에 공간을 추가하는 데 사용됩니다. Flow나 Positioned 같은 일부 레이아웃 모드에서는 margin: auto를 사용해 요소를 가운데 정렬할 수도 있습니다.
자동 마진은 Flexbox에서 훨씬 더 흥미롭게 작동합니다:
주축
주축
margin-left:
0 (default) auto
margin-right:
0 (default) auto
앞서 flex-grow 속성이 여분의 공간을 먹어 치워 자식 요소에 적용할 수 있다는 것을 보았습니다.
자동 마진은 여분의 공간을 먹어 치워, 그것을 요소의 마진에 적용합니다. 덕분에 여분의 공간을 어디에 분배할지 아주 정밀하게 제어할 수 있습니다.
흔한 헤더 레이아웃으로는 한쪽에 로고를 두고, 반대쪽에 내비게이션 링크를 두는 방식이 있습니다. 자동 마진을 사용하면 이런 레이아웃을 다음처럼 만들 수 있습니다:
Code Playground
Code editor:
Result
Corpatech 로고는 목록의 첫 번째 리스트 아이템입니다. 여기에 margin-right: auto를 주면, 여분의 공간이 모두 모여 1번째와 2번째 아이템 사이에 배치됩니다.
브라우저 devtools를 사용하면 여기서 어떤 일이 일어나는지 확인할 수 있습니다:

이 문제를 해결하는 다른 방법도 많습니다. 내비게이션 링크를 별도의 Flexbox 컨테이너로 묶을 수도 있고, 첫 번째 리스트 아이템에 flex-grow를 줄 수도 있습니다. 하지만 개인적으로 저는 자동 마진 해법이 정말 마음에 듭니다. 여분의 공간을 하나의 자원 으로 보고, 그것이 정확히 어디로 가야 하는지를 직접 결정하는 방식이니까요.
휴! 지금까지 정말 많은 내용을 다뤘습니다. 이제 마지막으로 꼭 공유하고 싶은 큰 포인트가 하나 남았습니다.
지금까지 모든 아이템은 하나의 행/열 안에서 나란히 배치되어 있었습니다. flex-wrap 속성을 사용하면 이것을 바꿀 수 있습니다.
보세요:
flex-wrap:
nowrap (default) wrap
Container width: 600
flex-basis 180 px Actual size 180px
flex-basis 180 px Actual size 180px
flex-basis 180 px Actual size 180px
주축
주축
대부분의 경우 2차원 작업에는 CSS Grid를 사용하는 것이 좋지만, Flexbox + flex-wrap 역시 분명 쓸모가 있습니다! 이 예제는 “deconstructed pancake”(opens in new tab) 레이아웃을 보여 주는데, 여기서는 3개의 아이템이 중간 크기 화면에서 뒤집힌 피라미드 형태로 쌓입니다.
flex-wrap: wrap을 설정하면 아이템이 가상 크기보다 더 작아지지 않습니다. 적어도 다음 행/열로 넘어가는 것이 가능한 선택지라면 말이죠!
하지만 잠깐! 그렇다면 우리의 꼬치 / 작은 소시지 비유는 어떻게 되는 걸까요??
flex-wrap: wrap을 사용하면 더 이상 모든 아이템을 한 번에 꿸 수 있는 하나의 주축 선이 존재하지 않습니다. 사실상 각 행이 자기만의 작은 flex container처럼 동작합니다. 큰 꼬치 하나 대신, 각 행이 자기 꼬치를 갖게 됩니다:

지금까지 배운 모든 규칙은 이렇게 축소된 범위 안에서도 계속 적용됩니다. 예를 들어 justify-content는 각 꼬치 위의 두 조각을 분배합니다.
하지만 음... 이제 행이 여러 개인데 align-items는 어떻게 동작할까요? 이제 교차축이 여러 아이템과 교차할 수도 있잖아요!
잠깐 생각해 보세요. 이 속성을 바꾸면 무슨 일이 일어날 것 같은지 추측해 보세요. 답을 정했거나 적어도 감이 왔다면, 실제로 맞는지 확인해 보세요:
주축
주축
align-items:
각 행은 자기만의 작은 Flexbox 환경입니다. align-items는 각 아이템을, 각 행을 감싸는 보이지 않는 상자 안에서 위아래로 움직이게 합니다.
하지만 행 자체를 정렬하고 싶다면 어떨까요? 그때는 align-content 속성을 사용할 수 있습니다:
주축
주축
align-items:
flex-start
align-content:New
flex-start
여기서 일어나는 일을 정리하면 다음과 같습니다:
flex-wrap: wrap은 두 줄의 아이템을 만듭니다.
각 줄 안에서는 align-items가 각 개별 자식을 위아래로 움직일 수 있게 해 줍니다.
하지만 더 큰 그림으로 보면, 이 두 줄은 하나의 Flexbox 컨텍스트 안에 들어 있습니다! 이제 교차축은 한 줄이 아니라 두 줄 과 교차합니다. 따라서 각 줄을 개별적으로 움직일 수는 없고, 그룹으로 분배해야 합니다.
앞서 정의한 용어를 사용하면, 지금 다루는 것은 items 가 아니라 content 입니다. 그런데 여전히 교차축에 대해 이야기하고 있죠! 그래서 필요한 속성이 align-content입니다.
한 가지 꼭 인정하고 싶은 게 있습니다: 이 튜토리얼은 꽤 밀도가 높았습니다. 우리는 아주 깊이 파고들었고, 여러분이 이미 Flexbox 전문가가 아니라면 머리가 조금 핑핑 돌고 있을 거라고 생각합니다. 😅
CSS의 많은 부분이 그렇듯, Flexbox는 처음 시작할 때는 단순해 보일지 모르지만, 기본기를 넘어서는 순간 복잡도가 빠르게 올라갑니다.
그 결과, 많은 사람들이 CSS에서 이른 정체 구간을 맞이합니다. 뭔가를 만들 만큼은 알지만, 늘 힘겹습니다. 언어가 낡고 불안정하게 느껴지죠. 마치 언제라도 끊어질 수 있는 오래된 밧줄다리처럼요. 다리가 끊어지면, 우리는 뭐라도 도움이 되길 바라며 StackOverflow 코드 조각을 아무렇게나 던져 봅니다.
즐거운 일은 아닙니다. 그리고 대부분의 프런트엔드 개발 직무에서 CSS가 꽤 큰 비중을 차지한다는 점을 생각하면 더 아쉽죠!
하지만 사실 CSS는 아주 견고하고 일관된 언어입니다. 문제는 우리의 멘털 모델 대부분이 불완전하고 부정확하다는 데 있습니다. 언어에 대한 올바른 직관을 쌓는 데 시간을 들이면, 하나둘 이해되기 시작하고 CSS는 정말 즐겁게 사용할 수 있는 도구가 됩니다. ✨
이 문제를 해결하기 위해 제가 만든 것이 CSS for JavaScript Developers(opens in new tab)라는 종합 코스입니다.
이 글이 도움이 되었다면, 이 코스도 분명 마음에 드실 겁니다. 접근 방식은 비슷하지만 CSS 언어 전체를 다루고, 실제로 새로운 기술을 익히고 있는지 확인할 수 있도록 연습 문제와 프로젝트도 포함되어 있습니다.
이 코스는 React/Angular/Vue 같은 JS 프레임워크를 사용하는 분들을 위해 특별히 만들어졌습니다. 코스의 80%는 CSS 기초에 집중하지만, 그 기초를 현대적인 JS 애플리케이션에 어떻게 통합하는지, CSS를 어떻게 구조화하는지 같은 것도 함께 다룹니다.
CSS가 어렵게 느껴진다면 꼭 한 번 살펴보시길 바랍니다. CSS에 대한 자신감을 얻는 것은 게임 체인저 입니다. 특히 이미 HTML과 JS에 익숙하다면 더더욱 그렇습니다. 이 거룩한 삼위일체를 완성하면, 흐름을 유지하며 웹 애플리케이션 개발을 진짜로 즐기기가 훨씬 쉬워집니다.
더 자세한 내용은 여기에서 확인할 수 있습니다:
이 튜토리얼의 시작에서 우리는 “4 layouts for the price of 1” 데모를 보았습니다:
Container width: 600
나를 드래그하세요!
이제 Flexbox 알고리즘에 대해 모두 배웠으니, 이것이 어떻게 동작하는지 짐작할 수 있겠나요? 여기 있는 코드를 자유롭게 실험해 보세요:
Code Playground
Code editor:
Result
이제 이것이 어떻게 작동하는지 차근차근 살펴보겠습니다:
읽어주셔서 정말 감사합니다! 이 글을 만드는 데 엄청난 노력이 들어갔고, 세상에 공개하게 되어 정말 기쁩니다! 유용하셨기를 바랍니다. 💖
2026년 4월 27일