본문으로 건너뛰기

flex는 부모에

HTML·CSS 목차한 줄로 나란히: flex레슨 64 / 93

목차

display: flex는 나란히 놓을 요소들의 부모에 줍니다. 그러면 그 부모의 직계 자식(항목)만 한 줄에 나란히 놓이고, 자식 안에 든 손자는 그대로입니다.

레시피 카드의 요리 이름 h2는 블록이라 한 줄을 통째로 차지하고, 옆에 두고 싶은 배지 '한식'은 그 아래 줄로 밀립니다. 머리글의 사이트 이름과 내비게이션도 두 줄로 쌓입니다. 일반 흐름은 블록을 위에서 아래로 쌓기만 하기 때문입니다. 둘을 한 줄에 두려면 놓는 방식을 바꿔야 합니다. 그 방식이 flex(flexbox — 항목을 한 방향으로 늘어놓고 자리를 나누는 배치)입니다. 그런데 display: flex를 어느 요소에 주느냐에 따라 나란해지는 것이 달라집니다. 이 레슨에서는 flex가 닿는 범위, flex를 줄 요소를 나무에서 고르는 법, 나란히 놓이지 않을 때 찾는 곳을 봅니다.

이 단원을 마치면 레시피 노트의 머리글을 한 줄로, 카드 제목과 배지를 양 끝으로, 후기 폼의 칸 둘을 한 줄로 놓을 수 있습니다. 이 단원(한 줄로 나란히: flex)을 이미 안다면 단원 연습 문제부터 풀고 틀린 레슨만 보세요. 건너뛸지 볼 때는 힌트를 펴지 않고 풉니다.

그림 준비 중

1부모에 display: flex — 직계 자식이 한 줄로

카드의 제목 묶음 div에는 요리 이름 h2와 배지 span이 들어 있습니다. 이 둘을 나란히 놓으려면 둘을 담은 div에 display: flex를 줍니다. 제목의 위아래 바깥 여백은 10단원의 레시피 카드처럼 지워 두었습니다.

<div class="recipe-card-head">
  <h2>떡볶이</h2>
  <span class="badge">한식</span>
</div>
.recipe-card-head {
  display: flex;
}

h2 {
  margin: 0;
}
결과 화면
잰 값개발자 도구에서 본 값
  • .recipe-card-head의 줄 수1
  • .badge의 displayblock

이름과 배지가 한 줄에 섰습니다. 잰 값의 '줄 수'는 보이는 자식이 몇 줄에 놓였는지 센 값입니다 — display: flex를 지우면 2입니다.

브라우저 규칙 7. 놓는 방식(일반 흐름 · flex · grid)은 부모가 정하고 직계 자식에게만 닿습니다 — 자식은 그 방식 안에서 제 몫(늘어남 · 차지할 칸)만 정합니다. 부모가 display: flex면 직계 자식이 그 규칙으로 놓입니다.

div·span과 블록·인라인에서 본 앞 절반(일반 흐름에서 블록은 쌓이고 인라인은 흐른다)에 이어지는 문장입니다. grid는 13단원에서 봅니다.

display: flex를 준 부모의 직계 자식을 항목(flex item)이라고 부릅니다. 부모는 자료에 따라 flex 컨테이너(flex container)라고 부릅니다. 인라인이던 배지 span도 항목이 되자 display가 block으로 바뀌었습니다. 항목은 블록 · 인라인이 아니라 부모의 flex 규칙을 따라 놓입니다.

2손자는 그대로 — 나란히 놓을 것들의 부모를 고른다

카드 전체 li에 display: flex를 주면 무엇이 나란해질까요? li의 직계 자식은 제목 묶음 div와 조리 시간 p 둘뿐입니다.

<li class="recipe-card">
  <div class="recipe-card-head">
    <h2>떡볶이</h2>
    <span>한식</span>
  </div>
  <p>조리 20분</p>
</li>
.recipe-card {
  display: flex;
}

.recipe-card h2,
.recipe-card p {
  margin: 0;
}
결과 화면
잰 값개발자 도구에서 본 값
  • .recipe-card의 줄 수1
  • .recipe-card-head의 줄 수2
  • .recipe-card span의 displayinline

제목 묶음과 조리 시간은 나란히 섰지만, 묶음 안의 이름과 배지는 그대로 두 줄입니다. 배지는 li의 손자라 li의 flex가 닿지 않고, display도 inline 그대로입니다.

그래서 flex를 줄 요소는 이 차례로 고릅니다.

  1. 나란히 놓을 요소를 먼저 찾습니다 — 이름 h2와 배지 span.
  2. 요소 나무에서 그 둘을 바로 담은 요소를 찾습니다 — div.recipe-card-head.
  3. 그 요소에 display: flex를 줍니다.

부모 안의 부모에도 따로 줄 수 있습니다. 머리글을 한 줄로 놓고 그 안의 nav도 링크를 한 줄로 놓으려면 header와 nav에 저마다 display: flex를 줍니다. flex가 손자에게 전해진 것이 아니라, 부모 둘이 저마다 제 자식을 놓는 것입니다.

3나란히 놓이지 않을 때 — 찾는 차례 다섯째

브라우저 기본 스타일과 Styles 창의 '안 먹을 때 찾는 차례'를 넷째(알아들었나 · 이겼나)까지 지났는데도 화면이 그대로면, 상자 네 겹에서 더한 다섯째 상자 · 배치를 봅니다. flex가 안 먹을 때 배치에서 볼 곳은 하나입니다 — display: flex를 준 요소가 나란히 놓을 요소들의 부모인가(브라우저 규칙 7).

  • Elements 패널에서 display: flex가 든 요소의 줄 끝에는 flex라는 작은 표시가 붙습니다. 그 줄을 펼쳐, 바로 한 겹 안의 자식이 나란히 놓을 요소인지 봅니다.
  • 그 요소를 골라 Computed 창의 display가 flex인지 봅니다. 규칙이 졌거나 버려졌으면 여기에 다른 값이 보입니다.

예제

레시피 노트의 머리글입니다. 머리글 header에 display: flex를 줬습니다. 결과 화면을 펴기 전에, 사이트 이름과 링크 둘이 어떻게 놓일지 먼저 적어 보세요.

<header class="site-header">
  <a href="index.html">
    레시피 노트
  </a>
  <nav>
    <a href="index.html">
      레시피 목록
    </a>
    <a href="kimchi.html">
      김치볶음밥
    </a>
  </nav>
</header>
.site-header {
  display: flex;
}
결과 화면 보기
결과 화면
잰 값개발자 도구에서 본 값
  • .site-header의 줄 수1
  • .site-header > a의 displayblock
  • nav a의 displayinline

셋이 한 줄에 섰습니다. 사이트 이름 a와 nav는 머리글의 항목이라 나란히 놓였고, 항목 사이 줄바꿈은 그려지지 않아 둘이 빈틈없이 붙었습니다. nav 안 두 링크는 머리글의 손자라 인라인 그대로 — 그 사이 빈칸 한 칸은 인라인 사이의 줄바꿈이 그린 것입니다. 이름과 내비게이션을 양 끝으로 떼는 일은 주축 정렬에서 합니다.

자주 틀리는 곳

틀린 생각: display: flex를 주면 그 안의 모든 요소가 나란히 놓인다.

flex를 준 요소 안이 모두 가로로 바뀔 것 같습니다(flex가 후손까지라는 생각).

<div class="a">
  <div>가</div>
  <div class="b">
    <div>나</div>
    <div>다</div>
  </div>
</div>
.a {
  display: flex;
}
결과 화면
잰 값개발자 도구에서 본 값
  • .a의 줄 수1
  • .b의 줄 수2

'가'와 .b는 한 줄에 섰지만, .b 안의 '나' · '다'는 그대로 쌓였습니다.

맞는 규칙: flex는 직계 자식에게만 닿습니다. 손자를 나란히 놓으려면 그 손자의 부모(.b)에도 display: flex를 줍니다(브라우저 규칙 7).

틀린 생각: 나란히 놓을 요소에 저마다 display: flex를 준다.

이름과 배지를 옆으로 보내고 싶으니, 그 둘에게 flex를 주면 될 것 같습니다.

<div class="recipe-card-head">
  <h2>떡볶이</h2>
  <span class="badge">한식</span>
</div>
h2,
.badge {
  display: flex;
}

h2 {
  margin: 0;
}
결과 화면
잰 값개발자 도구에서 본 값
  • .recipe-card-head의 줄 수2
  • .badge의 displayflex

이름과 배지는 그대로 두 줄이고, 배지는 오히려 한 줄을 통째로 차지했습니다. flex를 받은 h2와 배지는 제 안의 글을 놓는 부모가 되었을 뿐, 저 자신은 블록처럼 쌓입니다.

맞는 규칙: display: flex는 받은 요소의 자식을 놓습니다. 나란히 놓을 요소들이 아니라 그 둘을 담은 부모에 줍니다(브라우저 규칙 7).

틀린 생각: flex 항목 사이에도 HTML의 줄바꿈이 빈칸 한 칸을 만든다.

인라인 사이의 줄바꿈은 빈칸 한 칸으로 그려지니, 항목 사이도 그럴 것 같습니다.

<div class="tags">
  <span>밥</span>
  <span>김치</span>
</div>
.tags {
  display: flex;
}

.tags span {
  width: 40px;
}
결과 화면
잰 값개발자 도구에서 본 값
  • .tags span:first-child의 오른쪽 끝48px
  • .tags span:last-child의 왼쪽 자리48px

첫 항목이 끝난 자리(48px)에서 둘째 항목이 바로 시작합니다. 둘 사이에 빈칸이 없습니다.

맞는 규칙: 항목 사이처럼 빈칸만 있는 글은 그려지지 않습니다(브라우저 규칙 2). 블록과 인라인 display에서 본 인라인 블록 사이의 틈도 flex로 놓으면 생기지 않습니다. 항목 사이를 띄우려면 부모에 gap을 줍니다(gap과 flex: 1).

확인 문제

1. 예제 CSS의 선택자 .site-header를 nav로 바꿨습니다. 결과 화면에서 사이트 이름과 링크 둘은 어떻게 놓일까요?

<header class="site-header">
  <a href="index.html">
    레시피 노트
  </a>
  <nav>
    <a href="index.html">
      레시피 목록
    </a>
    <a href="kimchi.html">
      김치볶음밥
    </a>
  </nav>
</header>
nav {
  display: flex;
}
  • ① 두 줄 — 위에 '레시피 노트', 아래에 링크 둘이 붙어서 한 줄
  • ② 한 줄 — 사이트 이름 옆에 링크 둘
  • ③ 세 줄 — 사이트 이름, 링크, 링크가 한 줄씩
힌트

이제 display: flex를 받은 요소는 어느 것인지, 그 요소의 직계 자식은 무엇인지 나무에서 찾습니다.

정답

① — 위에 '레시피 노트', 아래 줄에 링크 둘이 붙어서 섭니다.

결과 화면
잰 값개발자 도구에서 본 값
  • .site-header의 줄 수2
  • nav의 줄 수1
  • nav a의 displayblock
  • nav의 직계 자식인 링크 둘이 항목이 되어 한 줄에 섰습니다. 항목 사이 줄바꿈은 그려지지 않아 두 링크가 붙었습니다(브라우저 규칙 2).
  • 머리글은 이제 flex가 아니라 일반 흐름이라, 블록 nav가 사이트 이름 아래 새 줄에서 시작합니다(브라우저 규칙 7).
  • ②는 flex를 받은 nav 자신이 옆으로 간다고 본 것입니다. flex는 받은 요소의 자식을 놓습니다.
  • ③은 항목의 display가 block이 되니 쌓인다고 본 것입니다. 항목은 블록처럼 쌓이지 않고 부모의 flex 규칙으로 놓입니다.

2. 재료 줄의 이름과 양을 한 줄에 놓으려 합니다. ____ 자리에 알맞은 선택자를 쓰세요.

<ul class="ingredient-list">
  <li>
    <p>밥</p>
    <p>2공기</p>
  </li>
</ul>
____ {
  display: flex;
}
힌트

나란히 놓을 두 요소를 먼저 찾고, 나무에서 그 둘을 바로 담은 요소를 찾습니다.

정답

li — .ingredient-list li로 써도 됩니다.

<ul class="ingredient-list">
  <li>
    <p>밥</p>
    <p>2공기</p>
  </li>
</ul>
.ingredient-list li {
  display: flex;
}
결과 화면
잰 값개발자 도구에서 본 값
  • .ingredient-list li의 줄 수1

나란히 놓을 것은 '밥'과 '2공기'의 p 둘이고, 둘을 바로 담은 부모는 li입니다. .ingredient-list(ul)에 주면 ul의 자식인 li가 항목이 되어, li 안의 두 p는 그대로 쌓입니다. p에 주면 저마다 제 안의 글만 놓습니다.

3. 동아리 공지를 이렇게 썼습니다. 결과 화면에는 어떻게 보일까요?

<div class="notice">
  <p>동아리 공지</p>
  <ul>
    <li>10월 20일</li>
    <li>3층 동아리방</li>
  </ul>
</div>
.notice {
  display: flex;
}
  • ① 한 줄 — '동아리 공지 10월 20일 3층 동아리방'
  • ② '동아리 공지' 옆에 목록이 서고, 목록의 두 줄은 위아래로
  • ③ 그대로 세 줄 — 공지, 날짜, 장소가 위아래로
힌트

나무를 그려 display: flex가 걸린 요소에 표시하고, 요소마다 무엇 안에 드는지 적어 보세요.

힌트 둘

flex는 display: flex를 준 요소의 직계 자식만 나란히 놓습니다. .notice의 직계 자식이 무엇무엇인지 세고, 날짜와 장소는 누구의 자식인지 봅니다.

정답

② — '동아리 공지' 옆에 목록이 서고, 날짜와 장소는 목록 안에서 위아래로 놓입니다.

결과 화면
잰 값개발자 도구에서 본 값
  • .notice의 줄 수1
  • ul의 줄 수2
  • .notice의 항목은 p와 ul 둘이라 둘이 나란히 섭니다. 날짜와 장소의 li는 ul의 자식, 곧 .notice의 손자라 그대로 쌓입니다(브라우저 규칙 7).
  • ①은 flex가 그 안의 모든 요소를 나란히 놓는다고 본 것입니다(flex가 후손까지라는 생각).
  • ③은 display: flex가 블록을 나란히 놓지 못한다고 본 것입니다. 항목은 블록이어도 한 줄에 섭니다.

컴퓨터에서 해 보기(선택) — 점수에 들지 않습니다. 폰으로만 보고 있다면 건너뛰세요. hello.html의 body 안을 2절 카드 코드로 바꾸고, style 요소 안에 2절 CSS를 넣어 저장 · 새로고침합니다. 그다음 .recipe-card 규칙의 display: flex를 손자인 배지에게 줘 봅니다 — 선택자를 .recipe-card span으로 바꿔 저장 · 새로고침하면, 배지만 제 줄을 통째로 차지하고 제목 옆으로 오지 않습니다. 마지막으로 선택자를 .recipe-card-head로 바꾸면 배지가 이름 옆에 섭니다. Elements 패널에서 flex 표시가 붙는 줄도 함께 봅니다. 파일을 만들고 여는 법은 준비: VS Code와 작업 폴더에 있습니다.

끝까지 내려오면 저절로 표시됩니다. 이 기기에만 저장합니다.

HTML·CSS 목차로