IE에서 innerHTML 사용시 주의해야할 점

일단, 발행하기 전에 정리부터 하자..
예제는 나중에 정리하고.. 생각나는대로 써보면,..
대충 정리해보면 아래와 같다.

1. DOM 노드 캐싱
DOM을 캐싱할때는,.. 캐싱하려는 노드를 cloneNode(true)로 떠서 캐시하자..
그렇치 않으면, IE에서는 innerHTML로 해당 노드를 날렸을때,. 캐시한 노드까지 날라간다.
반면, IE외의 다른 브라우저에서는 innerHTML로 해당 노드를 날렸을때..
해당 노드만 날라가고, 캐시한 노드는 그대로 있다.
[하나 더 주의] – cloneNode를 할 경우, 바인딩된 이벤트는 복사되지 않는다.!

2. 메모리 누수
모든 브라우저가 innerHTML로 엘리먼트를 교체하면, 엘리먼트가 걸려있는 이벤트 핸들러들이
날아가 버린다. 보다 정확한 의미는 참조를 잃어버린다!!
때문에 참조를 잃어버린 이벤트 핸들러들은 영영 해제할 길이 없어 메모리 누수가 생긴다.

따라서, 엘리먼트 교체를 해야할 경우엔, 이벤트 핸들러를 먼저 제거해주거나..
앞에서 얘기한 노드를 캐싱하거나..
아니면, HTML로 날릴만한 엘리먼트엔 이벤트 핸들러를 걸지말고, 그 상위 노드에 걸어서
버블링 처리를 해라~!!

3. HTML 렌더링
HTML을 innerHTML로 재할당을 할경우, 브라우저는 재할당한 부분만 랜더링을 다시하게 된다.
이때, 구식브라우저(꼬진브라우저, 느린브라우저)인 IE6에서는 랜더링 할때, 생각보다 속도가 느리다.
그래서 아래와 같이 사용하게 되면,..

node.innerHTML = “<div>….중간생략 복잡한 디비전 태그 교체…</div>”
doSomethingHere();

doSomethingHere() 함수 안에서 새로 랜더링 되는 엘리먼트의 width나 height 등을 가지고 뭔 짓꺼리를 하게 되면,
IE6에서는 htmlfile 에러는 내뱉는다.

이유는 앞서 설명했듯이 랜더링 속도가 느려서 발생하는 문제로…
로드 된후, 실행시 까지 약간의 딜레이를 주거나,
인터벌울 돌려서 렌더링이 됐는지 체크를 한다.

난 걍 랜더링시 문제가 될경우에 한에서만 이렇게 쓴다.

el.innerHTML = sTpl;
var oInstance = this;
setTimeout(function(){
          oInstance._initialize();
}, 100);

4. htmlfile 에러
그밖에 IE6혹은 IE7에서 htmlfile 에러가 발생하는 경우가 있는데 아래와 같은 경우가 있다.

  1. 붙이려는 노드가 읽기 전용 속성의 태그이고, 거기에 ID 값을 부여해서 innerHTML 할 경우
    ** IE에서 COL COLGROUP FRAMESET HTML STYLE TABLE TBODY TFOOT THEAD TITLE TR 개체등은 ID 필드가 읽기전용 속성이고 그 외의 개체에서는 모두 읽기/쓰기가 된다.
  2. 기본 HTML 구조 규칙을 깨는 경우,
    예를 들면, LI 엘리먼트 하위에 다시 LI 노드를 자식으로 넣는 경우가 있겠다.
    이 경우는 실제로 겪었던 부분으로, li 자식 노드로 li를 넣는 바보같은 짓을 범했다. 이런 경우가 흔하지 않는 경우인데, 안타까운건 FF 에서는 li 하위에 li 를 짚어 넣어도 아무런 에러를 발생시키지 않는 다는 것이다. 


5. FireBug를 믿지마라!

보통 FireFox의 플러그인 인 firebug 를 이용해서 디버깅을 하게되는데, FF 는 너무나 관대(?)해서,
IE에서 뱉는 에러를 몇몇가지를 그냥 쌩까고 혹은 아~ 이건 개발자 실수니까,..스마트(?) 하게 알아서 정정해준다.
즉, FF만 믿고 개발했다가는 IE 호환성에 적신호를 받게 될것이다.
그렇다고, firebug 가 구리다는건 아니고, 간간히 중간중간 때때로, 생각날때, IE 브라우저로 테스트를 해보는 습관을 갖는게 좋겠다.

추천하는 바는 그냥  IE6 브라우저를 버리고 갔으면 한다. (그냥 개인적인 바램~ OTL… ㅜㅜ )
하지만 현실은 시궁창… ㅎㅎ

Spring Framework 에서 UTF-8 한글 설정

인코딩 문제로 오늘 또 하루종일 씨름했다.

사실 그닥 어려운게 아닌데.. 처음 설정을 잘못해놓으면,.
고생하기 쉽상이다.
웹개발에서 인코딩과 관련해서 신경써야할 부분
아래와 같은 3가지라고 보면 되겠다.
1. 클라이언트 (javascript)
2. 서버 (apache or tomcat 설정)
3. DB
1. DB 캐릭터셋 설정하기
그중에서 가장 실수하기 쉬운 것중에 하나는 DB 설정.
mysql 을 설치하고, 꼭 아래와 같은 명령어로 현재 mysql 의 설정 상태를 확인해야한다.

mysql> \s

mysql 을 설치할대, 기본 캐릭터셋이 latin1 으로 설정되어 있기때문에
신경써서 설치하지 않으면, 원하는 utf8 설정이 안된다.
설정이 제대로 안되어 있다면, 설치 폴더로 가서 my.ini 파일을 열어
인코딩 관련 부분을 모두 수정해줘야한다.
여기서 그렇게 했는데도 불구하고, \s 명령어를 이용해, mysql 설정상태를 확인 했을때,
DB character set 이 latin1 로 나오는 경우가 있는데..
이런 경우는, create database로 db를 생성할 당시의 캐릭터 셋이 latin1이 었기 때문에
뒤늦게 수정해봐야 수정되지 않는다. 결국엔 db를 날리고 다시 생성해야한다.
이런 경우 때문에 초기 설치시에 주의해서 원하는 캐릭터셋으로 설정을 해야한다.
2. 스프링 프레임웍에서 설정하기

두번째로 서버 설정을 확인해야하는데.. tomcat에서 server.xml 을 열어서 8080포트와 8009번 포트의
커넥터에 URIEncoding=“UTF-8” 를 추가해준다.
스프링 프레임웍을 이용할경우, web.xml 열어서 아래와 같은 필터를 추가해준다.

<filter>

<filter-name>encodingFilter</filter-name>

<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>

<init-param>

<param-name>encoding</param-name>

<param-value>UTF-8</param-value>

</init-param>

</filter>

<filter-mapping>

<filter-name>encodingFilter</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>

그리고 xxxx-servlet.xml 파일을 열어서, viewResolver를 아래와 같이 또 수정해준다.

<bean id=“viewResolver” class=“org.springframework.web.servlet.view.InternalResourceViewResolver”>

<property name=“prefix” value=“/WEB-INF/view/”/>

<property name=“suffix” value=“.jsp” />

<property name=“contentType” value=“text/html; charset=UTF-8”/>

</bean>

이렇게 해주면, 기본적으로 클라이언트딴에서 특별한 인코딩을 수행하지 않아도 UTF8로 인식을 한다.
때문에 응답할때도 특별한 디코딩을 해주지 않아도 된다.


3. 서버 설정없이 클라이언트에서 무조건 UTF-8로 넘겨주기

위와 같이 서버딴에서 특별한 설정을 해주지 않았다면,
다소 귀찮은 작업을 해줘야하는데..
그것은 클라이언트에서 서버로 정보를 날릴때, 무조건 인코딩을 해서 넘겨야 한다는 것이다.
인코딩하는 방법은 간단하다.
아래와 같이 자바스크립트를 이용해, 넘기고자 하는 정보를 encodeURIComponent()
함수를 이용해 인코딩 해주면, 넘기는 정보를 UTF-8로 인코딩해서 넘기게 된다.
4. 인코딩 파헤치기

보통은 클라이언트에서 서버로 요청 파라메터 정보를 넘기게 되면,
아래와 같은 3가지 인코딩 방식으로 넘기게 된다. 아래 1번을 제외한 나머지 방법은
브라우저가 설정된 인코딩 방식을 따르게 된다. 1번방법은 자바스크립트에서 강제로 인코딩한다.
예를 들어 “한글”이란 문자열을 넘기면,
1. encodeURIComponent() 로 인코딩 할때, “%25ED%2595%259C%25EA%25B8%2580
2. UTF8 인코딩을 할때,  “%ED%95%9C%EA%B8%80
3. EUC-KR로 넘길 때, “%C7%D1%B1%DB

위 3가지 타입을 UTF8로 설정된 서블릿에서 각각 request.getParameter() 로 넘겨받은 정보를 읽어오게 되면,
UTF8로 디코딩 되어 각각  
1. %ED%95%9C%EA%B8%80
2. “한글
3. “???”
로 읽혀오게 된다.
여기서 문제는 3번이 문제가 된다. 3번은 실제로 “한글”이란 정보를 EUC-KR로 인코딩해서 넘기게 되는데..
서블릿에서 UTF-8로 디코딩을 하게 되어 알수없는 값이 되어버린다.

그리고 서버에서 이것을 다시 UTF8 캐릭터셋으로 설정된 DB에 그대로 저장하면,. 각각
1. %ED%95%9C%EA%B8%80
2. “?쒓?”
3. “ㅁ싼깍옙”
로 다시 저장된다.
1번을 제외한 2,3번 방식으로 DB에 저장이 되면, 본래 기대했던 값들과 다른 값들로 저장이 되는데..
DB 정보를 서블릿에서 다시 읽어볼때, 같은 UTF8로 디코딩을 하게 되면 사실 큰문제 없다.
하지만 위 DB정보를 다른 캐릭터셋으로 설정된 서블릿에서 읽어오게 되면 이식성에 문제가 생긴다.
또한 DB 캐릭터 셋이 UTF8이 아닌 다른 캐릭터셋으로 설정이 되었다면, DB에 저장될때 마찬가지로
문제가 생길수있다.

때문에 UTF8로 다국어를 지원해야하는 특별한 상황이라면, 1번 방법을 사용하는것이 보다 안전하다.
하지만 보통 하나의 캐릭터셋만을 지원하거나 통일하기 때문에 2번 방식으로 대부분 커버가 가능하다.

사실 더 중요한 것은, 인코딩과 디코딩할때,
클라이언트(브라우저인코딩) – 서버 – DB
3박자를 모두 잘 맞춰서 해줘야 한다는 것이다.

이상 정리 끝~

BDLayer 새버전 릴리즈!

우리팀에서 나름 유용하게 사용했던 BDLayer (내이름 이니셜을 붙였다..ㅋㅋㅋ)

작년말에 저거 만들때, 나름 설계에 고민을 많이 했는데..
구조상의 결함과 고치기 힘든 버그때문에..
결국 오늘 새버전을 만들어 버렸다. 
나름 그동안 구상해왔던, 설계로 작성했고,..
“최대한 불필요한 함수들은 노출하지 말자” 라는 기본 원칙을 세웠는데..
나름 잘 지켜진것 같다. 코드가 아주 심플해졌네~ ^^  
그리고 아직 크로스 브라우징 테스트는 하지 못했다.
(Mac버전 FF3, Safari, Opera 만 테스트 했음)
이번 코드는 시작부터 JSSpec을 적극 활용해서 진행했다.
역시나 서비스코드보다는 훨씬 적용하기가 수월했다.
 
AutoHide 플러그인에 잡기 힘든 버그도 잡아서 속이 다 시원하다. 
역시나, 타이머는 디버깅이 힘들다..ㅇㅎㅎ
만들면서 느낀 것,.. 
역시 자바스크립트는 프레임웍(프로토타입,제이쿼리,진도 등등)을 이용해 클래스를 흉내내는것보다. 
그냥 자바스크립트 본연의 모습이 보다 깔끔한 것 같다. 
향후 계획은 
1. 일단 크로스 브라우징 테스트부터 하고,
2. 모달 플러그인까지만 추가할 계획.
3. 그리고 SSR 프로젝트에 적용할 예정!
이번 연휴 계획 3개중 벌써 한개를 해치웠군..
이제 남은건 Processing.js 와 Spring 만 남은건가?
내일은 어린이날이니까, 로스트 보면서 하루 쉬어야징.. 

어느날 갑자기 피들러(Fiddler)가 느려졌을때, 해결방법

웹개발자나 UI 개발자에게 없어서는 알될 툴중에 하나가 바로,..
HTTP 모니터링툴일것이다.

대표적으로 HttpWatchFiddler 라는 Http Debugging Tool 이 있다.

두개 모두 내가 유용하게 쓰고 있는 툴인데,..
그중에서 피들러가 어느날 갑자기 너무 느려졌다..
피들러를 열고, 웹페이지 하나 열라치면,.. 몇 십초나 걸린다.--;..

도대체 뭐가 문제인거야? 한참을 고민하다.. 에라이~ 모르겠다..
접어두고, 그냥 불편한대로 다른 방법을 이용했는데..
그런데, 이젠 더이상 안되겠다 싶어…
Fiddler 관련 메일링 리스트를 뒤졌다..

역시 찾으니까 금방 나온다..--;..
인터넷옵션 – 연결 – LAN 설정에서 자동 구성의 체크박스를 모두 해제해두면 그걸로 끝!

다시 피들러가 제속도를 찾을것이다.
사용자 삽입 이미지

그래도 안된다고?.. 그럼 나도몰라!! ㅋㅋ

Httpwatcher에서 이미지 요청시 Aborted 되는 현상

항상 이런 이슈는 퇴근쯔음 생긴다..-_- 싫다 정말..
여튼, 개편이후, PV가 갑자기 줄어서.. 확인해본결과..
문제는 이미지 태그의 src 이용해 리퀘스트를 날릴때, 그 리퀘스트가 중지되는 현상이 발견됐다.
Httpwatcher에서 확인해본결과.. 
아래와 같은 코드 사용시에..
(new Image()).src = “로그집계기록” 
열에 2~3번은 aborted가 되고 있었다. 
위와 같은 상황이라면 PV가 대략 20~30%는 하락할것이다..ㅎㅎ
여튼 수많은 삽질을 통해 대강의 원인을 찾아본결과..
아래와 같은 이유가 있었다.  (이유는 3가진데.. 자세한건 아래 링크 참조)

http://blog.httpwatch.com/2008/01/28/what-does-aborted-mean-in-httpwatch/

그중에서 다운로드 중일때, 리퀘스트를 날리면 요청을 끊어버린다는 것!!
이게 가장 유력한 원인으로 파악되고 있다. 
일단.. 내일 다시 테스트해봐야징.. 졸립넹..-_-
—–
최종정리~
위에서 발생한 문제는 최종적으로 가비지 콜렉션 문제로 정리됐다. 
즉, 위와 같이 (new Image()).src = “로그집계 기록”을 호출할경우..
요청에 대한 응답을 받기도 전에 이미지 객체가 가비지 콜렉션 되어 사라질수 있다. 
이럴경우, 응답은 취소가 된다. 
해결책은 간단하다. 가비지 콜렉션 되지 않도록 이미지의 참조 카운터를 누군가가 들고 있으면 된다. 아래와 같이 전역변수로 가지고 있어도 되고,
_gImg = [];
function 로그집계 = {
    var o = new Image(); 
    o.src = “로그집계”;
    _gImg.push(o);
}
로그집계();
혹은 아래와 같이 abort 이벤트에 대해서 이벤트를 바인딩 해둬도 된다 
function 로그집계 = {
    var o = new Image(); 
    o.src = “로그집계”;
    o.abort=function(){};
}
로그집계();
후자보다는 전자가 보다 명확한 방법이다. 
후자의 경우의 코드상의 의미는 abort 이벤트가 발생했을때, 어떠한 처리를 따로 해주는것인데..
저렇게 아무런 의미없는 함수를 바인딩해둘필요는 없지 않을까?
그래서 전자로 처리했다.