블로그 내 검색

2011. 9. 20.

자바 new String() 시 초보들이 하기 쉬운 실수...


캐릭터셋 변환에 대해 인터넷 블로그 등에 잘못 떠돌고 있는 괴담(?) 은아니고 괴코드(?) 가 있다.

// 예상과는 다른 동작을 하는 코드
String convert = new String(message.getBytes("euc-kr"), "utf-8");


이건 잘못된 API의 이해가 부른 오동작 코드 이다.

String::getBytes 는 자바 내부에 관리되는 유니코드 문자열을 인자로 지정된 캐릭터셋의 바이트 배열로 반환하는 메서드이며,
new String(바이트배열, 캐릭터셋) 생성자는 해당 바이트 배열을 주어진 캐릭터 셋으로 간주 하여 스트링을 만드는 생성자이다.

다음 예제를 보자

String d = "안녕 親9"; // 자바는 내부 문자열을 모두 유니코드 처리한다
  
// 유니코드 문자열을 UTF-8 캐릭터 바이트배열로 변환하여 반환
byte [] utf8 = d.getBytes("UTF-8");

// 유니코드 문자열을 EUC-KR 캐릭터 바이트배열로 변환하여 반환
byte [] euckr = d.getBytes("EUC-KR");
  
// 당연히 다른 바이트 배열이므로 사이즈가 다르다.
System.out.println("byte length > " + utf8.length); // byte length > 11
System.out.println("byte length > " + euckr.length); // byte length > 8
  
// 실수 코드.
// UTF-8 캐릭터셋으로 배열된 바이트배열을 EUC-KR로 해석할 수 없다.
System.out.println(new String(utf8, "EUC-KR"));

절대 캐릭터 변환이랍시고 new String(바이트배열, 변환하고싶은 희망사항 캐릭터셋) 을 쓰는 오류는 범하지 말자.

자바 내부에서 처리하는 문자열은 일괄적으로 같은 유니코드 형식으로 처리되며,
이기종 전송 등 필요한 경우에는 getBytes()로 해당 문자열 바이트 배열로 변환 뒤 전송하면 그만일 것이다.

다만 예전 구형 웹서버등을 사용한 프로젝트의 경우의 문자열을 원래 캐릭터로 복구하는 코드가 위의 new String 을 쓰는 경우가 있는데,
이건 웹 서버에서 캐릭터셋을 잘못 해석하여 주는 것을 바로잡는 코드이거나, 비슷한 캐릭터 코드에서 코드로 해석한 것이며, 캐릭터 셋 변환이 아님을 알아두자.


좋은 참고 글 하나 링크한다.

Java Character Set의 이해


2011. 9. 17.

Javascript 이벤트 바인딩 별 차이

DOM Element에 이벤트를 바인딩하는 방법은 몇가지 방법이 있다

인라인(In-line) 방식
함수 레퍼런스 방식
addEventListener 방식
attachEvent 방식

이 넷은 일견 다 비슷해 보인다.
하지만 함부로 위 방법을 혼용해서 썼다간 원치 않는 이벤트 동작이 일어날 수도 있다.
특히 함수 내에 thisarguments 등의 늦은 바인딩 변수를 썼을 경우에는 문제가 심각해진다.

예제를 통해 이들의 차이를 알아보자
일단 함수를 하나 선언하는데 이 함수의 내용은 자신의 활성 객체(Activation Object) 아이디를 알려주는 기능을 한다.
그리고 객체 다섯개를 선언하고 그것마다 다른 방식의 이벤트 바인딩을 한 뒤 호출해본다.

 
// 테스트를 위해 윈도우의 프로퍼티에 아이디를 추가해둔다
window.id = "window";

// 자신의 컨텍스트 아이디를 보여줄 바인딩 함수
function clickObj() {
    alert(this.id);
}

window.onload = function() {
    
    // 이벤트를 지정할 참조 엘리먼트를 얻는다.
    var ele = document.getElementById("reference");

    // 함수 레퍼런스 방식. 함수의 참조를 그대로 이벤트에 복사
    ele.onclick = clickObj;
    
    // 이벤트 익명 함수 안에서 함수 실행
    ele = document.getElementById("referenceInner");
    ele.onclick = function(){
        clickObj();
    };
    
    // IE 전용의 이벤트 추가 메서드 이용
    if(ele.attachEvent) {
        ele = document.getElementById("attachEvent");
        ele.attachEvent("onclick", clickObj);
    }
    
    // 표준 이벤트 리스너 등록
    else if(ele.addEventListener) {
        ele = document.getElementById("addEventListener");
        ele.addEventListener("click", clickObj, false);
    }
};


예제의 결과를 보면

inline : window
attachEvent : IE전용, window
addEventListener : 표준 브라우저. addEventListener
reference : reference
referenceInner : window 

다음의 결과를 얻을 수 있다.

인라인 방식의 this는 글로벌
attachEvent의 this는 글로벌
addEventListener의 this는 
Activation Object
reference방식의 this는 
Activation Object 
reference지만 익명 함수 안의 this는 글로벌

인라인 방식은 함수의 활성객체가 글로벌(window) 객체이다.

하지만 함수 레퍼런스 방식과 addEventListener 방식은 바인딩 된 객체임을 알 수 있다.
레퍼런스 방식에서도, 레퍼런스 함수에서 다른 함수를 호출할 경우 그 함수의  활성 객체는 글로벌(window) 객체가 된다.

재미있는 점은 표준을 싫어하는(?) IE의 경우이다.
IE에서 지원하는 이벤트 바인딩 메서드는 활성 객체가 글로벌 객체로 지정되어 버린다.
캡처링도 지원 안하는 메서드가 무슨 깡으로 이런 오류까지 내는지 알 길이 없다(?)

이들의 차이를 잘 알지 못한채 함부로 스크립트를 만들다 보면 나중에 잡기 힘든 오류와 정신적 탭탠스를 추며 퇴근시간이 늦어지는 불상사가 있을지도 모르겠다.