들어가며
회사에서 에러 메시지를 볼 일이 있었는데, DB에 데이터가 insert되지 않는 에러가 있었다. 특수문자 유니코드를 넣으면 DB에 데이터가 쌓이지 않는 이슈였다. 어떻게 해야 하지? 라고 생각하다가 이 기회에 유니코드를 한번 쭉 정리하면 좋겠다는 생각이 들었다.
아마도 이랬을 거 같은 유니코드 이야기
거의 모든 프로그래밍 언어에는 char형이 있다. 이건 8비트, 즉 1바이트로 이루어진 캐릭터 형이다. 왜 하필 8바이트였을까? 태초에는 8바이트로 세상의 모든 영어를 표현할 수 있기 때문이 아니었을까 하고 추측할 뿐이다. 8바이트는 128개의 코드를 표현할 수 있는데, 알파벳 + 숫자 + 특수문자 등등 하여 128개면 충분할 거라고 생각했을 것이다.
그러나 시간이 흐르고 흘러 128개의 문자로 모든 걸 표현할 수 없는 시대가 왔다. 한자는 말 할 것도 없고, 한글 또한 자모조합의 규칙이 너무 많아서 표현하는게 쉽지 않다.
그래서 1바이트로 부족한 나라들은 각기 스스로의 표준을 발전시켜 왔다. 한국의 경우 KSC5601 표준을 발전시켜 왔는데, 이렇게 각각의 나라가 각기 표준을 만들게 되면 하나의 규약으로 모든 언어를 표현하는 것이 쉽지 않게 된다.
여기서 유니코드가 짠! 하고 등장하게 된다. 유니코드는 “세계의 모든 문자를 표현할 수 있는 코드 체계를 만들자” 라는 사상 아래 시작되었다.
유니코드는(Unicode 3.0 까지) 는 2^16 이하면 세상의 모든 수를 표현할 수 있으리라 믿었다. 즉 16비트(2바이트)면 세상의 모든 언어를 표현할 수 있을 거라 믿었는데, 이 영역을 기본다중언어판(BMP, Basic Multilingual Plane)이라고 부른다. 유니코드 3.0까지는 여기에만 문자 코드가 부여되어 있었다.
그러나 수요는 계속하여 증가하는 법. 고문서는? 한자 고어는? 그리고 언어를 사용하는 곳이 많아지면서 수요는 점점 증가하였고 2^16으로는 부족해졌다. 그래서 유니코드 3.0부터는 보충언어판(Supplementary Planes)을 정의하였다. 이를 위해 기본다중언어판(BMP)의 2048자를 대행코드 영역으로 할당하고, 대행코드 영역 중 1024자를 상위대행, 1024자를 하위대행으로 정의하여 이 둘의 조합으로 2^20개 만큼의 문자를 정의할 수 있도록 하였다.
유니코드의 구조
유니코드는 17개의 언어판으로 구성되어 있다. 각 언어판은 2^16개의 문자를 저장할 수 있으니, 유니코드에 저장할 수 있는 문자는 17 _ 2^16 개이다. 그러나 대행언어판의 2048개의 문자를 제외해야 하니, 총 저장할 수 있는 문자 수는 17 _ 2^16 - 1 개이다.
그러나 유니코드만 가지고 문자를 직접 그릴 수 없다. 유니코드는 숫자 체계이고, 이를 실제로 인코딩을 해야 문자를 만들어 낼 수 있는 것이다. 여기서 등장하는 것이 꾸준히 들어왔던 UTF-8, UTF-16, UTF-32 …같은 친구들이다.
UTF-8
UTF-8은 인터넷이나 리눅스/유닉스에서 사용하는 8비트 인코딩 기법이다. 원래 유니코드의 특성에 맞춰서는 16비트로 인코딩하는게 맞지만, 그렇게 되면 영어 같은 문자들은 8비트를 손해보게 된다. UTF-8은 그런 문제가 없다. UTF-8로 인코딩하면 1-3바이트 가변 인코딩이 된다. (4바이트까지 사용할 수도 있지만, 4바이트까지 사용하는 경우는 BMP 바깥의 유니코드 문자를 표현할 때 이고, 이런 경우는 거의 존재하지 않는다.)
표현 방식은 16진법의 코드 범위를 이진수로 바꿔서 표현한다. 위키에 자세한 표현 방법이 나와있다.
UTF-16
이 방법은 16비트로 인코딩한다. 직관적인 것으로 보면 훨씬 더 직관적이다. BMP에 속하는 값들은 그냥 그대로 16비트 값으로 인코딩이 되고, 그 이상의 값들은 32비트로 인코딩이 된다.