실험용으로 지은 다섯 문장
글자 수를 세는 방법이 몇 가지나 되는지 보려고, 세는 방식마다 결과가 갈리도록 일부러 재료를 섞은 다섯 문장을 지었어요. 공백, 줄바꿈 하나, 엄지 이모지, 영어 단어, 받침이 많은 낱말과 받침이 없는 낱말, 그리고 자모를 하나씩 따로 입력한 한글 한 글자가 들어 있어요.
오늘 회의에서 new plan이 OK를 받았어요. 다들 박수를 쳤고 저는 👍를 보냈어요.
받침이 많은 '닭볶음탕'과 받침이 없는 '바나나'는 바이트도 같을까요? 공백과 줄바꿈도 글자로 셀까요? 마지막 '한'은 자모를 하나씩 입력했어요.
두 번째 문장 뒤에서 줄이 바뀌고, 나머지 문장 사이는 공백 한 칸이에요. 마지막 문장의 작은따옴표 안 글자는 화면에서는 '한'으로 보이지만, 완성된 글자 하나가 아니라 첫소리 ㅎ, 가운뎃소리 ㅏ, 끝소리 ㄴ에 해당하는 조합용 자모 세 개(U+1112, U+1161, U+11AB)가 이어진 상태예요. 이 페이지의 인용문에도 그 상태 그대로 넣어 두었으니, 복사해서 직접 세어 봐도 같은 결과가 나올 거예요.
같은 글이 받는 아홉 가지 숫자
아래 표의 앞쪽 다섯 줄은 이 사이트 글자수 세기의 계산 함수를 node로 직접 실행한 값이에요. 나머지는 자바스크립트의 기본 기능과 파이썬으로 따로 구했고, 겹치는 항목은 두 언어의 결과가 모두 같았어요.
| 세는 방법 | 결과 | 무엇을 하나로 보나 |
|---|---|---|
| 글자수 (공백 포함) | 133 | 코드 포인트 하나 |
| 같은 기준, 줄바꿈을 2로 | 134 | 줄바꿈만 둘로 |
| 글자수 (공백 제외) | 105 | 공백 27개와 줄바꿈 1개를 뺌 |
| 바이트 (한글 2바이트) | 218 | 영문·숫자 1, 나머지 2 |
| 바이트 (UTF-8) | 304 | 실제 UTF-8 부피 |
| 눈에 보이는 글자 수 | 131 | 사람이 한 글자로 읽는 덩어리 |
| 자바스크립트 length | 134 | UTF-16 단위 하나 |
| NFC로 정규화한 뒤 | 131자, 298바이트 | 자모 셋을 '한' 하나로 합침 |
| CP949로 저장 | 저장 불가 | 이모지 1개와 조합용 자모 3개를 표현 못 함 |
같은 다섯 문장이 105에서 304까지 벌어져요. 어느 숫자도 틀린 게 아니고, 각자 다른 것을 세고 있을 뿐이에요. 이 차이를 만드는 원인은 크게 세 가지예요. 공백과 줄바꿈을 어떻게 다루는지, 글자 대신 바이트를 세는지, 그리고 '한 글자'를 무엇으로 정의하는지예요.
바이트는 글자의 부피예요
UTF-8을 정의한 RFC 3629는 문자 번호의 범위에 따라 한 글자를 1~4바이트로 저장한다고 정해요. U+0000부터 U+007F까지는 1바이트, U+0800부터 U+FFFF까지는 3바이트, U+10000 이상은 4바이트예요. 한글 완성형 음절은 U+AC00에서 U+D7A3 사이에 있으니 모두 3바이트예요. RFC에도 '한국어' 세 글자가 9바이트로 바뀌는 예시가 실려 있어요. 엄지 이모지 👍는 U+1F44D라 4바이트예요.
표본 문장으로 확인하면 한글 음절 81개가 243바이트, 영문·문장부호·공백·줄바꿈 48개가 48바이트, 이모지가 4바이트, 조합용 자모 셋이 9바이트로 합계 304바이트예요. 받침은 바이트에 영향을 주지 않아요. '닭볶음탕'은 네 글자 12바이트, '바나나'는 세 글자 9바이트로, 받침이 몇 개든 음절마다 3바이트예요. 다만 자모를 풀어 쓴 상태라면 이야기가 달라져서, 같은 낱말을 NFD로 풀면 '닭볶음탕'은 36바이트, '바나나'는 18바이트가 돼요.
옛 한글 인코딩인 CP949(EUC-KR의 확장)는 사정이 달라요. 파이썬의 코덱으로 인코딩해 보면 한글 음절은 한 글자에 2바이트이고, 영문은 1바이트예요. 이건 특정 서비스의 규칙이 아니라 인코딩 자체의 성질이에요. 그런데 이 인코딩은 이모지와 조합용 자모를 아예 표현하지 못해서, 표본 전체는 인코딩 단계에서 오류가 나요. 표현할 수 없는 네 글자를 빼면 210바이트이고, 자모 셋을 '한'으로 합친 뒤 이모지만 빼면 212바이트예요. 글자수 세기의 '바이트 (한글 2바이트)' 기준은 이런 글자도 2바이트로 셈해서 218이 나와요. 실제 저장이 가능한지까지 판단해 주지는 않는다는 뜻이에요.
하나로 보이는데 여러 개로 세지는 글자
유니코드 텍스트 분할 표준(UAX #29)은 사람이 한 글자로 여기는 단위를 '사용자가 인식하는 글자'라고 부르고, 프로그램에서는 이를 자소 클러스터로 다룬다고 설명해요. 같은 문서는 글자 수 제한을 이 단위로 세면 데이터 크기 제한이 아니라고 짚으면서, 저장 크기를 묶어야 한다면 글자 수 제한과 바이트 제한을 함께 쓰는 예를 들어요. 표에서 '눈에 보이는 글자 수'가 바로 이 단위이고, 자바스크립트의 Intl.Segmenter로 셌어요.
표본에서 숫자가 갈린 곳은 두 군데예요. 하나는 이모지예요. 👍는 코드 포인트로는 1개지만 자바스크립트 문자열 안에서는 UTF-16 단위 2개를 차지해서 length가 2예요. 피부색을 붙인 👍🏽는 코드 포인트 2개, UTF-16 단위 4개, UTF-8로 8바이트인데 눈에는 하나예요. 다른 하나는 자모를 풀어 쓴 '한'이에요. 글자수 세기는 코드 포인트를 세기 때문에 이 한 글자를 3자, 9바이트로 세요.
정규화 표준(UAX #15)은 '가'와 'ᄀ+ᅡ'를 정준 등가의 예로 들고, 정준 등가인 글자들은 올바르게 표시되면 늘 같은 모양과 동작을 보여야 한다고 정해요. 그래서 화면만 봐서는 구별이 안 돼요. NFC로 정규화하면 자모가 완성형 음절로 다시 합쳐지고, 표본은 131자, 298바이트가 돼요. 표본에서 NFC를 적용한 글자 수와 눈에 보이는 글자 수가 같아진 것도 이 때문이에요.
입력칸이 내 글을 거절하는 순간을 점검하는 순서
제출하려는 칸이 글자 수를 어떻게 세는지 모를 때는, 기준마다 결과가 다르게 나오는 짧은 시험 문장을 넣어 보면 알 수 있어요. 시험 문장의 값은 글자수 세기로 계산했어요.
| 시험 문장 | 공백 포함 | 공백 제외 | 2바이트 규칙 | UTF-8 |
|---|---|---|---|---|
| 가(공백)나 | 3 | 2 | 5 | 7 |
| 가(줄바꿈)나 | 3, 2로 세면 4 | 2 | 5, 2로 세면 6 | 7, 2로 세면 8 |
| 👍 | 1, length로는 2 | 1 | 2 | 4 |
- 안내문의 단위부터 읽어요. '자'인지 '바이트'인지, 공백 포함인지 제외인지가 적혀 있다면 그 기준을 글자수 세기의 '목표를 세는 기준'에서 같은 것으로 골라요.
- 안내가 없으면 '가 나'를 넣어 봐요. 칸 옆 카운터가 3이면 공백 포함, 2면 공백 제외, 5면 2바이트 규칙, 7이면 UTF-8 바이트로 세는 칸이에요.
- 줄바꿈을 확인해요. 가와 나 사이에 공백 대신 줄을 바꿔 넣었을 때 숫자가 하나 더 늘면 줄바꿈을 둘로 세는 칸이에요. 그때는 '줄바꿈을 2로 세기'를 켜요.
- 이모지를 넣어 봐요. 👍 하나에 2가 늘면 UTF-16 단위나 2바이트 규칙으로 세는 칸이에요. 이모지를 빼는 것이 가장 확실한 대처예요.
- 숫자가 이상하게 크면 정규화를 의심해요. 보이는 글자보다 카운터가 훨씬 크면 자모가 풀린 글일 수 있어요. 메모장 같은 곳에서 새로 입력하거나, 문제의 낱말만 다시 타이핑해 보세요.
목표 기능으로 한도를 맞추는 법
글자수 세기의 '목표 글자수 (선택)' 칸에 한도를 적고, '목표를 세는 기준'에서 글자수 (공백 포함), 글자수 (공백 제외), 바이트 (한글 2바이트), 바이트 (UTF-8) 중 하나를 고르면 진행 막대와 남은 양이 나와요. 500자, 700자, 1,000자, 1,500자, 공백 제외 1,000자 같은 단추를 누르면 목표와 기준이 함께 바뀌어요.
표본 문장에 목표 120자(공백 포함)를 걸면 '목표보다 13자 넘었어요'라는 안내와 함께, 아래에 '한도를 넘은 부분이에요. 여기부터 줄이면 돼요.'라는 상자가 열리고 '모를 하나씩 입력했어요.'가 표시돼요. 120번째 코드 포인트까지가 한도 안이라는 뜻이에요. 기준을 바이트 (한글 2바이트)로 바꾸고 목표를 200으로 두면 넘친 양은 18바이트, 넘친 부분은 '하나씩 입력했어요.'로 바뀌어요. 한도를 넘으면 줄이기 도우미도 함께 열려 규칙으로 찾은 줄일 곳을 제안하지만, 뜻이 바뀌지 않는지는 직접 확인해야 해요.
글자수 세기가 세는 단위는 코드 포인트라서, 자모가 풀린 글이나 피부색 이모지가 섞인 글은 눈으로 센 것보다 크게 나올 수 있어요. 이 사이트의 숫자가 제출할 칸의 카운터와 다르면, 위 점검 순서로 그 칸의 기준부터 확인하세요.
팁 한도가 빠듯한 글은 한도보다 5% 정도 여유를 두고 마무리하세요. 줄바꿈 하나, 이모지 하나, 붙여 넣을 때 따라온 보이지 않는 글자 하나가 기준에 따라 1~4씩 숫자를 바꾸기 때문이에요.