Outsider's Dev Story

Stay Hungry. Stay Foolish. Don't Be Satisfied.
RetroTech 팟캐스트 44BITS 팟캐스트

Template사용시 페이지 별로 Javascript 초기화 코드 다르게 하기

보통 웹사이트를 구축하면 헤더나 푸터등 공통된 부분이 있기 때문에 이런 부분은 별도로 만들어서 공통으로 사용하게 한다. 각페이지에 헤더,푸터파일을 인클루드해서 사용할 수도 있지만 그렇게 되도 반복코드가 많이 발생하니까 일반적으로는(그냥 내 생각에.. ㅡ..ㅡ) 템플릿 파일을 만들고 contents부분만 바꿔가면서 사용하지 않을까 싶다.(일단 난 그렇게 한다 ㅡ..ㅡ 전에 올렸던 포스팅처럼...)

이렇게 할때 가장 큰 문제가 자바스크립트 초기화 코드이다. 항상 바꿔치기할 contents부분은 <body>의 정중앙에 들어있는데 보통 스크립트는 <head>안에 있거나 <body>맨아래 있단 말이지. contents부분에 들어갈 페이지에 스크립트코드를 같이 써줘도 되기는 하지만 완성된 html에서 중간에 스크립트 코드가 들어가기 때문에 별로 좋은 방법은 아니라고 본다.

여기서 초기화코드라는 것은 머 여러가지가 될수 있는데 쉽게 페이지 로딩후(onload)에 자동으로 수행할 함수를 말한다. 가장 많이 쓰는데 이벤트 핸들러 등록이라던가 하는거고 onload후에 focus를 어디에 둔다던지 하는등의 코드를 말한다. 페이지가 다 다르게 생겼으므로 이런 코드도 다 다를 수 밖에 없는데 contents에 들어갈 페이지에서는 이걸 다룰수가 없다는게 고민거리였다. 물론 if-else문으로 해당 엘리먼트가 있는지 라든가 페이지 주소를 이용해서 억지로 할수야 이겠지만 쓸데없이 검사해야 하기 때문에 성능도 떨어지고 별로 알흠답지 못한 방법같았다. 이벤트 핸들러의 경우 html 엘리먼트에 직접 써넣는게 일반적이지만(<input type="button" onclick="test();"> 이렇게...) 요즘 추세는 자바스크립트도 완전히 분리하는 것이기 때문에 이걸 분리하자면 초기화코드가 꽤 많아진다. (이게 Unobtrusive Javascript.... 이렇게 하면 디자인 바뀌어도 많이 손 안대도 된다. 물론 Unobtrusive Javascript가 말하고자 하는건 저게 중점은 아니지만 개인적으로는 큰범주에서 보면 같은 흐름이라고 생각한다.)



어쨌든 이번에 플젝하면서는 이걸 좀 해결해 보고 싶었기 때문에 OKJSP에 질문글을 올렸더니 임은천님이 답변을 해 주셨다.(거의 익명사이트긴 하지만 도움얻은걸 혼자한척 하진 않는다.. ㅡ..ㅡ) 딱 내가 기다리던 명쾌한 답변.... 너무 자세하게 답변을 주셔서 구현하는데 별로 어렵지 않았다.


document.observe('dom:loaded', function() {
    <% if (request.getAttribute("jsDispatcher") != null && !request.getAttribute("jsDispatcher").equals("")) { %>
    var dispatcher = new Dispatcher();
    var jdName = "<%= request.getAttribute("jsDispatcher") %>";
    eval(dispatcher.get(jdName));
    <% } %>
});

위의 코드를 Template의 부모(?)가 되는 페이지의 스크립트 부분에 넣어준다. 물론 저 코드는 Prototype Framework의 코드이다. 쉽게 가자면 window.onload에다가 이벤트를 건거다. 여기선 JSP코드니까 JSP로 jsDispatcher이 파라미터로 넘어왔는지를 검사하고 jsDispatcher값이 있으면 디스패쳐를 실행한다. 물론 템플릿에 어떤 페이지를 contents부분에 띄워줄지 파라미터로 넘겨줄때 jsDispatcher의 값도 같이 넘겨주어야 한다. 어차피 템플릿으로 구성되는게 구현되어 있을테니까 파라미터 하나 더 넘겨주는건 그리 어렵지 않은 일이다.

디스패쳐가 각 jdName별로 실행할 함수목록을 가지고 있고 파라미터로 넘겨받은 jsDispatcher의 이름으로 디스패쳐에다가 조회해서(get) 해당 실행할 함수들을 가져오고 그자리에서 eval()해서 실행해 버리는거다. 이것 자체가 window.onload때 실행되니까(Prototype.js의 dom:loaded는 약간은 다른의미지만 여기선 별로 중요하지 않다.) 디스패쳐로 가져온 값도 페이지 초기화코드로 실행된다. 물론 여기서 Dispatcher은 이어서 만들 클래스이다. 원래 제공되는게 아니라...



그럼 Dispatcher클래스를 보자.


var Dispatcher = Class.create();
Dispatcher.prototype = {
    initialize : function() {},
    get : function(name) {
        return this.map[name];
    },
    map : { listform: 'ListenerCollection.registEventListFrom()',
                  writeform: 'ListenerCollection.registEventWriteFrom()',
                  modifyform: 'ListenerCollection.registEventModifyForm()'
    }
}

var ListenerCollection = {
    registEventListFrom: function() {
        // 리스트 페이지의 초기화 코드들
    },
    registEventWriteFrom: function() {
        // 작성 페이지의 초기화 코드들
    },
    registEventModifyForm: function() {
        // 수정 페이지의 초기화 코드들
    }
}

여기에는 Dispatcher와 ListenerCollection 2가지 클래스가 있다.(Prototype.js의 관점에서는 Dispatcher만 클래스이지만 그냥 넘어가자.)

Dispatcher클래스는 3가지 매서드가 있다.  initialize는 보는바와같이 아무것도 없는데 prototype.js의 클래스에선 필수로 만들어주어야 하는거라서 신경안써두 되고 get하고 map이사용할 메서드이다. map은 넘겨받은 이름과 함수가 쌍으로 맵핑되어 있고 get에서 넘겨받은 name으로 map에서 찾아서 해당 함수를 돌려준다.

ListenerCollection은 등록할 이벤트리스너(꼭 이벤트리스너일 필욘 없지만)의 코드목록을 담는 곳이다. map내애서 funcion(){}으로 바로 적어줄 수도 있지만 코드량이 많아서 분리했다. 약간 꼬아놔서 그렇지 구조는 별로 어렵지 않다.



이렇게 하면 템플릿을 유지하면서도 페이지별로 초기화 자바스크립트 코드를 따로 둘 수 있다. 모든 페이지에서 이코드가 들어가긴 하지만 위쪽에서 onload로 이벤트를 건 곳을 제외하고는 외부 js파일로 빼면 어차피 캐시가 되기 때문에 성능면에서는 별로 신경안써도 될듯하다. prototype.js를 기반으로 작성되서 prototype.js를 안써본 사람은 약간 코드이해에 어려움이 있을까 좀 걱정되긴 한다...
2008/12/26 23:44 2008/12/26 23:44