Java과 charset

java로 개발한다고 하면 극복해야 하는 것들이 몇가지 있다. 초기에는 클래스패스로 고생좀 하고, 광할한 stream들에서 방황도 하고, 그리고 charset관련하여 글자 깨지는것에 당황도 하게 된다. 인터넷을 찾아보면 많은 문서들이 있다. 이렇게 하면 되요, 저렇게 하면 되요라는 문서들이 대부분이지만 실제 적용해 보면 뭔지 모르지만 잘 안된다. 좀더 깊이 있는 문서들도 있지만 잘 이해도 안되고.

어쨋든 이제는 charset은 처리하는데 문제는 없게 되어서 다행인데, 이에 대하여 내가 가지고 있는 개념을 정리해 보고자 한다. 믿을만한 문서를 기반으로 한 것이 아니라 나만의 착각일 수도 있다. 하지만 개발하는데는 도움이 될 수도 있겠다.

다음과 같은 코드를 실행 시키면 결과는 당근 잘 나온다.
public class CharsetTest {
  public static void main(String[] args) {
     System.out.println("하이");
  }
}

그런데 위 코드를 컴파일해서 일본어 윈도우에서 실행시킨다면? 혹은 java 실행 옵션에 -Dfile.encoding=euc_jp를 준다면? 물음표 두개 "??"가 출력된다. 코드가 잘못된 것일까? 일본어 윈도우에서 한글이 출력되지 않는거라면 차라리 정상이 아닐까 싶다. 반대로 아랍어 윈도우(있는지 모르겠지만)에서 아랍어가 잘 출력되는 java 프로그램이 한글 윈도우에서 글자가 깨진다면?

깨지는 것이 정상이다.

java에서는 글자들에 대한 값들을 unicode로 가지고 있다. 컴파일된 클래스들에 포함된 실제 값들은 unicode들이다. 그런데 unicode는 java이외의 시스템에서는 처리하질 못한다. 예를 들어 웹 브라우져나 stdout같은 디바이스들은 unicode값을 처리하지 못한다. 그렇기 때문에 외부의 시스템이 이해할 수 있는 약속된 값으로 변환하여야 한다. 이러한 약속들을 code page, encode, charset등으로 칭한다. 그리고 특정 외부의 시스템은 처리할 수 있는 charset이 한정되어 있다. 한글 윈도우라면 한글을 위한 utf8, euc_kr, MS949등을 처리할 수 있다. 화면에 결과가 출력되는 것을 보면 OS와 java application간에는 STDOUT이라는 디바이스가 그 매개 역활을 한다. java 어플리케이션은 unicode를 특정 charset으로 변환한 값을 STDOUT에 전달하고 OS는 STDOUT에 전달된 값을 자신이 가지고 있는 charset의 폰트와 연결하여 화면에 출력한다. 그런데 java application이 사용한 charset과 OS가 사용하는 charset이 다르면 글자가 깨져 보이게 된다. java application에서 사용하는 charset값은 system 프로퍼티 file.encoding의 값을 사용한다. 이 값은 -Dfile.encoding=euc_kr와 같은 java 옵션으로 설정할 수 있으며 따로 설정하지 않으면 OS에서 설정한 값이 사용된다. 한글 위도우에서는 MS949이다. 만약 jsp에서 특정 charset을 설정하지 않는 다면 웹브라우져에게 보내지는 값은 역시 MS949로 보내지게 되고 웹브라우져에서 MS949로 처리하도록 설정하지 않느다면 글자가 깨질 것이다. 비슷하게 DB에 글자를 저장할 때 따로 charset을 따로 설정하지 않으면 역시 MS949로 인코딩된 값이 DBMS에 전달되고 만약 DBMS가 MS949를 처리하지 못한다면 한글로 된 데이타는 제대로 검색되지 않을 것이다.

한글이 안깨지게 하는 원리는 단순하다. java 어플리케이션에서 인코딩에 사용하는 charset과 상대편에서 처리할 charset을 동일하게만 하면 된다. jsp나 servlet에서는 contextType, request.setCharacterEncoding()등과 같은 방법으로 이를 설정해 준다. 이렇게 하면 인코딩에 해당 charset을 사용하고 http response에 해당 charset의 정보를 포함하여 웹 브라우져가 사용할 charset을 알려준다. DBMS와의 경우에는 DBMS 자체의 기본 charset을 설정해주어야 하고 MySQL의 경우 연결 시에 java application에서 사용할 charset을 알려준다. 그리고 당근 데이타를 db에 write하거나 read할 때 설정한 charset을 사용한다. 그리고 socket통신과 같이 byte[]로 데이타를 보낼 때도 특정 charset으로 인코딩하고 반대로 디코딩하면 된다.

다음과 같은 코드의 출력은 언제나 같을까?
  byte[] bytes = "하이".getBytes();
  for(int i=0; i<bytes.length; i++) {
    System.out.println(i+" : "+bytes[i]);
  }

그렇지 않다. String 객체와 byte[] 간의 변환에는 반드시 charset이 사용된다. 위의 코드와 같이 명시적으로 charset을 설정하지 않은 경우 system 프로퍼티 file.encoding에 설정된 값이 사용된다. file.encoding의 값은 OS에 의해서 결정되므로 일본어 윈도우에 가서 실행하면 다른 값을 보이게 된다. String 객체와 byte[] 간의 변환 시에는 다음과 같이 반드시 charset을 설정해 주어야 한다.
  byte[] bytes = someString.getBytes("utf8");
  String newString = new String(bytes, "utf8");

다음과 같은 jsp 코드를 한글 윈도우위의 WAS와 일본어 윈도우위의 WAS에 올려놓으면 결과가 어떨까?

<%@ page contentType="text/html; charset=EUC-KR" %>
<%
    out.print("헬로"); // to web browser
    System.out.println("하이"); // to stdout
%>

웹브라우져로 보는 것(out에 의한것)화면은 둘이 똑같을 것이다. 그런데 로그파일에 찍힌 것은(System.out에 의한 것)은 일본어 윈도우에서는 깨졌을 것이다. 왜 그럴까 보자. 한글 위도우이건 영문 윈도우 이건 "헬로"를 출력하는 것은 HttpResponse의 out으로 인코딩 시에 첫 줄의 charset에 명시된 EUC-KR을 사용한다. 그렇기 때문에 동일한 값으로 인코딩 되고 웹브라우져에서는 당연히 같은 결과를 보여준다. 그런데 "하이"를 출력할 때 부분은 jsp와 관계없는 것으로 시스템 프로퍼티 file.encoding에 설정된 charset을 가지고 인코딩 한다. 한글 윈도우에서는 MS949로 인코딩하여 제대로 출력하겠지만 일본어 윈도우에서는 shift_js(요거 확실한지는 기억이...)로 인코딩하려 할 것이고 "헬로" 글자에 해당하는 값이 없어서 "??"가 출력될 것이다. 이 얘기는 코드 자체는 정상이라는 것이고, 단지 STDOUT으로 전달된 값을 console에 제대로 그려주지만 못한다는 것을 의미한다.

정리하면 java에서 한글이 깨지는 경우는 2가지이다. 첫번 째는 한글을 인코딩할 때 charset을 설정안했거나 잘못 설정한 경우, 두번째는 상대방 시스템(웹 브라우져, 디비, STDOUT의 콘솔등)과 사용한 charset이 다른 경우이다. 그리고 System.out으로 뿌려진 것은 시스템에 따라 깨질 수도 있으니, 제대로 처리되고 있는 지를 확인하기 위해서는 String의 객체값을 byte[]로 변환하여 그 값을 숫자로 직접 눈으로 보는게 확실하다.

기타 charset관련 팁이라면. 왠만하면 모든 곳에서 charset을 utf8로 설정하길 권한다. DBMS의 default charset으로. jsp, servlet에서의 설정으로, 그리고 프로젝트의 파일을 저장할 때의 설정으로. 특히 프로젝트의 charset 설정은 java 소스의 컴파일에는 영향을 끼치지 않지만, 영어가 아닌 다른 언어로 적어놓은 주석이 타 윈도우에서 깨지는 것을 방지해 줄 수 있다. 고민하지 말고 charset 설정이 필요한 모든 곳에 utf8로 하기를 권한다.

by 어플로잇 | 2009/07/23 15:49 | java tip | 트랙백 | 핑백(1) | 덧글(4)
트랙백 주소 : http://aploit.egloos.com/tb/5020211
☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]
Linked at [運]과 함께하는 세상 : 자.. at 2013/08/29 15:37

... Java 와 Charset ... more

Commented by 김태정 at 2009/10/21 02:22
정말 최고의 설명입니다. :")
좋은 정보 감사드립니다.
Commented by 나창훈 at 2010/08/27 17:11
좋은 자료 정말 감사합니다.

하지만 익플이 문제인지 톰캣이 문제인지 윈도우가 문제인건지..

url단에서 던지는 겟방식의 파라미터는 어떤 방법을 써도 안되네요.ㅠ_ㅠ..우어~!

죄송한데.. 혹시나 시간이 되신다면..

http://kin.naver.com/qna/detail.nhn?d1id=1&dirId=1040201&docId=115837724

지식인에 질문을 했습니다.. 참고가 될 내용이 있으면 답변 좀 부탁드릴게요;
Commented by 이상익 at 2011/03/08 15:59
질문하나 드립니다..

다른 피씨는 다 괜찮은데 유독 하필이면 클라이언트 PM 자리에서...
한글이 깨지는 현상이..ㅠㅠ

JAVA WEB START 로 SWT 를 실행시키는 프로그램입니다...
JAR 가 참조가 안되 JAVA 콘솔을 켜서 확인해보니

user.dir = C:Documents and Settings?????????? ???
user.home = C:Documents and Settings??????
요렇게 다 깨져있는거에요...ㅠㅠ

콘솔에서 환경 찍어보면 분명
user.country = KR
user.language = ko
file.encoding = MS949....

이렇게 되어 있는데 말이죠..

Java Web Start 1.6.0_21
JRE ???? ??? 1.6.0_21-b07 Java HotSpot(TM) Client VM
????? ? ???? = C:Documents and Settings??????
----------------------------------------------------
c: ??? a ?????
f: ??? ??????? ??u ???
g: ?????? ?÷???
h: ?? ???? ????? ???
m: ??? ??? ?μ?
o: ?α? ?????
p: ????? ???? ??ε?
q: ??? ?????
r: ??a ???? ??ε?
s: ?y??? ?? ???? ??? ???? ????
t: ?????? ??? ????
v: ?????? ???? ????
0-5: ???? ?????? <n>(??)?? ????
----------------------------------------------------

이렇듯 한 피씨에서만 콘솔내 모든 한글이 깨집니다....JAVA 실행시 encoding 이 os 를 안따라가는 경우가 있을까요?
Commented by 어플로잇 at 2011/03/14 17:36
워낙 댓글 없는 이곳에 댓글 보고, 반가움, 놀람, 당황입니다. 그리고 1주일이 지나서야 봤네요.

원인 추측이 어렵네요. PM의 PC에서만 그런 현상이 발생한다고 하니, 환경적인 것으로 추측해봐야 겠습니다.

cmd 화면에서 java -version의 출력과 다음 코드의 출력을 알려주세요.
Properties properties = System.getProperties();
Set<Object> keySet = properties.keySet();
for(Object keyObject : keySet) {
String key = (String)keyObject;
System.out.println(key+" : "+System.getProperty(key));

}

의심되는 것중의 하나가 sun.jnu.encoding이라는 속성이네요.

:         :

:

비공개 덧글

< 이전페이지 다음페이지 >