개요 #
(최근에 회사 업무 중에) 고객이 업로드한 파일 이름의 자모음이 분할되어 저장되는 문제가 있었다. Mac 환경의 클라이언트가 해당 기능을 사용할 때 한글이 분할되는 현상이었다.
예를 들어, 클라이언트에서 가나다라.jpg
라는 파일을 업로드하면 ㄱㅏㄴㅏㄷㅏㄹㅏ.jpg
의 이름으로 저장이 되었다.
관련된 내용을 찾아보니 인코딩, 정규화와 같은 키워드로 많은 내용이 있었다. 이것들을 읽고 내용을 정리해보고자 한다!
문자코드표, 문자인코딩 #
컴퓨터는 데이터를 바이트(혹은 숫자) 단위로 처리한다. 그러므로 ‘문자/글자’를 나타내려면 바이트/숫자 <–> 문자/글자를 매칭시켜줘야한다. 어떤 기준으로, 어떻게 매칭시켜야 할 지에 대해 규칙을 정해놓은 것들이 ASCII, Unicode, UTF-8, EUC-KR 등인 것이다.
이런 것들을 통틀어 인코딩 규약 이라고 한다..?
위키백과{:target="_blank"}에서는 인코딩을 “정보의 형태나 형식을 변환하는 처리나 처리 방식” 라고 설명하고 있다.
‘문자 인코딩이란?’ 여기의 글을 읽었는데 단번에 이해되었다. 블로그의 글을 조금 요약해보면, 다음과 같다.
- 문자코드표 : 인코딩할 때 사용되는 매칭표(코드표). 대표적으로는 ASCII코드표, Unicode표가 있음. 모든 곳에서 동일하게 사용하기 위해 만든 것.
- 문자인코딩: 문자코드표를 어떤 방식으로 변환할지에 대한 방법, 동일한 문자코드표를 사용해도 변환(처리)하는 방식이 다를 수 있음. 이유는 ‘효율성’을 위해서. 대표적으로 UTF-8, UTF-16, EUC-KR 등의 인코딩 방식이 있음.
문자코드표 (문자코드) #
ASCII(American Standard Code for Information Interchange, 정보 교환을 위한 미국 표준 코드)
(풀네임을 읽어보면) American Standard Code, 즉 영어(알파벳 + 일부 문자)를 위해 사용되는 표준 코드(표)이다.
7bits 표현 방식으로 알파벳, 숫자, 간단한 문장부호 등 128개의 글자를 나타낼 수 있다. (2^7 = 128) 알파벳(영어대소문자) 52개 + 숫자 10개 + 특수문자 33개 + 제어문자 33개로 총 128개이다. 이 중 printable 문자(출력 가능한 문자)는 알파벳, 숫자, 특수문자로 95개(32 ~ 126)이다. Non-printable 문자(출력 불가능한 문자)는 제어문자로 33개(0 ~ 31, 127)이다.
Unicode
영어(ASCII)를 포함하여 전세계의 언어, 다양한 문구/부호를 표현하기 위해 사용되는 코드표이다.
다양한 방식으로 구현이 가능하다. 대표적인 방식으로 UTF-8
, UTF-16
의 방법이 있다. (유니코드를 실제 파일 등에 어떻게 기록할 것인지를 표준화한 것이다. 유니코드는 문자를 각 숫자에 대응시킨 코드표에 불과하고 이를 실제 비트로 어떻게 표현할지에 대한 방법은 다양한 것이다.)
문자인코딩 #
UTF-8 / UTF-16
UTF-8
1~4 Bytes 를 사용한다. UTF-16
2~4 Bytes 를 사용한다. 이렇게 여러가지 방식이 존재하는 이유는 앞서 말했듯이, 효율성을 위함이다. 표현할 문자에 따라 1byte로 표현할 수도, 2~3byte로 표현할 수도 있다. 즉, 가변적인 것이다. 예들 들어, 서구권 언어(알파벳/ASCII 등)에서는 대부분의 문자를 1byte 만으로 표현할 수 있어서 UTF-8
을 사용하는 것이 효율적이지만, 아시아권의 문자(특히 한중일)를 나타낼 때는 UTF-16
을 사용할 때 더 효율적이다.
* UTF-8의 경우 ASCII 코드표와도 호환이 되며, 최근에 가장 많이 사용되는 인코딩 방식이다.
Text : 가
Binary : 11101010 10110000 10000000
UTF-8: \xEA\xB0\x80 (3byte)
UTF-16 : \uac00 (2byte)
EUC-KR (Extended Unix Code - Korea)
한글 지원을 위해 사용되는 인코딩 방식이다. 2bytes 형태의 완성형 코드표를 매칭시킨다.
CP949 (Code Page 949)
한글 지원을 위해 사용되는 인코딩 방식이다. EUC-KR (2bytes) 로 표현할 수 없는 문자를 표현하기 위해 MS사에서 개선/확장된 인코딩 방식이다. 따라서 EUC-KR 과 호환 가능하다. MS949라고 부르기도 한다고 한다.
유니코드 정규화 ? #
아래 블로그들에 정말 쉽고, 상세히 정리되어 있다.
다시 처음 문제였던 한글 자모음이 분리되는 문제로 돌아와서, 이 문제는 뭐일까 검색해보니 ‘유니코드 정규화’ 라는 키워드에 대해 알 수 있었다.
유니코드에서 ‘결합문자’ 라는 개념이 있다. 예를 들어 ‘a’라는 문자와 ’e’라는 문자를 결합해서 ‘ae’의 하나의 문자가 되는 것이 결합문자이다. 근데 이 결합문자를 만들 때에도 어떻게 만들지에 대한 방법/방식이 4개가 있다. 이 방식을 정규화(normalization)라고 한다. 인코딩 방식이 다르면 문자가 깨지는 것 처럼, 정규화 방식이 다르면 의도하지 않은 문자로 보이게 되는 것이다.
정규화는 문자열을 분해/결합 + 정준/호환 의 방법이 조합되어 4가지 방법이 있다고 한다. (간단하게 말하면 어떻게 분해하고 어떻게 결합할 건지에 대한 방법인 것이다.)
유니코드는 텍스트를 한 가지 규칙을 이용하여 정규화하여 저장하는 것을 권장한다고 한다.
정규화 규칙에는 아래 4개의 방식이 존재한다.
- NFC: Normalization Form Canonical Composition : 모든 글자를 분해한 후에, (표준에 명시된 순서에 따라) 다시 합치는 방식이다. 다만, 옛 한글 자모의 결합은 결합하지 못한다. NFC는 많은 GNU/Linux 시스템, Windows에서 주로 사용한다.
- NFD: Normalization Form Canonical Decomposition : 모든 글자를 분해하여 저장한다. NFD는 macOS 시스템에서 주로 사용한다.
- NFKC: Normalization Form Compatibility Composition
- NFKD: Normalization Form Compatibility Decomposition
NFKC와 NFKD는 한글자모/한글음절 영역 이외의 한글 유니코드 영역을 처리할 때 유용하게 사용할 수 있다고 한다.
결론 #
결론적으로 Mac OS 와 Windows/Linux 환경에서 사용하는 정규화 방식이 다르기 때문에 한글 자모음이 분리되어 보여지는 현상이 나타난다.
PHP 에서는 이 정규화 방식에 대해 처리할 수 있는 ‘Normalizer 클래스’를 제공한다.