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-direction
과 flex-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-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;
는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 속성들을 활용하여 실제 웹 페이지의 흔한 레이아웃인 네비게이션 바와 카드 목록을 만들어 봅시다.
-
프로젝트 폴더 구조
web-dev-practice/ ├── flexbox_layout.html └── css/ └── 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"> <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>© 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의 조합으로 이루어진 것입니다.
- Live Server를 통해
이번 장에서는 현대 웹 레이아웃의 핵심 기술인 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도 쉽게 구성할 수 있는 기반을 다지게 되었습니다.