icon
3장 : CSS 기초와 레이아웃

Flexbox 레이아웃

지난 장에서 우리는 모든 HTML 요소가 '박스 모델'이라는 개념을 기반으로 하고 있으며, display 속성을 통해 요소들의 기본적인 배치 방식을 제어할 수 있음을 학습했습니다. display: block, display: inline, display: inline-block은 간단한 레이아웃에는 유용하지만, 복잡한 UI(User Interface) 요소들을 유연하게 정렬하고 배치하는 데는 한계가 있습니다. 특히 다양한 화면 크기에 맞춰 요소들을 재배치해야 하는 반응형 웹 디자인에서는 더욱 그렇습니다.

여기서 등장하는 것이 바로 Flexbox(Flexible Box Layout) 입니다. Flexbox는 1차원 레이아웃(한 줄 또는 한 열)을 위해 설계된 CSS3의 새로운 레이아웃 모듈입니다. 부모 요소(컨테이너) 안의 자식 요소(아이템)들을 효율적으로 정렬, 분배, 배치할 수 있는 강력한 기능을 제공하여, 복잡하고 유연한 레이아웃을 훨씬 쉽게 구현할 수 있도록 돕습니다.

이 장에서는 Flexbox의 기본 개념과 주요 속성들을 상세하게 학습하고, 실습을 통해 Flexbox의 강력함을 직접 경험해 볼 것입니다. Flexbox를 마스터하면 웹 레이아웃 구현 능력이 한 단계 성장할 것입니다.


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

Flexbox는 부모-자식 관계를 기반으로 작동합니다. Flexbox 레이아웃을 적용하려는 영역을 Flex 컨테이너(Flex Container) 로 만들고, 그 안에 있는 직계 자식 요소들이 Flex 아이템(Flex Item) 이 됩니다.

  • Flex 컨테이너: display: flex 또는 display: inline-flex 속성이 적용된 부모 요소입니다. 컨테이너에 적용하는 속성들은 그 안에 있는 Flex 아이템들의 전체적인 배치와 정렬에 영향을 미칩니다.
  • Flex 아이템: Flex 컨테이너의 직계 자식 요소들입니다. 아이템에 직접 적용하는 속성들은 개별 아이템의 크기나 정렬 방식에 영향을 줍니다.
  • 주축 (Main Axis): Flex 아이템들이 배열되는 주된 방향입니다. 기본값은 가로(row)입니다.
  • 교차축 (Cross Axis): 주축에 수직인 방향입니다. 주축이 가로이면 교차축은 세로(column)가 됩니다.

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

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

display

  • 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 (기본값): 아이템들이 컨테이너의 교차축 길이에 맞춰 늘어납니다. (height 속성이 지정되지 않은 경우)
  • flex-start: 교차축의 시작 지점으로 아이템들을 정렬합니다.
  • flex-end: 교차축의 끝 지점으로 아이템들을 정렬합니다.
  • center: 교차축의 중앙으로 아이템들을 정렬합니다.
  • baseline: 아이템들의 텍스트 기준선(baseline)에 맞춰 정렬합니다.
.container {
    display: flex;
    align-items: center; /* 아이템들을 세로 중앙 정렬 */
    height: 200px; /* align-items를 보려면 컨테이너에 높이 지정 필요 */
}

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

flex-wrap: wrap이 적용되어 Flex 아이템들이 여러 줄로 나뉘었을 때, 그 여러 줄 자체를 교차축 방향으로 정렬합니다. 한 줄일 때는 효과가 없습니다.

  • 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-directionflex-wrap을 한 번에 설정하는 단축 속성입니다.

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

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

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

order (순서 변경)

Flex 아이템의 시각적 순서를 변경합니다. 기본값은 0이며, 숫자가 낮을수록 먼저 표시됩니다.

.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으로 설정하면 줄어들지 않습니다.

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

flex-basis (기본 크기)

Flex 아이템이 flex-growflex-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;flex: 1 1 0%;와 동일합니다. (늘어나고, 줄어들고, 기본 크기는 0)
  • 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 속성들을 활용하여 실제 웹 페이지의 흔한 레이아웃인 네비게이션 바와 카드 목록을 만들어 봅시다.

  1. 프로젝트 폴더 구조

    web-dev-practice/
    ├── flexbox_layout.html    
    └── css/
        └── flex.css
  2. 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">
                        <img src="https://via.placeholder.com/200x150?text=Product+1" alt="제품 1">
                        <h3>제품명 1</h3>
                        <p>이 제품은 뛰어난 품질과 디자인을 자랑합니다.</p>
                        <span class="price">49,000원</span>
                    </div>
                    <div class="card">
                        <img src="https://via.placeholder.com/200x150?text=Product+2" alt="제품 2">
                        <h3>제품명 2</h3>
                        <p>일상에 편리함을 더해주는 필수 아이템입니다.</p>
                        <span class="price">75,000원</span>
                    </div>
                    <div class="card">
                        <img src="https://via.placeholder.com/200x150?text=Product+3" alt="제품 3">
                        <h3>제품명 3</h3>
                        <p>놀라운 기능과 합리적인 가격으로 만나보세요.</p>
                        <span class="price">120,000원</span>
                    </div>
                    <div class="card">
                        <img src="https://via.placeholder.com/200x150?text=Product+4" alt="제품 4">
                        <h3>제품명 4</h3>
                        <p>새로운 경험을 선사할 프리미엄 제품.</p>
                        <span class="price">99,000원</span>
                    </div>
                    <div class="card wide-card">
                        <img src="https://via.placeholder.com/200x150?text=Special+Product" alt="특별 제품">
                        <h3>특별 할인 제품</h3>
                        <p>지금 구매하시면 특별한 혜택이 기다리고 있습니다! 놓치지 마세요.</p>
                        <span class="price">150,000원</span>
                    </div>
                </div>
            </section>
        </main>
    
        <footer>
            <p>&copy; 2025 Flexbox 연습. 모든 권리 보유.</p>
        </footer>
    </body>
    </html>
  3. 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%; /* 모바일에서 넓은 카드 너비 조정 */
        }
    }
  4. 결과 확인

    • 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도 쉽게 구성할 수 있는 기반을 다지게 되었습니다.