본문 바로가기

Flexbox 레이아웃

Flex 컨테이너의 주축·교차축과 정렬·줄바꿈 속성을 사용해 내비게이션과 카드의 1차원 배치를 만듭니다.

지난 장에서 우리는 모든 HTML 요소가 박스 모델 개념을 기반으로 하며, display 속성으로 요소의 기본 배치 방식을 제어할 수 있음을 학습했습니다.

display: block, display: inline, display: inline-block은 간단한 레이아웃에는 유용하지만, 복잡한 UI 요소를 유연하게 정렬하고 배치하는 데는 한계가 있습니다.

특히 다양한 화면 크기에 맞춰 요소를 재배치해야 하는 반응형 웹 디자인에서는 그 한계가 더 분명해집니다.

여기서 등장하는 것이 바로 Flexbox(Flexible Box Layout)입니다.

Flexbox는 1차원 레이아웃(한 줄 또는 한 열)을 위해 설계된 CSS3 레이아웃 모듈입니다.

부모 요소(컨테이너) 안 자식 요소(아이템)를 효율적으로 정렬/분배/배치할 수 있어, 복잡하고 유연한 레이아웃을 훨씬 쉽게 구현할 수 있습니다.

이 장에서는 Flexbox의 기본 개념과 주요 속성을 상세하게 학습하고, 실습을 통해 Flexbox의 강력함을 직접 경험해 볼 것입니다.

Flexbox를 익히면 웹 레이아웃 구현 능력이 한 단계 성장합니다.

Flexbox 축과 남는 공간 분배 — 상태 추적

줄 형성 뒤 각 줄의 grow/scaled-shrink와 정렬을 적용한다. 같은 아이템/컨테이너의 남은 공간·각 크기·정렬 여백이 바뀌는 계산을 구분할 이득이 있다.

Flex는 줄을 만든 뒤 각 줄의 공간을 나눕니다

FLEX LAYOUT

Flex는 줄을 만든 뒤 각 줄의 공간을 나눕니다

주축·교차축과 기본 크기를 정하고 gap·wrap을 반영해 줄을 만듭니다. 크기를 해소한 다음 남은 공간을 정렬합니다.

Flex의 줄 형성과 공간 분배 단계축, 기본 크기, gap을 포함한 줄 형성, 각 줄의 grow/scaled-shrink 크기 해소, 주축 정렬과 교차축 정렬의 여섯 노드와 다섯 방향이다. min/max와 order 조건은 아래 설명을 함께 읽는다. 화면이 좁아져도 단계와 연결은 같다.
1 · 축direction · writing mode
연결 관계1 · 축 · direction · writing mode → 2 · 기본 크기 · basis · 콘텐츠 · min/max
2 · 기본 크기basis · 콘텐츠 · min/max
연결 관계2 · 기본 크기 · basis · 콘텐츠 · min/max → 3 · 줄 형성 · wrap · 바깥 크기 · gap
3 · 줄 형성wrap · 바깥 크기 · gap
연결 관계3 · 줄 형성 · wrap · 바깥 크기 · gap → 4 · 줄 안의 크기 · grow · scaled shrink
4 · 줄 안의 크기grow · scaled shrink
연결 관계4 · 줄 안의 크기 · grow · scaled shrink → 5 · 주축 정렬 · auto margin · justify
5 · 주축 정렬auto margin · justify
연결 관계5 · 주축 정렬 · auto margin · justify → 6 · 교차축 정렬 · items/self · content
6 · 교차축 정렬items/self · content

줄 형성에서는 order로 정한 시각 순서를 사용하며 기본 order: 0에서는 DOM 순서입니다. 부족한 공간은 shrink×기본 크기로 나누되 min/max가 최종 크기를 제한합니다. 주축의 auto margin은 남은 공간을 먼저 흡수합니다. align-items/self는 아이템, align-content는 wrap/wrap-reverse 컨테이너의 줄들을 정렬하며 실제 한 줄이어도 적용될 수 있습니다.

gap과 줄 형성을 먼저, 공간 분배는 각 줄 안에서

gap은 줄 형성과 가용 공간 계산에 반영합니다. grow·shrink는 min/max 제약을 따릅니다. order가 있으면 시각 순서를 따라 줄을 만들지만 DOM·기본 focus·스크린 리더 순서를 바꾸지는 않습니다.

align-content는 nowrap 컨테이너에는 적용되지 않습니다. wrap/wrap-reverse에서는 실제 줄 수가 하나여도 영향을 줄 수 있습니다.

이 예의 조건

  • 원도표의 전체 구조와 설명은 위 관계도와 표에서 유지합니다. 아래 인쇄 표에도 원도표의 관계·조건·라벨·설명·주석을 보존합니다.
  • 두 관찰 칸의 위치는 고정입니다. 관찰 상태는 해당 순간의 값이며, 동작·이유는 그 조건에서의 판정과 변화 원인입니다. 독립 비교와 실패 분기는 별도 조건으로 읽고 모든 분기를 한 요청의 필수 실행 순서로 이어 읽지 않습니다.
전체 상태와 변화 근거
장면현재 관찰값설명변화 이유
줄 구성현재: line별 아이템 / free space: 아직 분배 전컨테이너와 아이템 basis로 line을 구성한다flex는 line별로 크기를 분배한다
공간 분배grow: 양의 공간 / shrink: basis에 따른 가중치남는 공간은 grow, 부족한 공간은 scaled shrink로 나눈다shrink factor만으로 크기를 같은 비율로 줄이지 않는다
배치아이템: 최종 주축 크기 / 정렬: 남은 공간의 위치제약을 적용한 최종 크기로 정렬한다크기 분배와 min/max 제약 해소 뒤 남은 공간을 auto margin과 정렬이 사용해 아이템의 위치를 결정한다

Flexbox 축과 남는 공간 분배 — 상태 추적

줄 형성 뒤 각 줄의 grow/scaled-shrink와 정렬을 적용한다. 같은 아이템/컨테이너의 남은 공간·각 크기·정렬 여백이 바뀌는 계산을 구분할 이득이 있다.

Flex는 줄을 만든 뒤 각 줄의 공간을 나눕니다

FLEX LAYOUT

Flex는 줄을 만든 뒤 각 줄의 공간을 나눕니다

주축·교차축과 기본 크기를 정하고 gap·wrap을 반영해 줄을 만듭니다. 크기를 해소한 다음 남은 공간을 정렬합니다.

Flex의 줄 형성과 공간 분배 단계축, 기본 크기, gap을 포함한 줄 형성, 각 줄의 grow/scaled-shrink 크기 해소, 주축 정렬과 교차축 정렬의 여섯 노드와 다섯 방향이다. min/max와 order 조건은 아래 설명을 함께 읽는다. 화면이 좁아져도 단계와 연결은 같다.
1 · 축direction · writing mode
연결 관계1 · 축 · direction · writing mode → 2 · 기본 크기 · basis · 콘텐츠 · min/max
2 · 기본 크기basis · 콘텐츠 · min/max
연결 관계2 · 기본 크기 · basis · 콘텐츠 · min/max → 3 · 줄 형성 · wrap · 바깥 크기 · gap
3 · 줄 형성wrap · 바깥 크기 · gap
연결 관계3 · 줄 형성 · wrap · 바깥 크기 · gap → 4 · 줄 안의 크기 · grow · scaled shrink
4 · 줄 안의 크기grow · scaled shrink
연결 관계4 · 줄 안의 크기 · grow · scaled shrink → 5 · 주축 정렬 · auto margin · justify
5 · 주축 정렬auto margin · justify
연결 관계5 · 주축 정렬 · auto margin · justify → 6 · 교차축 정렬 · items/self · content
6 · 교차축 정렬items/self · content

줄 형성에서는 order로 정한 시각 순서를 사용하며 기본 order: 0에서는 DOM 순서입니다. 부족한 공간은 shrink×기본 크기로 나누되 min/max가 최종 크기를 제한합니다. 주축의 auto margin은 남은 공간을 먼저 흡수합니다. align-items/self는 아이템, align-content는 wrap/wrap-reverse 컨테이너의 줄들을 정렬하며 실제 한 줄이어도 적용될 수 있습니다.

gap과 줄 형성을 먼저, 공간 분배는 각 줄 안에서

gap은 줄 형성과 가용 공간 계산에 반영합니다. grow·shrink는 min/max 제약을 따릅니다. order가 있으면 시각 순서를 따라 줄을 만들지만 DOM·기본 focus·스크린 리더 순서를 바꾸지는 않습니다.

align-content는 nowrap 컨테이너에는 적용되지 않습니다. wrap/wrap-reverse에서는 실제 줄 수가 하나여도 영향을 줄 수 있습니다.

이 예의 조건

  • 원도표의 전체 구조와 설명은 위 관계도와 표에서 유지합니다. 아래 인쇄 표에도 원도표의 관계·조건·라벨·설명·주석을 보존합니다.
  • 두 관찰 칸의 위치는 고정입니다. 관찰 상태는 해당 순간의 값이며, 동작·이유는 그 조건에서의 판정과 변화 원인입니다. 독립 비교와 실패 분기는 별도 조건으로 읽고 모든 분기를 한 요청의 필수 실행 순서로 이어 읽지 않습니다.
전체 상태와 변화 근거
장면현재 관찰값설명변화 이유
줄 구성현재: line별 아이템 / free space: 아직 분배 전컨테이너와 아이템 basis로 line을 구성한다flex는 line별로 크기를 분배한다
공간 분배grow: 양의 공간 / shrink: basis에 따른 가중치남는 공간은 grow, 부족한 공간은 scaled shrink로 나눈다shrink factor만으로 크기를 같은 비율로 줄이지 않는다
배치아이템: 최종 주축 크기 / 정렬: 남은 공간의 위치제약을 적용한 최종 크기로 정렬한다크기 분배와 min/max 제약 해소 뒤 남은 공간을 auto margin과 정렬이 사용해 아이템의 위치를 결정한다

Flexbox의 기본 개념: 컨테이너와 아이템

Flexbox는 부모-자식 관계를 기반으로 작동합니다.

Flexbox 레이아웃을 적용하려는 영역을 Flex 컨테이너(Flex Container)로 만들고, 그 안에 있는 직계 자식 요소들이 Flex 아이템(Flex Item)이 됩니다.

  • Flex 컨테이너: display: flex 또는 display: inline-flex 속성이 적용된 부모 요소입니다. 컨테이너에 적용하는 속성들은 그 안에 있는 Flex 아이템들의 전체적인 배치와 정렬에 영향을 미칩니다.
  • Flex 아이템: Flex 컨테이너의 직계 자식 요소들입니다. 아이템에 직접 적용하는 속성들은 개별 아이템의 크기나 정렬 방식에 영향을 줍니다.
  • 주축 (Main Axis): Flex 아이템이 배열되는 방향입니다. row는 인라인 축, column은 블록 축을 따릅니다. 이 절의 가로쓰기·왼쪽에서 오른쪽 문서에서 기본 row는 가로 방향입니다.
  • 교차축 (Cross Axis): 주축에 수직인 방향입니다. 위 조건의 row에서는 세로이고 column에서는 가로입니다. 교차축이 세로라는 말은 flex-direction: column을 지정했다는 뜻이 아닙니다.

Flex 컨테이너 속성: 아이템 전체 정렬/배치

Flex 컨테이너에 적용하는 속성들은 Flex 아이템들의 배열 방식과 공간 분배를 제어합니다.

display로 Flex 컨테이너 선언

  • display: flex;: 컨테이너를 블록 레벨 Flex 컨테이너로 만듭니다. 즉, 컨테이너 자체가 새로운 줄에서 시작하고 부모 너비를 차지합니다. (가장 일반적인 사용)
  • display: inline-flex;: 컨테이너를 인라인 레벨 Flex 컨테이너로 만듭니다. 즉, 컨테이너 자체가 인라인 요소처럼 콘텐츠 흐름에 따라 옆으로 배치됩니다.

flex-direction (주축 방향 설정)

Flex 아이템들이 배열될 주축의 방향을 설정합니다.

  • row (기본값): 아이템들을 가로 방향으로 (왼쪽에서 오른쪽, 또는 오른쪽에서 왼쪽) 배열합니다. 주축은 가로입니다.
  • row-reverse: 아이템들을 row의 반대 방향으로 배열합니다.
  • column: 아이템들을 세로 방향으로 (위에서 아래) 배열합니다. 주축은 세로입니다.
  • column-reverse: 아이템들을 column의 반대 방향으로 배열합니다.
.container {
    display: flex;
    flex-direction: column; /* 아이템들을 세로로 쌓음 */
}

flex-wrap (아이템 줄바꿈 설정)

Flex 아이템들이 컨테이너를 벗어날 경우 줄바꿈(wrap)할지 여부를 설정합니다.

  • nowrap (기본값): 모든 아이템을 한 줄에 강제로 배치합니다. (아이템 크기가 줄어들 수 있음)
  • wrap: 아이템들이 컨테이너를 벗어나면 다음 줄로 넘어가게 합니다.
  • wrap-reverse: wrap의 반대 방향으로 줄바꿈합니다.
.container {
    display: flex;
    flex-wrap: wrap; /* 아이템이 넘치면 자동으로 줄바꿈 */
}

justify-content (주축 방향 정렬)

주축 방향으로 Flex 아이템들의 정렬과 공간 분배를 설정합니다.

  • flex-start (기본값): 주축의 시작 지점으로 아이템들을 정렬합니다.
  • flex-end: 주축의 끝 지점으로 아이템들을 정렬합니다.
  • center: 주축의 중앙으로 아이템들을 정렬합니다.
  • space-between: 아이템들 사이에 균등한 공간을 분배합니다. (시작과 끝 아이템은 컨테이너 경계에 붙음)
  • space-around: 각 아이템 주위에 균등한 공간을 분배합니다. (시작과 끝 아이템은 컨테이너 경계에서 떨어짐)
  • space-evenly: 모든 아이템과 컨테이너 경계 사이의 공간을 모두 균등하게 분배합니다.
.container {
    display: flex;
    justify-content: center; /* 아이템들을 가로 중앙 정렬 */
}

align-items (교차축 방향 정렬)

교차축 방향으로 Flex 아이템들의 정렬을 설정합니다.

  • stretch (기본값): 교차축 크기가 auto이고 교차축 margin이 auto가 아닌 아이템을 해당 줄의 교차축 크기에 맞춰 늘립니다. row에서는 높이, column에서는 너비가 이 조건에 해당하며 min/max 크기의 제한을 받습니다.
  • flex-start: 교차축의 시작 지점으로 아이템들을 정렬합니다.
  • flex-end: 교차축의 끝 지점으로 아이템들을 정렬합니다.
  • center: 교차축의 중앙으로 아이템들을 정렬합니다.
  • baseline: 아이템들의 텍스트 기준선(baseline)에 맞춰 정렬합니다.
.container {
    display: flex;
    align-items: center; /* 아이템들을 세로 중앙 정렬 */
    height: 200px; /* align-items를 보려면 컨테이너에 높이 지정 필요 */
}

align-content (여러 줄의 교차축 정렬)

flex-wrap: wrap인 컨테이너에서 줄들을 교차축 방향으로 정렬합니다.

nowrap인 한 줄 컨테이너에는 효과가 없습니다. wrap인 컨테이너는 실제 아이템이 한 줄에 들어가더라도 줄 정렬의 대상이며, 정렬 차이를 보려면 교차축에 여유 공간이 있어야 합니다.

  • stretch (기본값): 각 줄의 공간을 균등하게 늘려 컨테이너를 채웁니다.
  • flex-start: 각 줄을 교차축의 시작 지점으로 정렬합니다.
  • flex-end: 각 줄을 교차축의 끝 지점으로 정렬합니다.
  • center: 각 줄을 교차축의 중앙으로 정렬합니다.
  • space-between: 각 줄 사이에 균등한 공간을 분배합니다.
  • space-around: 각 줄 주위에 균등한 공간을 분배합니다.
.container {
    display: flex;
    flex-wrap: wrap;
    align-content: space-around; /* 여러 줄을 교차축 방향으로 분배 */
    height: 400px; /* align-content를 보려면 컨테이너에 충분한 높이 필요 */
}

단축 속성 flex-flow

flex-direction과 flex-wrap을 한 번에 설정하는 단축 속성입니다.

.container {
    flex-flow: row wrap; /* flex-direction: row; flex-wrap: wrap; 과 동일 */
}

앞의 Flexbox 다이어그램은 축을 정한 뒤 기본 크기, 남는 공간, 정렬, 줄바꿈을 선택하는 순서를 보여줍니다.

이 흐름을 기준으로 보면 justify-content, align-items, gap, wrap을 상황에 맞게 고르기 쉽습니다.


Flex 아이템 속성: 개별 아이템 제어

Flex 아이템에 적용하는 속성들은 해당 아이템의 크기 조절, 정렬, 순서 등을 개별적으로 제어합니다.

order (순서 변경)

Flex 아이템의 시각적 순서를 변경합니다.

기본값은 0이며, 숫자가 낮을수록 먼저 표시됩니다. 같은 값끼리는 소스 순서를 따릅니다. 시각적 순서를 바꾸어도 DOM과 기본 키보드·보조 기술 읽기 순서는 바뀌지 않으므로 의미 있는 읽기 순서는 HTML에서 먼저 정합니다.

.item-a { order: 2; }
.item-b { order: 1; } /* item-b가 item-a보다 먼저 표시됨 */

flex-grow (확대 비율)

컨테이너에 남는 공간이 있을 때, Flex 아이템이 그 공간을 얼마나 차지하여 늘어날지 비율을 설정합니다.

기본값은 0 (늘어나지 않음)입니다.

.item-1 { flex-grow: 1; } /* 남는 공간을 1의 비율로 가져감 */
.item-2 { flex-grow: 2; } /* 남는 공간을 2의 비율로 가져감 (item-1의 2배) */

flex-shrink (축소 비율)

Flex 아이템들이 너무 많아 컨테이너를 벗어날 때, Flex 아이템이 얼마나 축소될지 비율을 설정합니다.

기본값은 1 (줄어듦)입니다.

0으로 설정하면 이 축소 분배에 참여하지 않습니다. 다른 아이템의 축소량은 flex-shrink 숫자만 비교하지 않고 숫자×기본 크기를 가중치로 사용합니다. min/max 크기 제약이 있으면 단순 비례 결과가 다시 조정될 수 있습니다. 주축 공간 분배를 참고하세요.

.item-1 { flex-shrink: 0; } /* 공간이 부족해도 줄어들지 않음 */
.item-2 { flex-shrink: 1; } /* 기본 비율로 줄어듦 */

flex-basis (기본 크기)

Flex 아이템이 flex-grow나 flex-shrink 속성이 적용되기 전에 가질 기본 크기를 주축 방향으로 지정합니다.

width 또는 height와 유사하게 작동하지만, Flexbox 컨텍스트 내에서 더 유연합니다.

  • auto (기본값): 콘텐츠 크기나 명시된 width/height를 따릅니다.
  • px, %, em 등 다양한 단위 사용 가능.
.item-1 { flex-basis: 100px; } /* 기본 너비 100px */
.item-2 { flex-basis: 30%; } /* 기본 너비 컨테이너의 30% */

단축 속성 flex

flex-grow, flex-shrink, flex-basis를 한 번에 설정하는 단축 속성입니다.

  • flex: [flex-grow] [flex-shrink] [flex-basis];
  • flex: 1;는 늘어남 비율 1·줄어듦 비율 1과 생략된 basis를 설정하는 축약입니다. 브라우저에서 흔히 쓰이는 0%와 명시적인 길이 0은 컨테이너 크기가 불확정할 때 결과가 다를 수 있습니다. 계산의 시작 크기를 정확히 0으로 고정하려면 flex: 1 1 0px;처럼 세 값을 씁니다.
  • flex: auto;는 flex: 1 1 auto;와 동일합니다.
  • flex: none;는 flex: 0 0 auto;와 동일합니다. (늘어나지도 줄어들지도 않음)
.item {
    flex: 1 1 200px; /* 늘어나는 비율 1, 줄어드는 비율 1, 기본 크기 200px */
}

align-self (개별 아이템 교차축 정렬)

개별 Flex 아이템의 교차축 정렬을 설정합니다.

컨테이너의 align-items 속성보다 우선합니다.

  • auto (기본값): 부모의 align-items 설정을 따릅니다.
  • stretch, flex-start, flex-end, center, baseline: align-items와 동일한 값.
.item-special {
    align-self: flex-end; /* 해당 아이템만 교차축 끝에 정렬 */
}

실습: Flexbox를 활용한 레이아웃

지금까지 배운 Flexbox 속성들을 활용하여 실제 웹 페이지의 흔한 레이아웃인 내비게이션 바와 카드 목록을 만들어 봅시다.

프로젝트 폴더 구조
flexbox_layout.html
flex.css
flexbox_layout.html 파일 작성
flexbox_layout.html
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Flexbox 레이아웃 실습</title>
    <link rel="stylesheet" href="css/flex.css">
</head>
<body>
    <header class="navbar">
        <div class="logo">MyBrand</div>
        <nav class="nav-menu">
            <a href="#" class="nav-item">홈</a>
            <a href="#" class="nav-item">제품</a>
            <a href="#" class="nav-item">서비스</a>
            <a href="#" class="nav-item special-item">문의하기</a>
        </nav>
    </header>

    <main class="main-content">
        <h1>Flexbox로 만드는 유연한 레이아웃</h1>

        <section class="card-section">
            <h2>인기 상품</h2>
            <div class="product-cards">
                <div class="card">

                    <h3>제품명 1</h3>
                    <p>이 제품은 뛰어난 품질과 디자인을 자랑합니다.</p>
                    <span class="price">49,000원</span>
                </div>
                <div class="card">

                    <h3>제품명 2</h3>
                    <p>일상에 편리함을 더해주는 필수 아이템입니다.</p>
                    <span class="price">75,000원</span>
                </div>
                <div class="card">

                    <h3>제품명 3</h3>
                    <p>놀라운 기능과 합리적인 가격으로 만나보세요.</p>
                    <span class="price">120,000원</span>
                </div>
                <div class="card">

                    <h3>제품명 4</h3>
                    <p>새로운 경험을 선사할 프리미엄 제품.</p>
                    <span class="price">99,000원</span>
                </div>
                <div class="card wide-card">

                    <h3>특별 할인 제품</h3>
                    <p>지금 구매하시면 특별한 혜택이 기다리고 있습니다! 놓치지 마세요.</p>
                    <span class="price">150,000원</span>
                </div>
            </div>
        </section>
    </main>

    <footer>
        <p>&copy; 2025 Flexbox 연습. 모든 권리 보유.</p>
    </footer>
</body>
</html>
css/flex.css 파일 작성
css/flex.css
/* 기본 스타일 및 박스 사이징 */
* {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

body {
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    background-color: #f4f7f6;
    color: #333;
    line-height: 1.6;
}

/* 1. 네비게이션 바 (Flex 컨테이너) */
.navbar {
    display: flex; /* Flexbox 활성화 */
    justify-content: space-between; /* 로고와 메뉴를 양 끝으로 정렬 */
    align-items: center; /* 수직 중앙 정렬 */
    background-color: #2c3e50;
    color: white;
    padding: 15px 30px;
    box-shadow: 0 2px 5px rgba(0,0,0,0.2);
}

.navbar .logo {
    font-size: 1.8em;
    font-weight: bold;
    color: #f1c40f; /* 로고 색상 */
}

.navbar .nav-menu {
    display: flex; /* 메뉴 아이템들을 Flexbox로 정렬 */
    gap: 25px; /* 메뉴 아이템들 사이 간격 */
}

.nav-item {
    color: white;
    text-decoration: none;
    font-size: 1.1em;
    padding: 5px 0;
    transition: color 0.3s ease;
}

.nav-item:hover {
    color: #f1c40f;
}

/* Flex 아이템 개별 정렬 예시 */
.special-item {
    background-color: #e67e22; /* 주황색 버튼처럼 */
    padding: 8px 15px;
    border-radius: 5px;
    color: white !important; /* hover 색상보다 우선 */
    font-weight: bold;
    transition: background-color 0.3s ease;
    align-self: center; /* 해당 아이템만 수직 중앙 정렬 */
}

.special-item:hover {
    background-color: #d35400;
}

/* 메인 콘텐츠 */
.main-content {
    max-width: 1200px;
    margin: 40px auto;
    padding: 0 20px;
}

.main-content h1 {
    text-align: center;
    color: #3498db;
    margin-bottom: 40px;
    font-size: 2.5em;
}

/* 2. 상품 카드 섹션 (Flex 컨테이너) */
.card-section h2 {
    text-align: center;
    color: #2c3e50;
    margin-bottom: 30px;
    font-size: 2em;
}

.product-cards {
    display: flex; /* Flexbox 활성화 */
    flex-wrap: wrap; /* 공간 부족 시 줄바꿈 */
    justify-content: center; /* 카드들을 가로 중앙 정렬 */
    gap: 25px; /* 카드들 사이 간격 */
}

.card {
    background-color: white;
    border: 1px solid #ddd;
    border-radius: 8px;
    padding: 20px;
    width: 280px; /* 각 카드의 고정 너비 */
    text-align: center;
    box-shadow: 0 2px 10px rgba(0,0,0,0.08);
    transition: transform 0.2s ease-in-out;
    display: flex; /* 카드 내부의 콘텐츠도 Flexbox로 배치 */
    flex-direction: column; /* 카드 내부 콘텐츠를 세로로 쌓음 */
    justify-content: space-between; /* 카드 콘텐츠 위아래로 분배 */
}

.card:hover {
    transform: translateY(-8px);
}

.card img {
    max-width: 100%;
    height: 150px; /* 이미지 높이 고정 */
    object-fit: cover; /* 이미지 비율 유지하며 채우기 */
    border-radius: 5px;
    margin-bottom: 15px;
}

.card h3 {
    font-size: 1.4em;
    color: #2980b9;
    margin-bottom: 10px;
}

.card p {
    font-size: 0.95em;
    color: #555;
    margin-bottom: 15px;
    flex-grow: 1; /* 단락이 남는 공간을 차지하여 늘어남 */
}

.card .price {
    font-size: 1.2em;
    font-weight: bold;
    color: #e67e22;
    margin-top: 10px; /* 가격 위에 마진 */
}

/* 특정 카드에 Flex 아이템 속성 적용 */
.wide-card {
    width: 600px; /* 더 넓은 카드 (두 칸 차지) */
    flex-shrink: 0; /* 줄어들지 않도록 설정 */
}

/* 푸터 스타일 */
footer {
    background-color: #34495e;
    color: #ecf0f1;
    text-align: center;
    padding: 20px 0;
    margin-top: 50px;
    font-size: 0.9em;
}

/* 미디어 쿼리 (반응형: 화면 너비 768px 이하) */
@media (max-width: 768px) {
    .navbar {
        flex-direction: column; /* 네비게이션을 세로로 쌓음 */
        align-items: flex-start; /* 왼쪽 정렬 */
        padding: 15px 20px;
    }
    .navbar .nav-menu {
        flex-direction: column; /* 메뉴 아이템 세로 정렬 */
        gap: 10px;
        width: 100%; /* 너비 전체 차지 */
        margin-top: 15px;
    }
    .nav-item {
        width: 100%;
        text-align: center;
        padding: 10px 0;
        background-color: rgba(255, 255, 255, 0.1);
        border-radius: 5px;
    }
    .special-item {
        align-self: stretch; /* 전체 너비로 늘어남 */
    }
    .product-cards {
        flex-direction: column; /* 모바일에서는 카드들을 세로로 쌓음 */
        align-items: center; /* 교차축인 가로 방향으로 중앙 정렬 */
    }
    .card {
        width: 90%; /* 모바일에서 카드 너비 조정 */
    }
    .wide-card {
        width: 90%; /* 모바일에서 넓은 카드 너비 조정 */
    }
}
결과 확인
  • Live Server를 통해 flexbox_layout.html 파일을 열어 보세요.
  • 상단 내비게이션 바의 로고와 메뉴가 양 끝으로 깔끔하게 정렬되고, 메뉴 아이템들이 가로로 정렬된 것을 확인하세요.
  • 상품 카드들이 가로로 배치되고, 화면 너비가 좁아지면 자동으로 다음 줄로 넘어가는 것을 확인하세요. gap 속성으로 카드 사이의 간격이 유지되는 것도 확인하세요.
  • 특히 브라우저 창의 너비를 조절해 보세요. 768px 이하로 줄어들면 내비게이션 바와 상품 카드 레이아웃이 자동으로 바뀝니다. 이는 미디어 쿼리와 Flexbox를 함께 사용한 결과입니다.

이번 장에서는 현대 웹 레이아웃의 핵심 기술인 Flexbox(Flexible Box Layout)에 대해 깊이 있게 학습했습니다.

Flexbox가 컨테이너와 아이템이라는 두 가지 주요 개념을 중심으로 작동하며, 주축과 교차축의 개념을 통해 아이템의 정렬과 공간 분배를 제어한다는 것을 이해했습니다.

컨테이너에 적용하는 display, flex-direction, flex-wrap, justify-content, align-items, align-content 속성들을 통해 전체적인 레이아웃 방향과 정렬 방식을 제어하는 방법을 배웠습니다.

또한, 아이템에 적용하는 order, flex-grow, flex-shrink, flex-basis, align-self 속성들을 통해 개별 아이템의 순서, 크기, 정렬을 조절하는 방법도 익혔습니다.

Flexbox는 한 방향으로 배치되는 요소를 정렬하고 간격을 조정할 때 자주 사용됩니다.

이 장에서 배운 내용을 바탕으로 복잡한 UI도 쉽게 구성할 수 있는 기반을 다지게 되었습니다.

Flexbox 컨테이너와 아이템 속성은 주축과 교차축의 공간 분배를 함께 조절합니다.

두 종류의 공간을 따로 계산하기

기본 크기가 각각 100px·200px인 두 아이템이 한 줄에 있습니다. 두 아이템의 flex-grow와 flex-shrink는 모두 1이고 gap·패딩·테두리·마진은 0입니다. min/max와 콘텐츠 최소 크기의 제한도 없다고 가정합니다. 컨테이너가 360px일 때와 240px일 때의 최종 크기를 각각 예측하세요.

분배 과정 확인

360px에서는 기본 크기 합 300px을 뺀 60px을 grow 1:1로 나눠 각각 30px씩 더합니다. 결과는 130px·230px입니다. 240px에서는 60px이 부족하고 shrink 가중치가 1×100:1×200=1:2이므로 각각 20px·40px을 줄입니다. 결과는 80px·160px입니다. “둘 다 1”이라서 축소량도 같다고 계산하지 않습니다.

현재 실습에서 축 바꾸기

768px 이하의 .product-cards는 column이므로 주축이 세로, 교차축이 가로입니다. 이때 align-items: center가 카드를 가로 가운데로 놓습니다. 같은 화면에서 justify-content만 바꾸어 가로 정렬하려 하면 축을 잘못 고른 것입니다. 또한 .wide-card의 600px는 두 카드와 gap의 280+280+25=585px를 정확히 합친 “두 칸”이 아닙니다. Flexbox는 Grid처럼 공동 열 칸을 정의하지 않으므로 코드의 넓은 카드 폭과 실제 줄바꿈을 관찰하세요.

이 절의 통합 다이어그램은 Flexbox의 컨테이너와 아이템을 축, 공간 분배, 정렬, 줄바꿈 순서로 정리합니다.