🎨 Frontend CSS4. 타이포그래피05 — Text Properties (wrap·decoration·hyphens)

05 — Text Properties (wrap·decoration·hyphens)

폰트와 메트릭이 정해진 다음, 텍스트는 어떻게 줄을 바꾸고, 어떻게 분절되고, 어떻게 장식되는가. 2023년 이후 CSS는 이 영역에 text-wrap: balance/pretty 라는 게임 체인저를 추가했다.


한 문장 답 (Pyramid Top)

텍스트 흐름 제어는 줄바꿈 (text-wrap, word-break) + 단어 분절 (hyphens, overflow-wrap) + 장식 (text-decoration, text-underline-offset) 의 3축이다. 모던 CSS의 핵심 변화는 제목용 balance (모든 줄을 균등하게), 본문용 pretty (마지막 줄의 고아 단어 방지), 한글용 keep-all (단어 단위 줄바꿈) 의 3가지 한 줄 해결책이 나왔다는 점.


Why — 왜 이 챕터인가

다음 같은 “디자인 실무에서 매일 마주치는 문제들” 의 답:

  • 제목이 “긴 단어 한 줄 + 짧은 단어 한 줄” 로 어색하게 깨진다 → text-wrap: balance.
  • 본문 마지막 줄에 한 단어만 남는 (고아 단어, orphan) → text-wrap: pretty.
  • 영문 긴 단어가 컨테이너를 뚫고 나간다overflow-wrap: anywhere.
  • 한글 “안녕하세요 반갑습니다”“안녕\n하세요” 로 깨진다 → word-break: keep-all.
  • 영문 본문이 단어 사이 공백이 들쭉날쭉hyphens: auto.
  • 밑줄이 글자 아래쪽 꼬리(g, p)에 닿는다text-underline-offset + text-decoration-thickness.

How — text-wrap (CSS Text 4, 2023+)

1) wrap (기본) — 탐욕적 알고리즘

브라우저는 한 줄에 최대한 많은 단어를 넣고, 안 들어가면 다음 줄로. line-by-line greedy.

한 줄 컨테이너 (300px):
"This is a really long heading"
→ "This is a really"      ← 한 줄에 가능한 만큼
→ "long heading"          ← 나머지

결과: 첫 줄 280px, 두 번째 줄 100px (불균형)

2) balance (Chrome 114+, Safari 17.5+) — 균등 분배

h1, h2 {
  text-wrap: balance;
}

브라우저가 모든 가능한 줄 분할을 탐색해 각 줄의 폭을 최대한 균등하게 만든다.

"This is a really long heading"
→ "This is a really long"
→ "heading"

→ balance:
"This is a really"
"long heading"
(균등)

제약:

  • 보통 6줄 이하만 적용 (Chrome 기준). 본문에 쓰면 성능 문제.
  • 길어질수록 비용이 큰 알고리즘 (O(2^n) → 휴리스틱).
  • 제목·짧은 캡션 전용.

3) pretty (Chrome 117+) — 고아 단어 방지

p {
  text-wrap: pretty;
}

브라우저가 마지막 줄에 단어 하나만 남지 않도록 줄바꿈을 조정 — 영문 인쇄 조판의 widow/orphan 방지.

"This is a long paragraph that ends with one
word"
→ pretty:
"This is a long paragraph that ends with
one word"
(마지막 줄에 한 단어 외톨이 방지)
  • 본문에 적용 OK (성능 비용 작음).
  • 마지막 몇 줄만 재배치하는 부분 알고리즘.

4) 세 모드 비교

모드적용 대상성능효과
wrap (기본)모든 텍스트빠름greedy — 균형 X
balance제목 (3-6줄 이하)느림 (O(n)~O(n²))모든 줄 폭 균등
pretty본문보통마지막 줄 고아 방지

한 줄 BP: h1..h6 { text-wrap: balance; } body { text-wrap: pretty; }.


How — word-break, overflow-wrap, hyphens

세 속성이 “긴 단어가 컨테이너를 넘칠 때” 를 다룬다 — 다 비슷해 보이지만 다르다.

1) word-break: normal (기본)

"longwordwithoutspaces" → 컨테이너 뚫음

2) word-break: break-all

"longwordwithoutspaces"
→ "longwordwithoutsp"
   "aces"

어디서든 자른다 — 영문에서는 모양이 추해서 잘 안 씀. 한글·중문에서 코드 같은 긴 문자열에 유용.

3) word-break: keep-all (CJK 전용)

"안녕하세요 반갑습니다"
→ 기본: "안녕하세\n요 반갑..." (글자 단위로 깨짐)
→ keep-all: "안녕하세요\n반갑습니다" (단어 단위)
  • 한국어·일본어·중국어에 반드시 적용.
  • 영문 단어처럼 공백 사이에서만 줄바꿈.

4) overflow-wrap: anywhere (구 word-wrap)

.url {
  overflow-wrap: anywhere;
}
  • 일반 단어는 단어 경계에서 끊고, 너무 긴 단어(URL 같은)는 어디서든 끊는다.
  • 우선 단어 경계 시도 → 실패 시 강제 분리. break-all보다 더 지능적.

5) hyphens: auto

p {
  hyphens: auto;
  lang: en;   /* 또는 html lang="en" */
}
  • 영문 단어를 언어 사전 기반으로 음절에 따라 분절 + 하이픈 삽입.
    • “responsibility” → “respon-sibility”.
  • 반드시 lang 속성이 있어야 작동 (사전 선택용).
  • 한글에는 적용되지 않음.

결정 트리

긴 코드 문자열 (URL) →           overflow-wrap: anywhere
영문 본문, 더 자연스러운 단락 →   hyphens: auto
한국어 텍스트 →                  word-break: keep-all
코드 블록 안 긴 변수명 →         word-break: break-all

How — text-decoration (모던)

기본

a { text-decoration: underline; }

4축 분리 (CSS Text Decoration 4)

a {
  text-decoration-line: underline;
  text-decoration-style: solid;        /* solid / double / wavy / dotted / dashed */
  text-decoration-color: currentColor;
  text-decoration-thickness: 2px;      /* from-font / auto / 길이 */
  text-underline-offset: 3px;          /* 글자 아래 줄 위치 */
  text-underline-position: under;      /* baseline 기준 vs 글자 아래쪽 */
}

단축형

a { text-decoration: underline wavy red 2px; }

text-underline-offset의 BP

기본 auto폰트의 baseline에 줄을 긋는다 — g, p, y 같은 descender가 밑줄을 가로지른다.

a {
  text-decoration: underline;
  text-underline-offset: 0.2em;        /* baseline 아래 0.2em */
  text-decoration-thickness: 1px;
}

→ 밑줄이 descender 아래로 내려가 깨끗하게 보임.

text-decoration-skip-ink: auto (기본 활성)

a { text-decoration-skip-ink: auto; }  /* 기본값 */
  • g, p, j 같은 descender 위에서 밑줄을 자동으로 끊음.
  • Chrome 64+, Safari 12.1+, Firefox 70+에서 기본 활성.

What — text-emphasis (CJK 강조 점)

한국어·중국어·일본어에서 강조용 점을 글자 위에 찍는 전통적 조판.

em {
  text-emphasis: filled circle red;
  text-emphasis-position: over right;
}
"강조하고 싶은 단어"
   • • • •  ← 점이 찍힘

서양 책의 italic 대신 동양 책에서 강조에 쓰인다.


What — white-space 모음

공백줄바꿈줄바꿈 문자 (\n)
normal합침합침·자동무시
nowrap합침안 함무시
pre보존안 함보존
pre-wrap보존자동·\n보존
pre-line합침자동·\n보존
break-spaces보존자동·공백에서도보존

가장 흔한 BP:

  • 코드 블록: pre 또는 pre-wrap.
  • 사용자 입력 텍스트: pre-wrap (개행 보존하면서 줄바꿈).
  • 한 줄 라벨: nowrap + text-overflow: ellipsis.

한 줄 ellipsis

.truncate {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

다중 줄 ellipsis (모던)

.truncate-3 {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
 
/* CSS Overflow 4 (실험) */
.modern {
  line-clamp: 3;       /* 미래의 표준 */
}

What — text-align, text-indent, text-justify

p {
  text-align: justify;
  text-justify: inter-word;  /* 단어 사이만 늘림 */
  text-indent: 1em;
}
  • text-align: justify — 양쪽 정렬. 단어 사이 공백을 늘려 오른쪽 가지런.
  • text-justify: inter-word — 단어 사이만 (한글에서도 자연스러움).
  • text-justify: inter-character — 글자 사이까지 (CJK).

일반 본문에서 justify는 피한다 — 한글에선 단어 사이 공백이 너무 벌어져 읽기 어려움. text-align: start(또는 left)가 가독성에 좋음.


What-if — 잘못 쓰면

1) 본문에 balance 적용

p { text-wrap: balance; }

→ 본문이 길면 알고리즘 비용 폭발 (Chrome은 6줄 이상이면 자동 fallback하지만 의존하면 안 됨). 본문엔 pretty.

2) 한국어에 word-break 미설정

.korean { /* word-break 미설정 */ }

→ 좁은 컨테이너에서 “안녕하세\n요 반갑\n습니다” 같이 글자 단위 줄바꿈. word-break: keep-all 필수.

3) hyphens: autolang 누락

<html>  <!-- lang 없음 -->
  <p style="hyphens: auto">responsibility</p>  <!-- 분절 안 됨 -->
</html>

<html lang="en"> 또는 해당 요소에 lang.

4) 모든 a 태그에 text-decoration: none

a { text-decoration: none; }

→ 본문 안 링크가 식별 불가 → 접근성 문제. 전역 제거하지 말고 맥락에 따라.

5) text-overflow: ellipsis만 + overflow: visible

.truncate {
  text-overflow: ellipsis;
  /* white-space: nowrap, overflow: hidden 없음 */
}

→ 효과 없음. 세 속성이 세트.


Insight — 흥미로운 이야기

“text-wrap: balance는 Adobe InDesign에서 왔다”

1999년 Adobe InDesign이 “Adobe Paragraph Composer” 라는 줄바꿈 알고리즘을 도입했다 — 한 줄씩 처리하는 greedy 대신 문단 전체를 보고 균등하게 분배. 인쇄 조판의 대대적 향상이었다. 25년이 지난 2023년 Chrome 114가 이 알고리즘의 단순화 버전text-wrap: balance로 도입했다. Knuth-Plass 알고리즘 (1981년 TeX의 줄바꿈 알고리즘)이 이론적 기원. 인쇄 조판의 80년대 기술이 웹의 2020년대 표준이 된다 — 디지털이 인쇄를 따라잡는 데 40년이 걸렸다.

“하이픈 사전은 nation이 아니라 lang에 따라 다르다”

영문 “responsibility” 의 하이픈 위치는 미국 영어(re-spon-si-bil-i-ty)와 영국 영어(re-spon-si-bi-li-ty)가 다르다. 그래서 <html lang="en-US"> vs <html lang="en-GB">가 하이픈 결과를 바꾼다. 독일어는 복합어 분해까지 한다 — DonaudampfschifffahrtsgesellschaftDonau-dampf-schiff-fahrts-ge-sell-schaft. 한국어는 어절(공백) 단위로만 분리하므로 hyphens: auto가 작동하지 않고, 대신 word-break: keep-all어절 보호의 역할.


요약 + Mermaid

  • text-wrap: balance — 제목, 균등 줄 폭. pretty — 본문, 고아 단어 방지.
  • 한국어는 반드시 word-break: keep-all로 어절 보호.
  • hyphens: autolang 속성과 함께. 한국어에는 적용 안 됨.
  • text-decoration-skip-ink: auto로 descender 위 밑줄 자동 끊음.
  • white-space공백·줄바꿈·줄바꿈 문자의 3축 조합.

다음 문서 → 06-fluid-typography: 텍스트 크기가 뷰포트·컨테이너에 따라 자동 스케일.