Unicode와 UTF-8의 차이점이란?

오늘 열렬히 코드를 보고 있다가 유니코드(Unicode) 복원 부분에 대한걸 봤다. 역시나 Lucene코드를 뒤져보다 나온 내용이다. Lucene은 디폴트로 유니코드셋을 쓴다. 그러니 UTF-8은 피할수 없이 꼭 거쳐가야만 될 부분이였다.

일단 코드는 이렇다. (역시 Python 코드 ㅋㅋ)

def readChars(self, length):
   buffer = []
   for i in range(length):
       b = self.readByte()
       if (b & 0x80) == 0:
           buffer.append(unichr(b & 0x7F))
       elif (b & 0xE0) != 0xE0:
           tmpInt = (((b & 0x1F) << 6)|(self.readByte() & 0x3F))
           buffer.append(unichr(tmpInt))
       else:
           buffer.append(unichr((((b & 0x0f) << 12) | 
                        ((self.readByte() & 0x3F) << 6) |
                        (self.readByte() & 0x3F))))
   x =  u”.join(buffer)
   #print “READING: %r” % x
   return x

코드를 아무리 봐도 이해가 안가서 UTF-8에 대한 글을 찾아서 읽어봤다. (솔직히 이때 Unicode와 UTF-8을 같은걸로만 알고 있었다. ‘말만 다르게 표현한것이다’ 정도 말이다.)

내가 찾은 글은 위키백과에 있는 UTF-8에 관한 글이였다. 아주 좋은 글이였다. 진짜 UTF-8에 대해서 알고 싶은분은 꼭 필독하길 권하는 문서다.

그런데 여기서 뭘 UTF-8로 변환하는지 조차 모르고 그저 변환 방법만 찾아서 봤다. 일단 뭔가를 UTF-8로 변환하는 방법이랜다.ㅋㅋㅋ

(아래서 열거한건 modified UTF-8 방법이다.)

'\u0001' 에서 '\u007F' 사이의 모든 문자는 1byte로 처리된다. 여기서 가장 중요한 부분은 추가 바이트가 있는지 판단하는 마지막 1bit다 :

Bit Values
Byte 1
0

bits 6-0

널문자인 '\u0000'와, '\u0080' 에서 '\u07FF' 사이의 2바이트 문자는 아래와 같이 표현된다. 물론 여기서 중요한건 각 칸의 bits다:

Bit Values
Byte 1
1

1

0

bits 10-6
Byte 2
1

0

bits 5-0

'\u0800' 에서 '\uFFFF' 사이의 2바이트 문자는 아래와 같이 3바이트로 표현이 된다.:

Bit Values
Byte 1
1

1

1

0

bits 15-12
Byte 2
1

0

bits 11-6
Byte 3
1

0

bits 5-0

그럼 여기서 standard UTF-8 과 modified UTF-8의 차이점을 알아보자!

  • '\u0000' 와 같이 null문자를 2바이트로 표현함으로서 인코딩된 문자열에 null문자가 없게한다.
  • 오직 1,2,3 바이트만으로 표현된다.
  • 이외 별도의 추가문자는 별도의 쌍으로 제공한다.

이렇게 함으로 얻는 잇점이 무엇인가 생각해 보는게 중요하다.

그럼 여기서 물어보자!

“그럼 unicode와 utf-8의 차이점이 무엇인가?”

바로 저장용량의 차이이다.(이건 순전히 내가 판단한 부분임, 모르겠다 이게 맞는지 하지만 내가 보니까 그렇다. )
그냥 unicode로 저장할수도 있다. 그런데, 왜? utf-8로 변환해서 저장하고 또 불러올때 다시 디코딩 작업을 할까?

그건 문자의 길이정보를 UTF-8 자체가 가지고 있기 때문이다. 첫 바이트만 읽고 이게 3바이트가 연결되서 한문자가 되는지, 아니면 2바이트가 연결되서 한 문자가 되는지 알길이 없기 때문이다. 그래서 UTF-8 변환시 첫부분 비트를 보라고 한것이다.

상위 1~3비트 만 보면 이게 최상위 비트인지 아니면, 1바이트 문자인지 등 많은 정보를 알수 있다. 그래서 Unicode문자를 만들때 편하게 만들수 있는것이다.

만일 UTF-8과 같은 인코딩 방법이 없었다면, 한글자마다 길이정보를 앞부분에 넣어줘야 됐을것이다.

그럼 위의 코드는 무엇을 말하고 있을까?

바로 UTF-8코드를 읽어서 UTF-8 -> Unicode로 변환하는 모듈이다. 이 부분에 대한 설명은 Java Doc에 아주 좋은 문서가 나와있다. (readUTF)

UTF-8 -> unicode 로 변환되는 모듈은 위의 UTF-8 문서에 나와있으니 참고하길 바란다.
와우~! 이렇게 해서 유니코드에 대한 정리 존재의미까지는 아니더래도 UTF-8 인코딩의 존재의미는 정리가 되었다고 본다.

오늘도 저 코드 몇줄때문에 그동안 아련하게 알고 있었던 Unicode에 대한 궁금증을 확 풀어버렸다. 그리고 나름대로 UTF-8 인코딩의 필요성도 파악이 되었다. 또한 Java Doc에 굉장히 좋은 알짜배기 정보들이 의외로 많다는걸 알았다. 물론 쓰는 사람입장에서는 그냥 넘어가기 쉬운 부분이지만, 도움 정말 많이 된 부분이였다.

느낌상 어제의 VInt 형 구현에 대한 포스팅보다 더 큰거 같다. 물론 Vint나 UTF-8 이나 효율적인 저장 방법에 대한 인코딩 방법이니 굉장히 많이 비슷하더라.

오늘도 중요한걸 깨우쳐서 집에가는 발걸음이 가벼울거 같다. 얏호~!

CC BY-NC 4.0 Unicode와 UTF-8의 차이점이란? by from __future__ import dream is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.