목차
개요
지금까지 긴 문자열이 컨테이너를 뚫고 나가는 문제를 해결하기 위해 word-break: break-word를 적용했습니다. 그런데 최근에 이 속성이 비추천(deprecated)이라는 것을 알게 되었습니다. MDN 공식 문서에 따르면, word-break: break-word는 overflow-wrap: anywhere와 word-break: normal을 조합한 것과 동일한 효과를 가지는 레거시 값으로, deprecated로 명시되어 있습니다.
이에 따라 overflow-wrap: break-word로 변경했더니, 특정 요소에서 줄바꿈이 동작하지 않는 현상이 발생했습니다.
원인을 조사한 결과, overflow-wrap: break-word와 overflow-wrap: anywhere의 미묘하지만 중요한 차이를 발견했습니다. 이번 글에서는 그 원인 분석과 해결 방법, 그리고 Stylelint 커스텀 플러그인을 통해 검사와 수정 방법 추천을 자동화하는 과정에 대해서 공유합니다.
Stylelint 설정에 관해서는 다음 글을 참고하세요.
overflow-wrap이란?
overflow-wrap은 컨테이너를 넘치는 긴 문자열을 강제로 줄바꿈하는 안전장치입니다. 평소에는 아무 일도 하지 않고, 넘칠 때만 작동합니다.
영어의 경우
일반적인 영어 문장은 다음과 같이 overflow-wrap과 관계없이 스페이스에서 자연스럽게 줄바꿈됩니다.
This is a sample
text for testing
word wrap behavior.
하지만 URL이나 시리얼넘버처럼 스페이스 없이 이어지는 긴 문자열은 넘침이 발생합니다. 이때 overflow-wrap을 설정한 경우, 넘치는 지점에서 강제로 줄바꿈합니다.
- overflow-wrap 없음
https://example.com/very/long/path/to/resource ← 넘침!
- overflow-wrap 있음
https://example.co
m/very/long/path/t
o/resource
break-word와 anywhere의 차이
overflow-wrap의 두 값인 break-word와 anywhere는 눈에 보이는 줄바꿈 결과는 동일합니다.
// break-word
Password:
aB3$kL9mNpQrStUvWx
Yz1234567890abcdef
// anywhere
Password:
aB3$kL9mNpQrStUvWx
Yz1234567890abcdef
차이는 **레이아웃 계산(min-content)**에서 발생합니다.
break-word: min-content 계산 시 줄바꿈 기회를 고려하지 않습니다. 즉, 단어 전체 폭이 최소 폭이 됩니다.anywhere: min-content 계산 시 줄바꿈 기회를 고려합니다. 즉, 한 글자 폭까지 줄어들 수 있습니다.
MDN 공식 문서에서도 이 차이를 명확하게 설명하고 있습니다:
anywhere: “Soft wrap opportunities introduced by the word break are considered when calculating min-content intrinsic sizes.”break-word: “Soft wrap opportunities introduced by the word break are NOT considered when calculating min-content intrinsic sizes.”
이 차이가 바로 break-word가 특정 요소에서 동작하지 않는 원인이 됩니다.
break-word가 동작하지 않는 원인
문제의 요소에는 display: inline-block이 적용되어 있었습니다.
inline-block은 콘텐츠에 맞춰 크기가 결정되는 intrinsic sizing 요소입니다.
이 요소의 너비는 min-content를 기반으로 결정됩니다. 앞서 설명한 min-content 계산의 차이가 여기서 문제를 일으킵니다.
- inline-block + break-word인 경우
min-content = 단어 전체 폭 (260px)
→ 요소가 260px로 넓어짐
→ 넘침이 발생하지 않음
→ break-word가 트리거되지 않음
→ 줄바꿈 안 됨!
- inline-block + anywhere인 경우
min-content = 한 글자 폭 (~8px)
→ 부모에 맞춰 너비가 제한됨
→ 넘침 발생
→ 줄바꿈 발생!
정리하면, break-word는 “넘칠 때만 줄바꿈”하는데, intrinsic sizing 요소의 min-content 계산에 줄바꿈 기회를 포함하지 않으므로 요소 자체가 넓어져서 넘침 자체가 발생하지 않게 됩니다. 결국 줄바꿈이 트리거되지 않는 것입니다.
같은 문제가 발생하는 CSS 속성들
inline-block 외에도 intrinsic sizing을 사용하는 CSS 속성은 모두 동일한 문제가 발생합니다.
| CSS 속성 | 이유 |
|---|---|
display: inline-block | 콘텐츠에 맞춰 너비 결정 |
display: inline-flex | inline-level flex container |
display: inline-grid | inline-level grid container |
display: table-cell | 콘텐츠 기반 너비 |
float: left / right | shrink-to-fit |
position: absolute / fixed (width 미지정) | shrink-to-fit |
width: min-content / fit-content | 명시적 intrinsic sizing |
이런 속성들은 모두 콘텐츠에 맞춰 너비가 결정되기 때문에, break-word의 min-content 계산 방식으로 인해 줄바꿈이 동작하지 않게 됩니다.
권장하는 CSS 조합
이 문제를 해결하기 위해 다음 3개의 CSS 속성을 함께 사용하는 것을 권장합니다.
overflow-wrap: anywhere;
word-break: normal;
line-break: strict;
각 속성의 역할
overflow-wrap: anywhere
긴 문자열이 컨테이너를 넘지 않도록 강제 줄바꿈합니다. break-word와 달리 min-content 계산에 줄바꿈 기회를 포함하므로, intrinsic sizing 요소에서도 정상적으로 동작합니다.
word-break: normal
기본 단어 분리 규칙을 사용합니다. 영어는 스페이스에서, 일본어, 중국어(CJK)에서는 문자 단위로 줄바꿈합니다. 기본값이므로 생략할 수 있지만, 이전에 word-break: break-word가 적용되어 있던 곳에서는 명시적으로 추가하여 의도를 명확히 하는 것이 좋습니다.
word-break의 각 값에 따른 동작 차이는 다음과 같습니다:
| 값 | 영어 | 일본어, 중국어(CJK) |
|---|---|---|
normal | 단어 단위 줄바꿈 | 문자 단위 줄바꿈 |
break-all | 문자 단위 줄바꿈 | normal과 동일 |
keep-all | normal과 동일 | 단어 단위 줄바꿈 (넘침 위험) |
line-break: strict
일본어 금칙처리(禁則処理)를 엄격하게 적용합니다. 행의 시작에 오면 안 되는 문자(、 。 ぁ っ 々 ー 등)를 제어하여 더 자연스러운 텍스트를 만듭니다.
- strict 없음 → 「々」가 행두에 옴 (부자연스러움)
そこは湖のほ
とりで木
々が輝いてい
- strict 있음 → 「木々」를 함께 유지 (자연스러움)
そこは湖の
ほとりで
木々が輝い
영어에는 실질적 영향이 없으므로 함께 적용해도 부작용이 없습니다.
참고:
3개 속성의 결합 효과
영어 텍스트의 경우:
# 입력: "Visit this link: https://example.com/very/long/path/to/resource"
Visit this link: ← word-break: normal (스페이스에서 줄바꿈)
https://example.co ← overflow-wrap: anywhere (넘치는 지점에서 강제 줄바꿈)
m/very/long/path/t
o/resource
일본어 텍스트의 경우:
# 입력: "デバイスID: ABCDEFGHIJKLMNOPQRSTUVWXYZ を確認してください。"
デバイスID: ← word-break: normal (CJK 문자 단위 줄바꿈)
ABCDEFGHIJKLMNOPQR ← overflow-wrap: anywhere (영문 강제 줄바꿈)
STUVWXYZを確認し
てください。 ← line-break: strict (「。」가 행두에 오지 않음)
anywhere로 통일해도 되는가?
break-word와 anywhere의 시각적 줄바꿈 결과는 동일하고, 차이는 min-content 계산뿐입니다.
anywhere로 통일했을 때 이론적으로 flex/grid/table 레이아웃에서 공간 배분이 달라질 수 있습니다. anywhere는 min-content가 한 글자 폭까지 줄어들 수 있으므로, flex item이 예상보다 작게 축소되거나 grid 트랙이 좁아지는 경우가 있을 수 있습니다.
하지만 실무에서 이것이 문제가 되는 경우는 거의 없습니다. 그 이유는 다음과 같습니다:
- min-content는 하한값일 뿐입니다. 실제 렌더링 너비는
flex-basis,width,grid-template-columns등 다른 속성에 의해 결정됩니다. min-content가 작아졌다고 해서 요소가 바로 그 크기로 줄어드는 것이 아닙니다. - flex 레이아웃에서는
flex-grow/flex-shrink가 공간을 배분합니다. 대부분의 flex item은flex: 1이나 고정flex-basis를 가지고 있어, min-content 차이가 최종 너비에 영향을 미치지 않습니다. min-content가 관여하는 것은 item이 더 이상 줄어들 수 없는 최소 한계를 결정할 때뿐입니다. - grid 레이아웃에서도 트랙 크기가 명시적으로 지정됩니다.
grid-template-columns: 1fr 2fr이나minmax(200px, 1fr)같은 선언이 일반적이므로, min-content가 달라져도 트랙 크기에 영향을 주지 않습니다.min-content키워드를 트랙 크기로 직접 사용하는 경우에만 차이가 생길 수 있지만, 이는 드문 패턴입니다. - 테이블에서는
table-layout: fixed가 일반적입니다. 고정 레이아웃에서는 셀 내용이 아닌 열 너비 선언에 따라 크기가 결정되므로 min-content 차이가 무시됩니다.table-layout: auto(기본값)에서만 영향이 있을 수 있지만, 이 경우에도 텍스트가 길어서 넘치는 문제보다는 적절히 줄바꿈되는 것이 더 나은 결과를 줍니다.
오히려 break-word를 사용하면 앞서 살펴본 것처럼 intrinsic sizing 요소에서 줄바꿈이 동작하지 않는 실질적인 버그가 발생합니다. 따라서 anywhere로 통일하는 것이 안전합니다.
word-break: break-word는 왜 비추천인가?
word-break: break-word는 CSS3 사양에서 정식으로 정의된 값이 아닙니다. 원래 overflow-wrap: break-word의 레거시 별칭(legacy alias)으로 브라우저들이 호환성을 위해 지원해 왔습니다. CSS Text Level 3 스펙에서도 이 값을 비추천으로 명시하고 있으며, overflow-wrap: anywhere를 대신 사용할 것을 권장하고 있습니다.
Stylelint 커스텀 플러그인으로 자동화
이 규칙을 개발자가 매번 기억하기는 어렵습니다. 그래서 Stylelint의 커스텀 플러그인을 만들어 자동으로 감지하고, 에러 메시지와 함께 올바른 대체 조합을 안내하도록 설정했습니다.
Stylelint 설정에 관해서는 다음 글을 참고하세요.
감지 규칙
| 감지 대상 | 에러 메시지 |
|---|---|
overflow-wrap: break-word | 기대대로 줄바꿈되지 않는 케이스가 있으므로 비추천 → 3개 세트 안내 |
word-break: break-word | 비추천(deprecated) → 3개 세트 안내 |
overflow-wrap: anywhere + line-break: strict 없음 | ’line-break: strict를 추가하세요’로 안내 |
플러그인 코드
프로젝트 루트에 plugins/require-strict-wrap-rules.js 파일을 생성하고 다음과 같이 작성합니다.
'use strict';
const stylelint = require('stylelint');
const ruleName = 'custom/require-strict-wrap-rules';
const REPLACEMENT =
' 대신 "overflow-wrap: anywhere; word-break: normal; line-break: strict;"를 사용하세요.';
const messages = stylelint.utils.ruleMessages(ruleName, {
rejectedOverflowWrapBreakWord:
'"overflow-wrap: break-word"는 기대대로 줄바꿈되지 않는 케이스가 있어 비추천(deprecated)입니다.' +
REPLACEMENT,
rejectedWordBreakBreakWord:
'"word-break: break-word"는 비추천(deprecated)입니다.' + REPLACEMENT,
rejectedLineBreak:
'overflow-wrap: anywhere를 사용하는 경우 "line-break: strict"도 함께 지정하세요' +
'(일본어 금칙처리를 위해 필요합니다).',
});
const ruleFunction = (primary) => {
return (root, result) => {
const validOptions = stylelint.utils.validateOptions(result, ruleName, {
actual: primary,
possible: [true],
});
if (!validOptions) return;
root.walkRules((rule) => {
let anywhereNode = null;
let hasLineBreakStrict = false;
rule.walkDecls((decl) => {
if (decl.prop === 'overflow-wrap' && decl.value === 'break-word') {
stylelint.utils.report({
message: messages.rejectedOverflowWrapBreakWord,
node: decl,
result,
ruleName,
});
}
if (decl.prop === 'word-break' && decl.value === 'break-word') {
stylelint.utils.report({
message: messages.rejectedWordBreakBreakWord,
node: decl,
result,
ruleName,
});
}
if (decl.prop === 'overflow-wrap' && decl.value === 'anywhere') {
anywhereNode = decl;
}
if (decl.prop === 'line-break' && decl.value === 'strict') {
hasLineBreakStrict = true;
}
});
if (anywhereNode && !hasLineBreakStrict) {
stylelint.utils.report({
message: messages.rejectedLineBreak,
node: anywhereNode,
result,
ruleName,
});
}
});
};
};
ruleFunction.ruleName = ruleName;
ruleFunction.messages = messages;
module.exports = stylelint.createPlugin(ruleName, ruleFunction);
Stylelint 설정
프로젝트 루트의 .stylelintrc.json 파일에 플러그인과 규칙을 추가합니다.
{
"plugins": ["./plugins/require-strict-wrap-rules.js"],
"rules": {
"custom/require-strict-wrap-rules": true
}
}
실행 결과
이렇게 설정한 규칙을 적용하여, Stylelint를 실행하면 다음과 같은 에러 메시지가 출력됩니다.
- overflow-wrap: break-word 사용 시
✖ "overflow-wrap: break-word"는 기대대로 줄바꿈되지 않는 케이스가 있어 비추천입니다.
대신 "overflow-wrap: anywhere; word-break: normal; line-break: strict;"를 사용하세요.
- word-break: break-word 사용 시
✖ "word-break: break-word"는 비추천(deprecated)입니다.
대신 "overflow-wrap: anywhere; word-break: normal; line-break: strict;"를 사용하세요.
- overflow-wrap: anywhere에서 line-break: strict 누락 시
✖ overflow-wrap: anywhere를 사용하는 경우 "line-break: strict"도 함께 지정하세요
(일본어 금칙처리를 위해 필요합니다).
마무리
CSS의 줄바꿈 속성은 비슷해 보이지만 미묘한 차이가 있어 의도대로 동작하지 않는 경우가 있습니다. 이번 글에서 다룬 내용을 정리하면 다음과 같습니다.
| 비추천 | 권장 |
|---|---|
word-break: break-word | overflow-wrap: anywhere |
overflow-wrap: break-word | word-break: normalline-break: strict |
overflow-wrap: anywhere는break-word와 달리 intrinsic sizing 요소(inline-block,inline-flex,float등)에서도 정상적으로 동작합니다.word-break: normal은 영어와 일본어(CJK) 모두에서 가장 자연스러운 줄바꿈을 제공합니다.line-break: strict는 일본어 금칙처리를 엄격하게 적용하여 텍스트 품질을 높입니다.- Stylelint 커스텀 플러그인으로 이 규칙을 자동화하면 개발자가 매번 기억할 필요가 없습니다.
참고
- MDN - overflow-wrap
- MDN - word-break
- MDN - line-break
- MDN - Intrinsic Size
- MDN - Block Formatting Context
- W3C - CSS Text Level 3: overflow-wrap
제 블로그가 도움이 되셨나요? 하단의 댓글을 달아주시면 저에게 큰 힘이 됩니다!
앱 홍보
Deku가 개발한 앱을 한번 사용해보세요.Deku가 개발한 앱은 Flutter로 개발되었습니다.관심있으신 분들은 앱을 다운로드하여 사용해 주시면 정말 감사하겠습니다.