Bulilder로 한글 xml 만들기

배치 작업을 통해 오라클 db에서 데이타를 가져와서 xml을 만들 일이 생겼습니다. 언제나 그렇지만 AcriveRecord로 데이타를 가져오고 거기에 xml은 Builder로 만들면 편하겠구나 하는 생각에 1시간정도 뚝딱 거려서 만들어냈습니다. 딴짓(미투데이?) 안 하고 집중하니 정말 1시간도 안 걸리더군요. 로컬에서 만들어 나가면서 테스트 할 때에는 한글이 깨져 나오길래 로컬 LANG이 UTF-8이라 깨지겠거니 했습니다만 웬걸 서버에 직접 올리고 돌려봐도 한글은 깨지는 겁니다.

먼저 가장 의심가는 것은 오라클 디비 커넥션의 인코딩 문제였습니다. 구글을 열심히 돌아다녀 보니 오라클의 LANG 환경 변수를 셋팅하라는 조언이 많이 보이네요. 그래서 데이타를 담는 AR 모델에 오라클 환경 변수를 설정해넣었습니다.

ENV[‘NLS_LANG’]=’Korean’

하지만 계속 깨집니다. 분명히 오라클 연결의 문제일거야 라는 생각에 빌더를 빼버리고 파일에 syswrite로 한줄 한줄 써넣어봤습니다. 결과는 안 깨진다입니다.

결국 Builder의 문제로 결론을 짓고 문제를 따라가보기 시작했습니다. xml을 만들때 보통 method_missing을 이용할 것이라는 생각에 grep 으로 Builder 소스를 뒤져봤습니다. method_missing은 lib/builder/xmlbase.rb 에 정의돼 있습니다.

    def method_missing(sym, *args, &block)
      text = nil
      attrs = nil
      sym = "#{sym}:#{args.shift}" if args.first.kind_of?(Symbol)
      args.each do |arg|
        case arg
        when Hash
          attrs ||= {}
          attrs.merge!(arg)
        else
          text ||= ''
          text << arg.to_s
        end
      end
중략

대충 보니 args로 받은 값을 블럭으로 돌면서 해쉬인지 텍스트인지 판단해서 to_s를 이용해서 계속 붙여나가는 듯이 보이네요. 일단 제가 보내는 값이 어디까지 제대로 넘어오는지 확인하기 위해 arg들을 puts로 죽 찍어 보니 안 깨지고 제대로 나오는 겁니다. 그래서 arg의 to_s 메소드를 의심하고 제거해봤습니다. 그랬더니 to_s가 없는 Sting 객체를 제외한 것들이 메소드가 없다고 아우성을 칩니다.

더 아래로 들어가봐야겠습니다. xchar.rb는 Sam Ruby가 만든 xml을 위한 라이브러리입니다. 여기서 볼 수 있습니다. 문제는 String 클래스에 정의한 to_xs 메소드입니다. 문자열을 인코딩에 따라 unpack 하여 캐릭터를 변환하는 메소드 같아 보입니다. 여기서 euc-kr 코드는 rescue에서 처리되는데 제대로 동작하지 xchr이 제대로 동작하지 않습니다. 그래서 그냥 캐릭터를 리턴하도록 n.xchr을 n.chr로 수정했습니다. 이제 잘 나옵니다.

def to_xs
 unpack('U*').map {|n| n.xchr}.join # ASCII, UTF-8
rescue
 unpack('C*').map {|n| n.chr}.join # ISO-8859-1, WIN-1252
end

아무래도 루비 샘이 한글 코드에 잘 알지 못 해 발생하는 문제 같습니다만 아직 제대로 이해하지 못 하니 여러분의 조언 부탁드립니다.

Advertisements