본문으로 건너뛰기

gap과 flex: 1

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

목차

항목 사이를 띄우는 간격은 부모의 gap으로 줍니다 — 항목 사이에만 들고 마지막 항목 뒤에는 생기지 않습니다. 남는 자리를 한 항목에 몰아주려면 늘어날 자식에 flex: 1을 줍니다.

내비게이션 링크마다 오른쪽 바깥 여백을 주면 링크 사이는 띄워지지만, 마지막 링크 뒤에도 여백이 남아 줄 끝이 들쭉날쭉해집니다. 입력 칸과 추가 단추를 한 줄에 놓을 때는 단추는 제 크기로 두고 입력 칸만 남는 폭까지 늘리고 싶습니다. 둘 다 부모와 자식이 맡을 일을 나눠 주면 풀립니다. 이 레슨에서는 간격을 맡는 gap, 남는 자리를 받는 flex: 1, 그리고 줄어드는 몫을 정하는 flex-shrink를 봅니다.

그림 준비 중

1사이는 부모의 gap — 마지막 뒤에는 없다

항목 둘을 오른쪽 끝에 모으고(flex-end), 항목마다 margin-right: 16px로 띄워 봅니다.

<div class="menu">
  <span>가</span>
  <span>나</span>
</div>
.menu {
  display: flex;
  justify-content: flex-end;
  width: 200px;
}

.menu span {
  margin-right: 16px;
}
결과 화면
잰 값개발자 도구에서 본 값
  • .menu span:last-child의 오른쪽 끝192px

상자는 208px에서 끝나는데 '나'는 192px에서 끝납니다. 마지막 항목 뒤에도 바깥 여백 16px이 붙어, 끝에 붙여도 그만큼 떨어졌습니다. 같은 간격을 부모의 gap으로 줍니다.

<div class="menu">
  <span>가</span>
  <span>나</span>
</div>
.menu {
  display: flex;
  justify-content: flex-end;
  gap: 16px;
  width: 200px;
}
결과 화면
잰 값개발자 도구에서 본 값
  • .menu span:last-child의 오른쪽 끝208px

gap은 이웃한 항목 사이에만 들어, '나'가 상자 끝(208px)에 붙었습니다.

쓰는 것주는 요소만드는 자리
padding부모부모 테두리와 내용 사이
margin항목그 항목의 바깥
gapflex를 준 부모이웃한 항목 사이

margin이 틀린 속성은 아닙니다. 항목 하나만 떼어 둘 때는 여전히 바깥 여백을 씁니다. 모든 항목 사이를 고르게 띄울 때 gap이 맞습니다.

2남는 자리는 늘어날 자식의 flex: 1

너비 240px 상자에 '가'와 너비 60px인 '나'를 놓고, '가'에만 flex: 1을 줍니다.

<div class="row">
  <div class="a">가</div>
  <div class="b">나</div>
</div>
.row {
  display: flex;
  width: 240px;
}

.a {
  flex: 1;
}

.b {
  width: 60px;
}
결과 화면
잰 값개발자 도구에서 본 값
  • .a의 너비180px
  • .a의 flex-grow1
  • .a의 flex-shrink1
  • .a의 flex-basis0%

'가'가 '나'를 뺀 남는 자리를 모두 받아 180px이 되었습니다. flex: 1은 짧게 적기 한 줄이라 풀어 적은 속성 셋에 한꺼번에 값을 냅니다(브라우저 규칙 4) — 여백 짧게 적기의 margin과 같은 꼴입니다.

풀어 적은 속성flex: 1이 낸 값뜻
flex-grow1남는 자리를 받는 몫
flex-shrink1자리가 모자랄 때 줄어드는 몫
flex-basis0%나누기 전에 잡는 크기

나누기 전 크기가 0이라, 두 항목에 모두 flex: 1을 주면 남는 자리를 같은 몫으로 나눠 너비가 같아집니다. 늘어나는 것은 항목 자신의 몫이라 부모가 아니라 늘어날 자식에 씁니다(브라우저 규칙 7 — 자식은 제 몫만 정합니다).

예제

입력 칸과 추가 단추를 한 줄에 놓습니다. 부모는 줄과 간격을, 입력 칸은 늘어날 몫을 맡습니다.

<form class="add">
  <input
    type="text"
    aria-label="할 일"
  />
  <button type="submit">
    추가
  </button>
</form>
.add {
  display: flex;
  gap: 8px;
  width: 280px;
}

input {
  flex: 1;
}
결과 화면
잰 값개발자 도구에서 본 값
  • button의 오른쪽 끝288px
  • input의 flex-grow1

단추가 줄 끝(8 + 280 = 288px)에 닿았습니다. 입력 칸이 단추와 간격 8px을 뺀 자리를 모두 가져갔기 때문입니다. 단추 글자가 길어지면 단추가 넓어지고, 입력 칸이 그만큼 줄어듭니다 — 입력 칸 너비를 셈해 적을 필요가 없습니다. 할 일 화면의 폼 줄도 같은 꼴입니다 — #todo-form에 display: flex와 gap: 8px, 입력 칸 #todo-input에 flex: 1을 줍니다.

자주 틀리는 곳

틀린 생각: flex: 1을 준 칸은 긴 글자가 들어도 남는 자리 안에서 줄어든다.

공유 주소 칸과 복사 단추를 너비 160px 줄에 놓았습니다. 주소는 빈칸 없는 긴 글자 하나입니다.

<div class="share">
  <p>
    example.com/recipes/kimchi
  </p>
  <button type="button">
    복사
  </button>
</div>
.share {
  display: flex;
  width: 160px;
}
결과 화면일부러 틀린 코드
잰 값개발자 도구에서 본 값
  • p의 min-widthauto

주소가 줄을 밀어내, 복사 단추가 상자 밖으로 튀어나갔습니다. flex 항목의 최소 너비(min-width)는 처음 값 auto라, 꺾을 수 없는 가장 긴 글자보다 작아지지 않습니다.

<div class="share">
  <p>
    example.com/recipes/kimchi
  </p>
  <button type="button">
    복사
  </button>
</div>
.share {
  display: flex;
  width: 160px;
}

p {
  min-width: 0;
  overflow-wrap: anywhere;
}
결과 화면
잰 값개발자 도구에서 본 값
  • p의 min-width0px
  • button의 오른쪽 끝168px

맞는 규칙: 긴 글자가 드는 항목에는 min-width: 0으로 줄어들 수 있게 하고, overflow-wrap: anywhere⁠로 아무 데서나 꺾이게 합니다. 이제 단추가 상자 끝(168px)에 머뭅니다. 할 일 화면의 글 .text도 같은 까닭으로 overflow-wrap: anywhere⁠를 씁니다.

틀린 생각: 제목 옆 배지는 항목이니 늘 제 크기를 지킨다.

너비 100px 묶음에 긴 이름과 배지를 놓습니다.

<div class="recipe-card-head">
  <h2>김치볶음밥</h2>
  <span class="badge">한식</span>
</div>
.recipe-card-head {
  display: flex;
  width: 100px;
}

h2 {
  margin: 0;
}
결과 화면
잰 값개발자 도구에서 본 값
  • .badge의 flex-shrink1

자리가 모자라자 이름과 배지가 함께 줄어, 배지의 '한식'이 '한' / '식' 두 줄로 꺾였습니다. flex-shrink의 처음 값 1은 모자란 자리를 항목들이 나눠 줄어든다는 뜻입니다.

맞는 규칙: 줄어들면 안 되는 항목에는 flex-shrink: 0을 줍니다. 배지에 주면 배지는 제 너비를 지키고, 이름이 모자란 몫을 받아 꺾입니다.

틀린 생각: gap과 flex: 1은 어느 요소에 줘도 된다.

<form class="add">
  <input
    type="text"
    aria-label="할 일"
  />
  <button type="submit">
    추가
  </button>
</form>
.add {
  display: flex;
  flex: 1;
  width: 280px;
}

input {
  gap: 8px;
}
결과 화면
잰 값개발자 도구에서 본 값
  • input의 flex-grow0

입력 칸은 늘어나지 않고, 단추와 사이도 붙어 있습니다. 부모에 준 flex: 1은 부모 자신의 몫(그 부모가 다른 flex의 항목일 때)이고, 입력 칸에 준 gap은 입력 칸 안의 항목 사이를 띄우는 값이라 할 일이 없습니다.

맞는 규칙: gap은 항목을 놓는 부모에, flex: 1은 늘어날 자식에 줍니다(브라우저 규칙 7).

확인 문제

1. 2절 상자에서 '나'에도 flex: 1을 주고 width: 60px 줄을 지웠습니다. '가'의 너비는 얼마가 될까요?

<div class="row">
  <div class="a">가</div>
  <div class="b">나</div>
</div>
.row {
  display: flex;
  width: 240px;
}

.a,
.b {
  flex: 1;
}
  • ① 120px
  • ② 180px 그대로
  • ③ 240px — '나'를 밀어낸다
힌트

flex: 1이 내는 값 셋 가운데 나누기 전 크기와 받는 몫을 보고, 남는 자리를 두 항목에 나눕니다.

정답

① 120px

결과 화면
잰 값개발자 도구에서 본 값
  • .a의 너비120px
  • .b의 너비120px
  • 둘 다 나누기 전 크기(flex-basis)가 0%이고 받는 몫(flex-grow)이 1이라, 240px을 1:1로 나눠 120px씩입니다.
  • ②는 '가'가 먼저 받은 몫을 지킨다고 본 것입니다. 몫은 늘어날 항목들이 함께 나눕니다.
  • ③은 flex: 1이 남는 자리를 혼자 가진다고 본 것입니다. 같은 몫을 받은 항목이 둘이면 둘이 나눕니다.

2. 후기 폼의 이름 칸과 별점 칸을 한 줄에 놓으려 합니다. 주석은 배치를 짜는 세 걸음입니다(셋은 자주 쓰는 세 모양에서 정리합니다). 이름 칸 묶음이 남는 폭을 받게 하는 걸음을 통째로 쓰세요.

<div class="form-row">
  <div>이름 칸</div>
  <div>별점 칸</div>
</div>
/* 1. 부모 정하기 */
.form-row {
  display: flex;
  /* 2. 방향과 간격 */
  gap: 16px;
}

/* 3. 남는 자리와 정렬 */
____
힌트

남는 폭을 받을 요소는 부모인지 자식인지 먼저 정하고, 그 요소를 고르는 선택자와 선언 하나를 씁니다.

정답
<div class="form-row">
  <div>이름 칸</div>
  <div>별점 칸</div>
</div>
/* 1. 부모 정하기 */
.form-row {
  display: flex;
  /* 2. 방향과 간격 */
  gap: 16px;
}

/* 3. 남는 자리와 정렬 */
.form-row > div:first-child {
  flex: 1;
}
결과 화면
잰 값개발자 도구에서 본 값
  • .form-row > div:first-child의 flex-grow1
  • .form-row의 줄 수1

남는 폭은 늘어날 자식, 곧 첫째 div의 몫이라 그 자식에 flex: 1을 씁니다. 이름 칸 묶음에 이름표를 붙여 그 이름으로 골라도 됩니다. .form-row에 쓰면 부모 자신의 몫이 되어 이름 칸은 늘어나지 않습니다.

3. 플로우 카페 메뉴판의 한 줄입니다. 메뉴 셋 사이를 12px씩 띄우려고 이렇게 썼습니다. 마지막 '녹차'의 오른쪽 끝은 몇 px일까요?

<div class="menu">
  <div>커피</div>
  <div>라테</div>
  <div>녹차</div>
</div>
.menu {
  display: flex;
  gap: 12px;
}

.menu div {
  width: 60px;
}
  • ① 212px
  • ② 224px
  • ③ 188px
힌트

상자는 8px에서 시작합니다. 항목 셋의 너비와, 간격이 몇 곳에 드는지를 차례로 더합니다.

정답

① 212px

결과 화면
잰 값개발자 도구에서 본 값
  • .menu div:last-child의 오른쪽 끝212px
  • 8 + 60 + 12 + 60 + 12 + 60 = 212px입니다. 간격은 이웃한 항목 사이 두 곳에만 듭니다.
  • ②는 마지막 항목 뒤에도 간격이 붙는다고 본 것입니다(224 = 212 + 12). 마지막 뒤에 붙는 것은 margin-right입니다.
  • ③은 간격을 빼고 너비만 더한 값입니다.

컴퓨터에서 해 보기(선택) — 점수에 들지 않습니다. 폰으로만 보고 있다면 건너뛰세요. hello.html의 body 안을 1절 둘째 코드로, style 요소 안을 그 CSS로 바꿔 저장 · 새로고침합니다. gap: 16px 줄을 지우고, 1절 첫째 코드처럼 .menu span 규칙에 margin-right: 16px을 써서 다시 저장 · 새로고침하면, '나'가 상자 오른쪽 끝에서 16px 떨어집니다. Elements 패널에서 span 줄에 마우스를 올리면 바깥 여백이 따로 칠해져, 마지막 항목 뒤에도 여백이 붙은 것이 보입니다. 파일을 만들고 여는 법은 준비: VS Code와 작업 폴더에 있습니다.

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

HTML·CSS 목차로