한글 단어는 지키고, 코드는 어디서 줄바꿈하죠? (keep-all만 넣고 끝내면 안 되는 이유)

시작은 iOS였습니다.
개인 포트폴리오의 최근 글 목록을 만들면서, 짧은 링크에 쓰던 .text-link 스타일을 글 제목에도 재사용했습니다.
이미 밑줄과 hover 효과가 있으니 이름만 봐도 딱 맞아 보였거든요.
그런데 iOS 위젯은…이라는 제목이 화면에서 IOS 위젯은…으로 바뀌었습니다.
.text-link는 짧은 CTA를 위한 primitive라서 text-transform: uppercase가 들어 있었습니다.
한글은 그대로 두고 제목 속 영문 제품명만 대문자로 바꿔버린 겁니다.
CSS가 한국어를 모르는 줄 알았는데, 필요한 부분만 아주 성실하게 건드리고 있었습니다. (일은 잘했는데 시킨 일이 아니었습니다.)
결국 글 제목에는 별도의 .record-line을 만들었습니다.
CTA와 제목은 둘 다 링크이지만, 텍스트의 역할이 다르면 타이포그래피 규칙도 달라야 한다는 아주 평범한 결론에 도착했죠.
그리고 비슷한 질문이 줄바꿈에서도 다시 나타났습니다.
한국어는 왜 글자 사이에서도 줄이 바뀔까?
영어 문장은 보통 공백을 기준으로 줄이 바뀝니다. 반면 브라우저의 기본 CJK 줄바꿈은 한글 음절 사이도 줄바꿈 지점으로 취급할 수 있습니다.
그래서 폭이 좁아지면 이런 느낌의 제목이 나올 수 있습니다.
반응형 포트폴리오를
만드는 방법
공간이 더 애매하면 단어 안에서도 끊길 수 있습니다.
반응형 포트폴리오를 만
드는 방법
문법적으로 읽지 못할 문장은 아닙니다. 그런데 제목과 긴 본문에서 이런 줄바꿈이 반복되면 눈이 단어를 다시 조립해야 합니다. 화면 폭은 줄었는데 독자의 할 일은 늘어납니다. (반응형이 독자에게 일을 외주 줍니다.)
이때 가장 먼저 적용할 수 있는 규칙이 word-break: keep-all입니다.
body {
overflow-wrap: break-word;
word-break: keep-all;
}
keep-all은 CJK 텍스트에서 단어 안쪽의 줄바꿈을 억제합니다.
한국어 문장은 공백을 기준으로 덩어리를 유지하면서 다음 줄로 넘어갑니다.
W3C CSS Text Module Level 3도 한국어를 포함한 CJK 텍스트에서 normal과 keep-all의 줄바꿈 차이를 구분합니다.
제가 만든 포트폴리오에서는 이 규칙을 body에 두고, 큰 제목과 본문에도 의도를 다시 명시했습니다.
.page-title,
.post__title {
text-wrap: balance;
word-break: keep-all;
}
text-wrap: balance는 제목의 각 줄 길이가 지나치게 들쭉날쭉해지지 않도록 균형을 맞추고, keep-all은 한글 단어가 음절 사이에서 갈라지지 않도록 막습니다.
자, 이제 끝났을까요?
아니요.
개발 블로그에는 TurboModuleRegistry.getEnforcing<Spec>(...) 같은 친구들이 살고 있습니다.
공백이라고는 찾아볼 수 없는 아주 길고 굳센 친구들이요.
keep-all을 전역에 두면 코드가 새로운 문제가 됩니다
한국어 단어를 지키겠다고 줄바꿈을 보수적으로 만들면, 긴 코드 식별자는 좁은 화면의 읽기 폭을 밀어낼 수 있습니다.
그렇다고 모든 텍스트에 overflow-wrap: anywhere를 적용하면 다시 원점입니다.
이 값은 다른 줄바꿈 지점이 없는 긴 덩어리가 넘칠 때 임의 지점에서 끊을 수 있게 하므로, 한국어 본문의 단어 보존 규칙도 overflow 상황에서는 느슨해질 수 있습니다.
해법은 전역 규칙을 포기하는 것이 아니라 콘텐츠 종류별로 예외를 좁히는 것입니다.
이 포트폴리오에서는 inline code만 필요할 때 토큰 중간에서 줄을 바꿀 수 있게 했습니다.
.post__body code {
box-decoration-break: clone;
overflow-wrap: anywhere;
}
여기서 overflow-wrap: anywhere는 긴 식별자 때문에 줄 전체가 읽기 영역 밖으로 밀려나는 상황을 막습니다.
W3C CSS Text Module Level 3의 overflow-wrap 정의처럼, 목적은 평소의 문장 줄바꿈을 바꾸는 것이 아니라 깨지지 않는 문자열이 상자를 넘는 상황에 비상 줄바꿈 지점을 허용하는 것입니다.
box-decoration-break: clone도 같이 둔 이유가 있습니다.
inline code가 두 줄로 갈라졌을 때 배경과 테두리를 각 줄 조각에 다시 그려주기 위해서입니다.
이 규칙이 없으면 첫 줄의 오른쪽과 다음 줄의 왼쪽이 열린 상자처럼 보일 수 있습니다.
W3C CSS Fragmentation Module Level 3은 clone이 분할된 각 조각에 배경과 테두리 같은 장식을 독립적으로 적용한다고 설명합니다.
한국어 본문에는 단어 보존 규칙을 유지하고, 실제로 긴 토큰이 등장하는 inline code만 더 느슨하게 만든 셈입니다.
그런데 block code는 또 다릅니다
코드 블록까지 inline code처럼 아무 곳에서나 줄을 바꾸면 들여쓰기와 한 줄의 구조를 읽기 어려워집니다.
예를 들어 긴 method chain이나 JSON 한 줄이 화면에 맞춰 임의로 접히면, 그 줄바꿈이 원래 코드인지 화면이 만든 것인지 구분하기 어렵습니다.
그래서 block code 안에서는 다시 줄바꿈을 끄고 가로 스크롤을 선택했습니다.
.post__body pre {
overflow-x: auto;
}
.post__body pre code {
overflow-wrap: normal;
white-space: pre;
}
W3C CSS Text Module Level 3의 white-space 정의에서 pre는 공백을 보존하고 줄을 접지 않는 값입니다.
inline code는 문장 안에 있으므로 읽기 폭 안에 남는 것이 중요합니다. block code는 코드의 행과 들여쓰기를 보존하는 것이 더 중요합니다.
둘 다 code 태그라고 같은 줄바꿈 정책을 적용하면 안 되는 이유입니다.
요리로 치면 다 같은 칼이라고 식칼과 빵칼을 같은 방식으로 쓰는 셈입니다.
자를 수는 있겠지만 결과가 자꾸 부스러집니다.
전역 규칙과 예외 규칙을 나누는 순서
제가 지금 유지하는 순서는 다음과 같습니다.
body에서 한국어 단어 보존을 기본값으로 둡니다.- 큰 제목에는
text-wrap: balance와keep-all을 함께 적용합니다. - inline code에만
overflow-wrap: anywhere를 허용합니다. - block code는
white-space: pre와 가로 스크롤로 원래 행을 보존합니다.
이 순서의 장점은 예외가 어디에 있는지 설명하기 쉽다는 것입니다.
“모바일에서 뭔가 넘치니까 일단 전부 anywhere!”라고 시작하면, 나중에 한국어 제목이 왜 이상하게 끊기는지 다시 추적해야 합니다.
반대로 모든 곳에 keep-all만 넣으면 긴 URL과 식별자가 화면 밖으로 여행을 떠납니다.
전역에는 가장 흔한 독서 경험을 위한 규칙을 두고, 구조가 다른 콘텐츠에만 예외를 둬야 합니다.
테스트할 때는 짧은 예문을 믿지 마세요
안녕하세요 하나만 넣고 모바일 화면을 보면 거의 모든 설정이 멀쩡해 보입니다.
대신 다음 세 종류를 같이 확인해보세요.
- 공백이 여러 개 있는 긴 한국어 제목.
TurboModuleRegistry.getEnforcing<Spec>(...)처럼 공백이 없는 inline code.- 들여쓰기와 긴 한 줄이 함께 있는 code block.
그리고 데스크톱만 보지 말고 실제 읽기 폭이 좁아지는 지점을 확인해야 합니다. 이 포트폴리오의 설계는 768픽셀 이하에서 분할 구성을 한 열로 바꾸고, 640픽셀 아래에서도 헤더가 가로 스크롤 없이 감싸지도록 요구합니다. 줄바꿈 규칙은 그 축소된 레이아웃 안에서 읽혀야 의미가 있습니다.
CSS 속성이 붙어 있다는 사실은 검증이 아닙니다. 그 속성이 읽을 문장과 긴 토큰을 실제로 만났을 때 어떻게 렌더링되는지가 검증입니다.
마무리
한국어 타이포그래피를 다룬다고 거창한 라이브러리부터 필요한 것은 아닙니다. 이번에 쓴 핵심 속성은 세 개입니다.
word-break: keep-all;
overflow-wrap: anywhere;
white-space: pre;
중요한 것은 속성의 개수보다 어디까지 같은 규칙을 적용할 것인지입니다.
제목과 CTA가 모두 링크라고 같은 primitive를 쓰면 iOS가 IOS가 될 수 있습니다.
본문과 코드가 모두 텍스트라고 같은 줄바꿈을 쓰면 한쪽은 단어가 쪼개지고, 다른 쪽은 화면을 밀어냅니다.
CSS는 문맥을 모릅니다. 우리가 selector로 문맥을 알려줘야 합니다.
한국어 단어는 지키고, inline code에는 탈출구를 열어주고, code block에는 스크롤을 주세요. 네, 규칙이 하나로 끝나지는 않습니다.
대신 독자가 단어를 다시 조립하거나 코드가 화면 밖으로 도망가는 일은 줄어듭니다. 그 정도면 CSS 세 줄치고 꽤 괜찮은 거래 아닐까요? ㅎㅎ
다음 글도 받아보세요.
글을 끝까지 읽으셨다면, 다음 글은 받은편지함이나 RSS 리더에서 만나보세요.