JAVA도 인코딩때문에 꼬인다. JAVA

JAVA는 내부에서는 유니코드 문자로 움직인다. (정확히는 유니코드 중에 약간 변형된 UTF-16인데 잘 모르면 그냥 넘어가자.) 하지만 외부 환경이 그렇지 못 한 경우가 많아서 입출력단에서 적절한 인코딩 변환을 통해 데이터를 보내고 받는다. 이 때 외부환경, 즉 OS의 인코딩은 file.encoding값에 저장이 된다. System.in혹은 System.out을 통해 왔다갔다 하는 데이터도 보면 file.encoding에 정의된 인코딩에서 유니코드로 변환해서 들어오거나, 유니코드에서 해당 인코딩으로 변환되서 나간다. 그러니 OS에서는 그대로 받아서 뿌려주면 된다. 그리고 여기서부터 꼬이기 시작한다.

내 리눅스 환경은 100% 유니코드 환경, 정확히 말하면 UTF-8환경에 맞추어져 있다. 자바 설정 기본값을 보면 file.encoding도 UTF8로 지정이 되어 있다. 이클립스에서도 기본값이 UTF-8이고, 내가 주도해서 만든 프로젝트도 모두 UTF-8을 기본으로 만들게 했다. 참 잘 돌아간다.

맥오에스로 넘어와보자. Mac OS는 사용자의 언어 설정에따라 기본 인코딩이 바뀐다. 영어 상위라면 MacRoman이고 한국어 상위라면 EUC-KR이다. 나는 영어를 잘 못하는 한국인이라 한국어 상위를 쓰고 있고, 그래서 JAVA의 file.encoding은 EUC-KR로 되어 있다.

eclipse에서 전역 환경의 기본 인코딩이 EUC-KR로 잡혀있다. 이것은 사용자가 바꾸면 되기 때문에 UTF-8로 바꾸었다. 여기까지도 별 문제가 없었다. 하지만 이클립스 내부에서 실행하는 콘솔은 시스템 설정을 따라서 움직인다. 디버그 콘솔은 여전히 EUC-KR이었다. 이것이 문제가 될 수도 있다. 이 문제는 나중에 쓰자.

xml파일을 읽어들여 데이터를 파싱하는 테스트코드에 문제가 있는 것을 발견했다. 한글 글자가 다 깨져서 저장이 되는 것이다.리눅스에서 쓸 때는 잘 되던게 맥오에스로 넘어오니 에러가 난다. 근 하루간의 삽질끝에 원인을 발견했다.

문제가 된 xml파일을 읽을때 시스템의 인코딩으로 간주해서 읽었던 것이다. 파일 읽는 코드는 아래처럼 구현했었는데,

BufferedReader reader = new BufferedReader(new FileReader(filepath));

FIleReader클래스는 읽어들이는 파일의 인코딩을 무조건 file.encoding 으로 간주해 읽으려 했기 때문이었다.
어쨌든 인터넷을 뒤져 아래와 같은 코드로 바꾸었다. 파일을 무조건 UTF-8문서로 간주해 읽기다.
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(filepath),"UTF8"));

애초에 파일을 읽는 이유가 jdom에서 xml파싱을 하기 위한 거였는데 SAXBuilder클래스의 build메써드를 보니 인자가 StreamReader가 아닌 파일경로로 지정하는게 있더라. 이것을 쓰자 알아서 xml파일 안의 인코딩 정보를 찾아서 잘 읽었다.

----------------------------
아직도 이 페이지를 찾아오는 분들이 많아서 약간 더 부연설명을 추가함.

JAVA의 Reader인터페이스는 기존의 Stream보다 더 진보된 놈이고 쓰기도 편하지만 JVM 외부와 접점을 가지게 되는 FileReader같은 놈은 아닌 것 같다. Reader는 기본적으로 자바 캐릭터를 사용하는 것을 전제로 움직이는 것이고 이것은 사실 인코딩이 UTF-16으로 고정된 것이라 별도로 인코딩을 설정하는 것 자체가 없다. (애초에 Reader나 Writer는 JVM안쪽에서만 돌아가는 데 초점이 맞춰져 있는 것처럼 보인다.) 하지만 바깥 세상은 좀 다르다. 대부분의 요즘 OS들도 커널단 내부에서는 문자를 대부분 UTF-16을 사용하지만 외부와 통신을 하는 경우는 대체로 이것을 UTF-8로 변환해서 통신을 한다.

JVM도 비슷하게 동작을 한다. 파일이나 콘솔에서 받는 입력 데이터는 UTF-16으로 변환을 한 다음 JVM내부에 저장된다. (String클래스나 CharacterSequence클래스가 들고 있는 것은 UTF-16문자열이다) 이 변환을 하는 대상 인코딩을 Reader나 Writer는 따로 지정하는 것이 없이 시스템의 인코딩을 사용하고, Stream은 수동으로 지정하는 기능이 있다.

설명이 두서없는데 결론을 쓰자면 Reader/Writer와 Stream은 서로 역할분담을 해서 사용하는 것이다.
JVM내부에서의 문자 스트림 처리는 Reader/Writer를 사용하고, JVM외부의 데이터 스트림 입출력에는 Stream을 사용하는 것이다. 그 결과 위에 쓴 예제 코드처럼 InputStream으로 파일을 적절한 인코딩을 설정해 변환해 가져온 다음 Reader로 감싸서 내부에서 사용하면 된다.  

핑백

덧글

댓글 입력 영역



메모장

W 위젯