CSS · 레이아웃 — Flexbox와 Grid
요소를 어디에·어떻게 배치할지 정하는 것이 레이아웃입니다. CSS? 에는 한 줄로 나란히 세우는 Flexbox, 바둑판처럼 칸을 짜는 Grid, 그리고 요소를 띄워 겹치는 position 이 있습니다.
레이아웃 CSS가 있고 없고의 차이를 먼저 눈으로 보세요. 똑같은 박스 세 개인데, 왼쪽은 그냥 세로로 쌓이고(기본), 오른쪽은 display:flex 한 줄로 가로 정렬됩니다.
개념마다 직접 고쳐 실행할 수 있는 라이브 데모가 있습니다. 안의 코드를 바꾸고 실행을 눌러 보세요. 값을 직접 만져 봐야 레이아웃이 손에 익습니다.
1. 일반 흐름(normal flow)
아무 배치 속성도 안 주면 블록 요소(div 등)는 위에서 아래로 한 줄씩 쌓이고, 인라인 요소(span 등)는 왼쪽에서 오른쪽으로 흐릅니다 — 이 기본 동작을 일반 흐름이라 부릅니다.
2. Flexbox — 한 줄(또는 한 열) 정렬의 왕
무엇: 부모에 display:flex 를 주면 그 부모는 플렉스 컨테이너가 되고, 직계 자식들은 플렉스 아이템이 되어 한 축(가로 또는 세로)을 따라 늘어섭니다. 정렬·간격·줄바꿈을 속성 몇 개로 끝낼 수 있습니다.
방향 — flex-direction
무엇: 아이템이 늘어서는 축(주축, main axis)을 정한다. row(기본·가로), column(세로), 그리고 역방향 row-reverse / column-reverse.
.wrap {
display: flex;
flex-direction: row; /* 가로 한 줄 (기본값) */
}아래에서 flex-direction 을 column 으로 바꿔 세로로 세워 보세요.
주축 정렬 — justify-content
무엇: 주축(가로 기본) 방향으로 아이템들을 어떻게 분배할지. 자주 쓰는 값:
| 값 | 뜻 |
|---|---|
flex-start | 시작 쪽으로 몰기 (기본) |
center | 가운데로 모으기 |
flex-end | 끝 쪽으로 몰기 |
space-between | 양 끝에 딱 붙이고 사이를 균등하게 |
space-around | 각 아이템 양옆에 같은 여백 |
space-evenly | 모든 틈(끝 포함)을 완전히 동일하게 |
justify-content 값을 위 표의 다른 값으로 바꿔 보세요.
교차축 정렬 — align-items
무엇: 주축과 직각인 축(교차축, cross axis)으로 정렬. row 일 때는 세로 정렬을 뜻한다. 값: stretch(기본·꽉 채움), flex-start, center, flex-end, baseline(글자 밑선 맞춤).
컨테이너 높이가 큰 상황에서 align-items 를 center/flex-end 로 바꿔 보세요.
완전 가운데 정렬 — 가장 유명한 비법
무엇: justify-content:center (가로 가운데) + align-items:center (세로 가운데) 를 함께 주면 자식이 정확히 한가운데에 옵니다. 예전엔 어렵던 일이 두 줄로 끝납니다.
간격 — gap
무엇: 아이템 사이의 틈을 한 번에 준다. 예전엔 margin 으로 일일이 줬지만 이제 gap 한 줄이면 됩니다. gap: 세로 가로 로 두 값도 가능.
.wrap {
display: flex;
gap: 12px; /* 모든 아이템 사이 12px */
/* gap: 8px 20px; 세로 8px, 가로 20px */
}줄바꿈 — flex-wrap
무엇: 기본값 nowrap 은 한 줄에 욱여넣어 아이템을 찌그러뜨린다. wrap 을 주면 공간이 부족할 때 다음 줄로 넘김. 카드 목록에 필수.
아래 데모에서 flex-wrap 을 nowrap 으로 바꾸면 카드가 한 줄에 눌려 들어가는 걸 볼 수 있어요.
아이템 속성 — flex / order / align-self
무엇: 컨테이너가 아니라 아이템 각각에 주는 속성들.
flex: 1— 남는 공간을 나눠 차지. 여러 아이템에flex:1을 주면 똑같이 늘어남. (flex-grow flex-shrink flex-basis의 단축)flex-grow— 남는 공간을 차지하는 비율(기본 0=안 늘어남).2면1짜리의 두 배로 늘어남.flex-shrink— 공간이 모자랄 때 줄어드는 비율(기본 1).0이면 안 줄어듦.flex-basis— 늘이고 줄이기 전의 기본 크기(예:200px, 기본auto).order— 화면에 그려지는 순서(기본 0). 작을수록 앞. HTML 순서를 바꾸지 않고 보이는 순서만 조정.align-self— 그 아이템만 교차축 정렬을 따로(컨테이너의align-items무시).
가운데 박스에만 flex:1 을 줘서 남는 공간을 다 먹게 했습니다. 숫자를 flex:2 로 바꿔 보세요.
order 와 align-self 도 한 번에 — "3"이 order:-1 로 맨 앞에 오고, 자기만 위로 붙습니다.
3. Grid — 가로·세로 칸을 짜는 2차원 레이아웃
무엇: 부모에 display:grid 를 주면 그리드 컨테이너가 된다. Flexbox가 한 줄(1차원)에 강하다면, Grid는 행과 열(2차원)을 동시에 짜는 데 강하다. 칸 구조를 미리 정의하고 그 안에 아이템을 배치한다.
열 정의 — grid-template-columns
무엇: 몇 칸을 어떤 너비로 만들지. 단위가 핵심:
200px— 고정 너비1fr— 남는 공간의 비율(fraction).1fr 1fr은 반반,2fr 1fr은 2:1repeat(3, 1fr)—1fr 1fr 1fr의 줄임. "3칸 균등"을 짧게
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr); /* 균등 3칸 */
gap: 12px; /* 칸 사이 간격 */
}repeat(3, 1fr) 의 숫자를 4 로 바꾸면 4칸이 됩니다. 2fr 1fr 1fr 처럼 비율도 섞어 보세요.
행 정의와 자동 채움 — grid-template-rows · auto-fit
무엇: grid-template-rows 는 행 높이를 정한다. 칸 개수를 화면 너비에 맞게 자동 조절하려면 repeat(auto-fit, minmax(150px, 1fr)) 패턴을 쓴다 — "최소 150px, 들어갈 수 있는 만큼 칸을 만들어라". 갤러리에 안성맞춤.
아래는 자동으로 칸 수가 늘고 줄어드는 카드 갤러리. 크게 보기 로 넓혀 칸이 늘어나는 걸 확인해 보세요.
칸 합치기 — grid-column / grid-row span
무엇: 아이템 하나가 여러 칸을 차지하게 한다. grid-column: span 2 는 "가로로 2칸", grid-row: span 2 는 "세로로 2칸". grid-column: 1 / 3 처럼 몇 번째 선부터 몇 번째 선까지로도 지정할 수 있다(칸이 아니라 칸 사이의 선 번호).
첫 칸은 가로 2칸, 둘째 칸은 세로 2칸을 차지하게 했습니다. span 숫자를 바꿔 보세요.
이름표로 배치 — grid-template-areas
무엇: 칸마다 이름을 붙여 그림 그리듯 레이아웃을 짠다. 컨테이너에서 grid-template-areas 로 지도를 그리고, 각 아이템에 grid-area: 이름 을 주면 그 자리에 들어간다. 같은 이름을 여러 칸에 쓰면 그만큼 영역이 합쳐진다. 점(.)은 빈 칸.
아래는 헤더 · 사이드바 · 본문 구조. 따옴표 안의 지도를 바꿔 배치를 재구성해 보세요.
4. position — 흐름에서 떼어내 자유 배치
무엇: 요소를 일반 흐름에서 띄워 원하는 곳에 둔다. top · right · bottom · left 로 위치를, z-index 로 겹침 순서를 조정한다. 다섯 가지 값을 알아 두면 충분하다.
| 값 | 뜻 | 기준 |
|---|---|---|
static | 기본. 일반 흐름 그대로(top/left 무시) | — |
relative | 원래 자리에서 살짝 이동. 자리는 그대로 차지 | 자기 원래 위치 |
absolute | 흐름에서 빠져 자유 배치 | 가장 가까운 relative(등) 조상 |
fixed | 화면(뷰포트)에 고정. 스크롤해도 안 움직임 | 뷰포트 |
sticky | 평소엔 흐름대로, 스크롤하다 정해진 위치에서 달라붙음 | 스크롤 컨테이너 |
position:absolute 자식은 가장 가까운 position이 static이 아닌 조상을 기준으로 배치됩니다. 그래서 보통 부모에 position:relative 를 먼저 주고, 자식에 absolute 를 줍니다. 이걸 안 하면 화면 전체를 기준으로 튀어 버립니다.
카드 모서리에 배지를 absolute 로 붙인 예. 부모가 relative 라 카드 기준으로 배치됩니다. top/right 값을 바꿔 보세요.
position:sticky 헤더 — 안쪽 영역을 스크롤하면 "고정 헤더"가 위에 달라붙습니다. top:0 이 달라붙는 위치입니다.
z-index 는 겹친 요소의 앞뒤 순서를 정합니다. 숫자가 클수록 위로 올라옵니다. position 이 static이 아닌 요소에서만 작동합니다.
5. float · clear — 옛 방식 (참고만)
무엇: 원래 float 은 이미지 옆에 글자를 흐르게 하려고 만든 속성인데, 과거엔 이걸로 전체 레이아웃까지 짰습니다. clear 는 float을 해제하는 짝입니다. 지금은 레이아웃에 거의 쓰지 않습니다 — flex 나 grid 가 훨씬 쉽고 정확합니다.
img { float: left; } /* 이미지를 왼쪽에, 글자는 그 오른쪽으로 흐름 */
.footer { clear: both; } /* 위의 float 영향에서 벗어남 */float 로 짠 레이아웃은 높이 붕괴(clearfix 필요) 등 골치 아픈 문제가 많습니다. 글을 이미지 주위로 감싸는 본래 용도 외에는 flex/grid 를 쓰세요.
6. 실전 — 헤더·사이드바·본문 레이아웃
실무에서 가장 흔한 화면 구조(상단 헤더 + 좌측 사이드바 + 우측 본문)를 Grid로 짜면 단 몇 줄입니다. grid-template-areas 로 지도를 그리는 방식이 읽기 쉽습니다.
사이드바(nav)를 본문과 푸터 두 행에 걸쳐 같은 이름으로 적었기 때문에 세로로 길게 이어집니다. 지도 문자열만 바꾸면 구조가 통째로 바뀝니다.
한눈에 — Flex/Grid 속성 카탈로그
| 대상 | 속성 | 역할 |
|---|---|---|
| Flex 컨테이너 | display:flex | 플렉스 켜기 |
| Flex 컨테이너 | flex-direction | 주축 방향 (row/column) |
| Flex 컨테이너 | justify-content | 주축 정렬·분배 |
| Flex 컨테이너 | align-items | 교차축 정렬 |
| Flex 컨테이너 | flex-wrap | 줄바꿈 허용 |
| Flex/Grid 공통 | gap | 아이템 사이 간격 |
| Flex 아이템 | flex | grow·shrink·basis 단축 |
| Flex 아이템 | order | 보이는 순서 |
| Flex 아이템 | align-self | 자기만 교차축 정렬 |
| Grid 컨테이너 | display:grid | 그리드 켜기 |
| Grid 컨테이너 | grid-template-columns | 열 정의 (fr·px·repeat) |
| Grid 컨테이너 | grid-template-rows | 행 정의 |
| Grid 컨테이너 | grid-template-areas | 이름표로 영역 배치 |
| Grid 아이템 | grid-column / grid-row | 칸 합치기 (span·선번호) |
| Grid 아이템 | grid-area | 영역 이름 지정 |
| 위치 | position | static/relative/absolute/fixed/sticky |
| 위치 | top·right·bottom·left | 좌표 이동 |
| 위치 | z-index | 겹침 순서 |
Flex는 한 방향(1차원). 메뉴 한 줄, 버튼 묶음, 카드 안의 가로 정렬처럼 "한 줄로 늘어놓고 정렬"이 핵심일 때.
Grid는 두 방향(2차원). 페이지 전체 골격(헤더·사이드바·본문), 카드 갤러리, 표 같은 격자처럼 "행과 열을 동시에" 짜야 할 때.
둘은 섞어 씁니다: 페이지 큰 틀은 Grid로, 그 안 헤더의 로고·메뉴 정렬은 Flex로 — 가 흔한 조합입니다.
레이아웃으로 배치를 끝냈다면, 다음은 글자·색·배경을 다듬을 차례입니다. 타이포 · 색 · 배경 으로 이어집니다.