본문 바로가기
느리게 변하는 지식

인코딩의 이해 with Java

by oncerun 2021. 8. 11.
반응형

 

과거 컴퓨터에게 문자를 표현하는 문제가 발생하였을 때 이기종 컴퓨터들이 문자 데이터를 교환하기 위해서는 표준이 필요했으며, 해당 표준은 ASCII 코드와 같은 표준 문자 인코딩이 나타났다. ASCII(American Standard Code for Information Interchange) 약자이며, 영어 알파벳 및 동일 형식의 유럽어를 포함한다.

* 이기종 :  소프트웨어가 두 부품 CPU와 GPU의 컴퓨팅 자원을 자유롭게 활용하는 시스템

 

문자를 표현한다는 것은 문자의 집합이 정의되어 있다는 것이고, 문자 집합은 표현할 문자와 순서가 지정된 것이다.

영어로 예를 들면 A ~ Z까지 문자 집합의 문자와 순서가 존재한다. 숫자, 특수문자, 제어 문자도 문자 집합에 포함되어 야한다. 이러한 문자 집합을 코드 형태로 표기한 것을 코드화 된 문자 집합(CCS, coded charachter set)이라고 한다. 그리고 문자 집합을 컴퓨터에 저장하기 위해서 옥텟 형태로 표현한 것을 인코딩 방식(CES , character encoding scheme)이라고 한다.

 

영어나 유럽어는 알파벳을 기초로 사용하여, 256개의 코드를 사용하여, 문자 집합을 표현하는 것이 가능하지만, 한국, 중국, 일본에서 사용하는 문자 집합은 개수가 많아 Extended ASCII로 모두 처리할 수없기 때문에 별도의 방안이 필요했다.

 

컴퓨터에서 한글을 표현하는 방법은 크게 조합형과 완성형으로 나눌 수 있으며, 세분화하면 N바이트 조합형, 3바이트 조합형, 7비트 완성형, 2바이트 조합형, 2바이트 완성형으로 나눌 수 있다.

조합형이란 한글의 제자 원리에 기반하여 초성, 중성, 종성에 각각 코드를 할당하는 방식이고, 완성형이란 '가', '나'와 같은 완성된 문자에 코드를 할당하는 방식이다. 이 중 완성형이 한글 표준안으로 채택되었고, 유니코드의 한글 표현방식에도 완성형이 먼저 채택되었다.

 

 

한글의 인코딩 방식의 EUC-KR은 2바이트 완성형의 인코딩 방식이며, CP949(MS949, x-windows-949)는 확장 완성형의 인코딩 방식이다. 그렇기에 EUC-KR은 2,350자의 한글을 CP949는 11,172자의 한글을 표현할 수 있다. 

하지만 JAVA에서는 CP949와 MS949를 다르게 취급한다. CP949는 IBM에서 처음 지정한 코드 페이지(sun.nio.ext.IMB949)가 기준이고, Microsoft가 제정한 확장 완성형은 MS949(sun.nio.ext.MS949)를 기준이다. 그러므로 Java에서는 CP949와 EUC-KR이 사실상 같으며, 확장 완성형을 사용하기 위해서는 MS949로 지정해야 한다.

 

하나의 문자 집합을 사용하는 문서는 여러 언어를 동시에 표현할 수 없다. 예를 들어 유로화를 나타내는 '€' 기호에는 ISO 8859-15의 코드 값 중 0 xA4가 할당, ISO 8859-1의 0xA4의 값은 '¤'이기에 문서마다 유로화 기호가 다르게 표현되는 문제가 발생한다. 이러한 문제를 해결하기 위해 전 세계적으로 사용하는 모든 문자 집합을 하나로 모아 탄생시킨 것이 유니코드이다. 

유니코드 값을 나타내기 위해서는 코드 포인트(code point)를 사용하는데, U+ 를 붙여 표시한다. 'A'의 유니코드 값은 U+0041로 표현한다. (혹은 \u0041로 표기)  U+(16진수 숫자)로 처리는 것이 관례이다.

유니코드는 논리적으로 평면이라는 개념을 이용하여 구획을 나누며, 0번평면인 기본 다국어 평면에서 16번 평면까지 모두 17개의 평면으로 이루어져 있고, 대부분의 문자는 U+0000 ~ U+FFFF 범위에 있는 기본 다국어 평면에 속하며, 보조 다국어 평면은 U+10000 ~ U+1 FFFF에 범위에 속한다. 

한글은 U+1100 ~ U+11FF 사이에 한글 자모 영역, U+AC00 ~ U+D7AF 사이의 한글 소리마디 영역에 포함된다.

 

 

유니코드의 인코딩 방식으로는 UCS-2, UCS-4, UTF-7, UTF-8, UTF-16, UTF-32 인코딩 등이 있다.

이 중 ASCII와 호환이 가능하면서 유니코드를 표현할 수 있는 UTF-8 인코딩이 가장 많이 사용된다. UTF-8은 코드 포인트 범위에 따라 인코딩 방식이 다르다.

 

나무위키 UTF-8

 

만약 코드값의 자릿수가 7비트 초과 11비트 이내인 경우, 총 2바이트로 표현된다. 가장 앞에 1을 써서 1바이트를 초과한다는 것을 선언한 뒤, 1을 하나 더 써서 이 뒤에는 1개의 바이트가 추가로 온다고 선언한다. 마지막으로 0을 써서 선언을 종료하면, 첫 3비트는 110이 된다. 남은 5비트는 문자 인코딩에 사용하고, 두 번째 바이트는 맨 앞 2비트로써 10을 써서 독립적인 문자가 아닌 전 바이트 후속 바이트라고 명시해주고 남은 6비트를 문자에 사용한다.

 

각 코드값의 자릿수마다 여러 규칙들이 존재한다. 나무 위키

 

예를 들어 "갑"의 유니코드 값은 0 xAC11인데 이진수로 표현하면 1010 1100 0001 0001이며 이는 총 16비트가 필요하므로, 11101010 10110000 10010001으로 표현된다. 완성형 글자 하나는 3바이트인데, 조합형은 자모 하나하나가 각각 3바이트이다. 그렇기에 옛 한글이 아닌 이상 조합형을 사용할 이유가 없다.

 

 

 

Java는 String에서 사용하는 인코딩은 UTF-16 BE(Big Endian)이다. 문자열 전송/수신을 위해서 직렬 화가 필요할 경우는 UTF-8을 사용한다. Javad의 DataInput, DataOutput 인터페이스 구현체에서는 문자열을 기록하거나 읽어 들일 때 이 변형된 UTF-8을 사용한다. 

U+0000을 2바이트로 표시하는 이유는 인코딩 된 결과에 널 문자가 나타나지 않도록 하기 위해서다. Null 문자를 문자열의 끝으로 처리하는 언어에서 U+0000을 읽을 때, 문자열의 끝으로 잘못 처리하는 일이 없도록 하기 위해서다.

 

Java에서 글자를 표현하기 위해서는 2바이트 크기를 가지는 char를 사용하는데, 전체 유니코드 글자를 2바이트로 표현할 수 없기 때문에  U+01000 이상의 코드를 표현하기 위한 CESU-8을 사용한다. 자바에서 변형된 UTF-8은 CESU-8에서 Null문자 처리(U+0000)를 추가한 것이다.

 

자바에서는 유니코드의 코드 포인트 값을 확인할 수 있는 메서드를 제공한다.

String.codePointAt(int);이다. 또한 인코딩 된 값을 알기 위해서는 getBytes() 메서드를 이용하면 된다.

 

자바는 항상 문자열을 UTF-16 BE 인코딩으로 저장하며, file.encoding 시스템 프로퍼티에 의해 인코딩 값이 결정된다는 것이다. String 객체 내부(메모리 상에서) UTF-16 BE 인코딩으로 저장하고, 문자열을 입/출력할 때만 사용자가 지정한 인코딩 값 또는 운영체제의 기본 인코딩 값으로 문자열을 인코딩한다. JVM 기본 인코딩은 JVM 로딩 시에만 초기화되므로, 코드 중간에서 file.encoding 프로퍼티를 바꾸는 것은 아무 의미가 없다. 만약 file.encoding이 지정되어 있지 않다면, OS 환경 변숫값을 따른다. Java에서 글자를 깨뜨리지 않으려면, 문자 집합의 이름을 지정해야 한다.

 

문자열 객체의 getBytes() 메서드로 바이트 배열을 얻고자 할 때, getBytes(String charsetName) 메서드를 사용하고,

반대로 바이트 배열에서 문자열 객체를 얻고자 할때, new String(byte [] b) 대신 new String(byte[] bs, String charsetName) 메서드를 사용한다.

 

웹에서 한글이 깨지는 이유는 브라우저 인코딩 값과 서버 인코딩 값이 다르기 때문이다.

많이 사용하는 Tomcat에서는 파라미터 인코딩 및 키와 값을 설정하기 위해

org.apache.catalina.connector.Request.parseParameters 메서드와

org.apach.tomcat.util.http.Parameters.processParameters 메서드를 이용하여 처리하고 있다.

 

웹에서 여러 인코딩을 지원하려면, 인코딩 된 URL 문자열과 사용한 인코딩 정보를 파라미터로 전달해야 한다.

value=인코딩 된 URL 문자열? 인코딩 종류=EUC-KR와 같이 말이다.

가능하다면, javascript의 encodeURL 메서드를 사용하는 것이 좋다.

Java의 URL Encoder.encode 메서드와 Javascript의 encodeURI 메서드는 공백(whitespace)을 '%20'으로 인코딩하느냐, '+'로 인코딩하느냐만 다르다. 마지막으로 encodeURIComponent 메서드는 encodeURI 메서드와 유사하지만, :;/=?&도 인코딩한다.

 

알아두면 좋은 것들

영문 MS Windows는 CP1252, 한글 MS Windows는 MS949가 기본 인코딩이다. 리눅스에서는 LANG 환경 변수에 따라 다르지만, ko, ko_KR, ko_KR.eucKR은 모두 EUC-KR 인코딩이며, ko_KR.UTF-8만 UTF-8 인코딩이다. CentOS의 경우 /etc/sysconfig/i18n에서 시스템 기본 인코딩을 설정할 수 있다. 참고로 i18n은 국제화(internationalization)를 의미하며, l10n은 지역화(localization)를 의미한다. 18과 10이라는 숫자는 i와 n 사이, 또는 l과 n 사이의 글자 수를 의미한다. 요즘 편집기는 여러 인코딩을 처리할 수 있으므로, 보통 문서의 처음에 BOM(Byte Order Mark)이라는 값을 지정하여 인코딩 정보를 저장한다. UTF-8은 0xEF 0xBB 0xBF이며, 나머지 인코딩에 대한 BOM 값은 위키백과(http://en.wikipedia.org/wiki/Byte_order_mark)를 참고하면 좋다.

 

 

출처 : naver d2

반응형

'느리게 변하는 지식' 카테고리의 다른 글

데드락  (0) 2022.08.04
CPU 작동 원리  (0) 2022.04.08
Lean 소프트웨어 개발  (0) 2022.04.05
소프트웨어 개발 방법론  (0) 2022.04.05
객체지향 설계 기법  (0) 2022.03.15

댓글