본문으로 건너뛰기

자주 쓰는 세 모양

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

목차

flex로 자주 만드는 모양은 셋입니다 — 양 끝 줄, 한가운데, 입력 칸과 단추 줄. 셋 모두 같은 세 걸음(1. 부모 정하기 → 2. 방향과 간격 → 3. 남는 자리와 정렬)으로 만들고, 걸음마다 부모에 쓸 선언과 자식에 쓸 선언이 정해져 있습니다.

justify-content · align-items · gap · flex: 1을 하나씩 알아도, 화면 앞에서는 무엇을 어느 요소에 먼저 써야 할지 막히곤 합니다. 선언을 외운 묶음으로 붙이면 모양이 조금만 달라져도 깨집니다. 차례가 있으면 처음 보는 모양도 같은 물음 셋으로 풀립니다. 이 레슨에서는 세 걸음의 물음, 그 걸음으로 만드는 세 모양, 한가운데가 안 될 때 볼 곳을 봅니다.

그림 준비 중

1세 걸음 — 부모 정하기 → 방향과 간격 → 남는 자리와 정렬

걸음묻는 것쓰는 선언
1. 부모 정하기나란히 놓을 요소들을 바로 담은 요소는?그 부모에 display: flex
2. 방향과 간격가로인가 세로인가, 사이는 얼마나?부모에 flex-direction · gap
3. 남는 자리와 정렬남는 자리는 어디로, 교차축은 어디에?부모에 justify-content · align-items, 늘어날 자식에 flex: 1

처음 값으로 되는 걸음은 비워 둡니다 — 가로로 놓을 때 flex-direction: row는 적지 않습니다. 세 모양에서 각 걸음이 쓰는 선언은 이렇습니다.

모양부모자식
양 끝 줄space-between · 세로 center그대로
한가운데두 축 center · 높이그대로
입력 칸과 단추gap입력 칸 flex: 1

양 끝 줄은 머리글 · 카드 제목 줄 · 할 일 화면 아랫줄에, 한가운데는 그림 칸의 로고처럼 높이를 정한 칸에, 입력 칸과 단추는 할 일 폼 · 찾기 줄에 씁니다.

2양 끝 줄 — 머리글

사이트 이름은 왼쪽 끝, 내비게이션은 오른쪽 끝, 둘의 세로 가운데를 맞춥니다. 머리글 끝이 창 폭에 따라 달라지니 폭 360px 창으로 그립니다.

<header class="site-header">
  <a href="index.html">
    레시피 노트
  </a>
  <nav>
    <a href="kimchi.html">
      김치볶음밥
    </a>
  </nav>
</header>
/* 1. 부모 정하기 */
.site-header {
  display: flex;
  /* 3. 남는 자리와 정렬 */
  justify-content:
    space-between;
  align-items: center;
}
결과 화면폭 360px
잰 값개발자 도구에서 본 값
  • .site-header > a의 왼쪽 자리8px
  • nav의 오른쪽 끝352px
  • .site-header의 align-itemscenter

1걸음은 사이트 이름과 nav를 바로 담은 header, 2걸음은 처음 값 row 그대로라 비웠고, 3걸음에서 남는 자리를 둘 사이로 보내고 교차축(세로)을 가운데에 맞췄습니다. 사이트 이름 글자를 크게 키워도 두 항목의 세로 가운데가 맞습니다.

3한가운데 — 그림 칸의 로고

카드의 그림 자리(200 × 150)에 아직 그림이 없을 때, 로고를 한가운데에 둡니다.

<div class="thumb">
  <img
    src="images/logo.svg"
    alt="레시피 노트"
    width="48"
    height="48"
  />
</div>
/* 1. 부모 정하기 */
.thumb {
  display: flex;
  /* 3. 남는 자리와 정렬 */
  justify-content: center;
  align-items: center;
  width: 200px;
  height: 150px;
}
결과 화면
잰 값개발자 도구에서 본 값
  • img의 왼쪽 자리84px
  • img의 위 자리59px

로고가 가로로 (200 − 48) ÷ 2 = 76px, 세로로 (150 − 48) ÷ 2 = 51px 들어가, 칸이 시작하는 8px에서 그만큼 떨어진 자리에 놓였습니다. 주축(가로)은 justify-content, 교차축(세로)은 align-items — 두 축을 모두 가운데로 해야 한가운데입니다. 세로로 옮길 자리는 부모의 높이 150px이 만듭니다.

4입력 칸과 단추 줄 — 할 일 폼

라벨은 줄 밖에 두고, 입력 칸과 단추만 한 줄에 놓습니다. 남는 폭은 입력 칸이 받습니다.

<form>
  <label for="task">할 일</label>
  <div class="input-row">
    <input
      id="task"
      name="task"
    />
    <button type="submit">
      추가
    </button>
  </div>
</form>
/* 1. 부모 정하기 */
.input-row {
  display: flex;
  /* 2. 방향과 간격 */
  gap: 8px;
}

/* 3. 남는 자리와 정렬 */
.input-row input {
  flex: 1;
}
결과 화면폭 360px
잰 값개발자 도구에서 본 값
  • button의 오른쪽 끝352px
  • .input-row input의 flex-grow1

1 · 2걸음은 부모 .input-row에, 3걸음의 flex: 1은 늘어날 자식인 입력 칸에 썼습니다. 단추가 줄 끝(352px)에 닿았고, 단추 글자가 길어지면 입력 칸이 그만큼 줄어듭니다. 할 일 화면의 폼 줄도 이 모양입니다.

예제

카드 제목 줄입니다. 양 끝 줄에 간격과 '줄지 않는 배지'까지 더해, 이름이 길어져도 배지가 오른쪽 끝을 지키게 합니다.

<div class="recipe-card-head">
  <h2>김치볶음밥</h2>
  <span class="badge">한식</span>
</div>
/* 1. 부모 정하기 */
.recipe-card-head {
  display: flex;
  /* 2. 방향과 간격 */
  gap: 12px;
  /* 3. 남는 자리와 정렬 */
  justify-content:
    space-between;
  align-items: center;
  width: 140px;
}

h2 {
  margin: 0;
}
결과 화면

배지에는 자식이 정하는 제 몫 하나(줄지 않기)를 씁니다.

.badge {
  flex-shrink: 0;
}
결과 화면
잰 값개발자 도구에서 본 값
  • .badge의 오른쪽 끝148px
  • .badge의 flex-shrink0

배지는 묶음 끝(8 + 140 = 148px)에 붙었습니다. 묶음이 좁아 자리가 모자라면 이름이 꺾여 두 줄이 되고, 배지는 flex-shrink: 0이라 제 너비로 한 줄을 지킵니다. 세 걸음을 차례로 지나면, 묶음에 쓸 선언 넷과 배지에 쓸 선언 하나가 저마다 제자리에 붙습니다.

자주 틀리는 곳

틀린 생각: 두 축을 center로 하면 언제나 한가운데에 온다.

3절 그림 칸에서 높이를 빼 봅니다.

<div class="thumb">
  <img
    src="images/logo.svg"
    alt="레시피 노트"
    width="48"
    height="48"
  />
</div>
.thumb {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 200px;
}
결과 화면
잰 값개발자 도구에서 본 값
  • .thumb의 높이48px
  • img의 위 자리8px

칸 높이가 로고 높이(48px)만큼으로 줄어, 로고는 가로로만 가운데이고 세로는 위 끝(8px)입니다. 세로로 남는 자리가 없습니다.

맞는 규칙: 정렬은 남는 자리를 옮길 뿐입니다. 한가운데에는 부모에 내용보다 큰 높이가 있어야 합니다(브라우저 규칙 6 — 블록 상자의 높이는 내용만큼).

틀린 생각: 부모 선언도 나란히 놓을 자식에게 쓰면 된다.

양 끝으로 보낼 것은 사이트 이름과 nav이니, 그 둘에 선언을 줘 봅니다.

<header class="site-header">
  <a href="index.html">
    레시피 노트
  </a>
  <nav>
    <a href="kimchi.html">
      김치볶음밥
    </a>
  </nav>
</header>
.site-header > a,
nav {
  display: flex;
  justify-content:
    space-between;
}
결과 화면
잰 값개발자 도구에서 본 값
  • .site-header의 줄 수2

머리글은 그대로 두 줄입니다. 사이트 이름 a와 nav는 저마다 제 안을 놓는 부모가 되었을 뿐, 둘을 나란히 놓을 부모 header에는 아무 선언도 없습니다.

맞는 규칙: 1걸음에서 고른 부모에 1 · 2 · 3걸음의 부모 선언을 씁니다. 자식에게 쓰는 것은 제 몫(flex: 1 · flex-shrink)뿐입니다(브라우저 규칙 7).

틀린 생각: 세 모양의 선언 묶음을 외워 두면 어디에나 붙일 수 있다.

옆 메뉴의 두 링크를 위 끝과 아래 끝에 두려고, 머리글의 양 끝 줄 묶음을 그대로 붙였습니다.

<nav class="side">
  <a href="index.html">
    레시피 목록
  </a>
  <a href="kimchi.html">
    김치볶음밥
  </a>
</nav>
.side {
  display: flex;
  justify-content:
    space-between;
  height: 120px;
}
결과 화면
잰 값개발자 도구에서 본 값
  • .side의 줄 수1

두 링크가 위아래가 아니라 가로 양 끝으로 갔습니다. 묶음에는 2걸음(방향)이 빠져 있었습니다 — 머리글은 가로라 비워 둔 걸음입니다.

맞는 규칙: 모양마다 세 걸음을 다시 묻습니다. 위아래 양 끝이면 2걸음에서 flex-direction: column⁠을 쓰고, 3걸음의 space-between이 세로로 나눕니다.

틀린 생각: flex 안에서도 위아래 바깥 여백은 겹쳐서 큰 쪽 하나만 남는다.

세로로 세운 두 칸 사이를 바깥 여백 12px씩으로 띄웠습니다.

<div class="col">
  <div class="a">이름 칸</div>
  <div class="b">별점 칸</div>
</div>
.col {
  display: flex;
  flex-direction: column;
}

.a {
  margin-bottom: 12px;
}

.b {
  margin-top: 12px;
}
결과 화면
잰 값개발자 도구에서 본 값
  • .b의 위 틈24px

틈이 12px이 아니라 24px입니다. 일반 흐름에서는 위아래 바깥 여백이 겹쳐 큰 쪽 하나만 남지만, flex 항목의 바깥 여백은 겹치지 않고 더해집니다.

맞는 규칙: 위아래 바깥 여백이 겹치는 것은 일반 흐름의 일입니다. flex 안의 간격은 바깥 여백이 아니라 2걸음의 gap으로 줍니다 — gap: 12px이면 틈이 꼭 12px입니다.

확인 문제

1. 3절 그림 칸에서 justify-content: center⁠를 flex-end로 바꿨습니다. 로고의 왼쪽 자리와 위 자리는 어떻게 될까요?

<div class="thumb">
  <img
    src="images/logo.svg"
    alt="레시피 노트"
    width="48"
    height="48"
  />
</div>
.thumb {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  width: 200px;
  height: 150px;
}
  • ① 왼쪽 자리 160px · 위 자리 59px
  • ② 왼쪽 자리 84px · 위 자리 110px
  • ③ 왼쪽 자리 160px · 위 자리 110px
힌트

바꾼 속성이 맡는 축이 가로인지 세로인지 먼저 정하고, 그 축의 자리만 다시 셉니다.

정답

① 왼쪽 자리 160px · 위 자리 59px

결과 화면
잰 값개발자 도구에서 본 값
  • img의 왼쪽 자리160px
  • img의 위 자리59px
  • justify-content는 주축(가로)을 맡습니다. flex-end면 로고가 칸 오른쪽 끝에 붙어 8 + 200 − 48 = 160px에서 시작합니다.
  • 교차축(세로)은 align-items: center 그대로라 위 자리는 59px입니다.
  • ②는 justify-content를 세로로 본 것이고, ③은 바꾼 값이 두 축 모두에 닿는다고 본 것입니다.

2. 할 일 화면의 아랫줄입니다. 남은 개수는 왼쪽 끝, 지우기 단추는 오른쪽 끝에 두고 세로 가운데를 맞추려 합니다. 세 걸음 가운데 3걸음을 통째로 쓰세요.

<footer class="footer">
  <p id="count" class="count">
    전체 3개 중 2개 남음
  </p>
  <button
    type="button"
    id="clear-done"
    class="clear"
  >
    완료한 일 지우기
  </button>
</footer>
/* 1. 부모 정하기 */
.footer {
  display: flex;
  /* 3. 남는 자리와 정렬 */
  ____
}
힌트

양 끝 줄 모양에서 부모가 3걸음에 쓰는 선언 둘을 1절 표에서 찾습니다.

정답
/* 1. 부모 정하기 */
.footer {
  display: flex;
  /* 3. 남는 자리와 정렬 */
  justify-content:
    space-between;
  align-items: center;
}

남는 자리를 개수와 단추 사이로 보내는 justify-content: space-between, 높이가 다른 두 항목의 세로 가운데를 맞추는 align-items: center 둘입니다. 2걸음은 처음 값 row라 비웠습니다.

3. 여행 일정 칸에서 날짜와 장소를 한가운데에 놓으려고 이렇게 썼습니다. 결과 화면에서 '부산'은 어디에 놓일까요?

<div class="trip">
  <div class="place">부산</div>
</div>
.trip {
  display: flex;
  height: 120px;
}

.place {
  justify-content: center;
  align-items: center;
  width: 80px;
  height: 40px;
}
  • ① 칸의 한가운데
  • ② 칸의 왼쪽 위 끝
  • ③ 칸의 가로 가운데, 위 끝
힌트

두 정렬 선언을 받은 요소가 무엇이고, 그 요소에게 놓을 항목이 있는지 봅니다.

정답

② 칸의 왼쪽 위 끝

결과 화면
잰 값개발자 도구에서 본 값
  • .place의 왼쪽 자리8px
  • .place의 위 자리8px
  • 정렬 선언 둘을 자식 .place에 썼습니다. .place는 flex를 받은 부모가 아니라 놓을 항목이 없어, 두 선언이 할 일이 없습니다. .trip은 처음 값대로 '부산'을 시작 쪽 위 끝에 둡니다(브라우저 규칙 7).
  • 고치려면 1걸음에서 고른 부모 .trip에 두 선언을 옮깁니다.
  • ①은 선언을 받은 요소를 따지지 않은 것이고, ③은 한 선언만 먹었다고 본 것입니다.

컴퓨터에서 해 보기(선택) — 점수에 들지 않습니다. 폰으로만 보고 있다면 건너뛰세요. hello.html의 body 안을 3절 그림 칸 코드로, style 요소 안을 3절 CSS로 바꿔 저장 · 새로고침합니다. height: 150px 줄을 지우고 다시 저장 · 새로고침하면, 로고가 칸의 위 끝으로 올라가 가로로만 가운데에 남습니다. 지운 높이를 되살리면 한가운데로 돌아옵니다. 그림 logo.svg가 없으면 아래에서 받아 images 폴더에 둡니다.

파일을 만들고 여는 법은 준비: VS Code와 작업 폴더에 있습니다.

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

HTML·CSS 목차로