자바스크립트 템플릿 엔진 비교

어떤 템플릿 엔진을 써야할까?

평소에는 레식 사마가 만들었던 micro-template 을 변형해 만든 JSTools의 pre-compile 기능을 주로 써왔었는데,.. 아무래도 일반적이지 않다보니 (물론 난 pre-compile이 좀더 익숙하지만) 대중적으로 어떤 템플릿 엔진들이 있는지 좀 알아봤다. 역시나 나와 같은 고민을 하는 사람이 이미 전세계에 수없이 많고, 그중에서 LinkedIn에서 공유한 자료가 꾀나 괜찮았다. 정리해보자. 템플릿 엔진은 크게 두 가지로 분류할수있다.

1. 자바스크립트 로직을 품는 레식 사마의 micro-template 계열

존레식이 처음 제안했던 마이크로 템플릿은 jQuery-Template 에 포함되면서 발전해봤고,  이제는 다른 엔진에 밀려 deprecated 된다는 얘기가 있어서 최종적으로는 underscore.js 로 귀결되는 듯싶다. 마이크로템플릿의 장점은 learning-curve가 거의 없다. Syntax가 간단하고 직관적이어서 딱 3가지만 알면된다.  다음은 underscroe.js의 문법이다.

  • < % 자바스크립트 코드 %> 
  • < %= 치환될 데이터 변수 %>
  • < %- escaped 데이터 변수 %>

기본적으로 자바스크립트 로직을 템플릿에서 쓸수 있기 때문에 루프라든가 조건문을 자유롭게 쓸수 있다.  단, 템플릿에 자바스크립트 로직이 들어가기 때문에 다소 보기가 어려울수도 있지만 확실히 배우기 쉽다.  

2. 자바스크립트 로직이 없는  logic-less의 mustache 계열

템플릿 문법에 자바스크립트 로직이 없기 때문에 반복문이나 조건문 같은 로직을 템플릿 자체 문법을 가지고 해결한다.  다음은 간단한 dust.js 템플릿 코드다.

{?tags}
  <ul>
    {#tags}
      <li>{.}</li>
    {/tags}
  </ul>
{:else}
  No Tags!
{/tags}

템플릿 엔진을 선택할때 고려해야할 요소

다음은 LinkedIn에서 고민했던 요소들이다.

  1. DRY: how DRY is the templating technology? is there support for code-reuse and partials?
  2. i18n: is there support for translations and multiple languages?
  3. Hot reload: are changes visible immediately or is there a compile/deploy cycle?
  4. Performance: how long does it take to render in the browser and server?
  5. Ramp-up time: how is the learning curve?
  6. Ramped-up productivity: once you’ve ramped-up, how fast can you build things?
  7. Server/client support: can the same template be rendered both client-side and server-side?
  8. Community: is there an active community using this project? Can you google issues?
  9. Library agnostic: are there dependencies on other JS libraries, such as jQuery or Mootools?
  10. Testable: how hard is it to write unit and integration tests?
  11. Debuggable: is it possible to step through the code while it’s running to track down errors?
  12. Editor support: is there an editor with auto-complete, syntax highlighting, error checking, etc?
  13. Maturity: is this a relatively stable project or still experimenting and churning?
  14. Documentation: how is the documentation?
  15. Code documentation: do the templates encourage/require documentation/comments?

위와 같은 요소들을 다 고려하기에 내 프로젝트가 너무나 초라하다? 라고 생각된다면 고민할 필요도 없다. 그냥 underscore.js 를 쓰면되겠다.  위와 같은 많은 고민을 해야하는 경우는 프로젝트의 사이즈가 큰 경우라고 보면 된다. 프로젝트 사이즈가 크지 않을 경우에는 배우기 쉽고 쓰기 쉬운 underscore.js 를 강력 추천한다. 하지만 내가 진행하는 프로젝트가 어느정도 규모가 있고 템플릿을 적극적으로 쓸 의지가 확고하다면, Logic-Less 계열의 템플릿 엔진도 고민해봄직하다. 기본적으로 템플릿 자체에 로직이 없기 때문에 훨씬 더 간결한 템플릿을 만들어 낼 수 있다. 즉, 더 적고 더 효율적인 코드를 생산해 낼수있다는 얘기다. 특히 모바일 플랫폼이라면 사이즈도 적이다!

템플릿 엔진 최적화

엔진마다 최적화 방법이 서로 다르고, 이미 각 엔진마다 Best Practice가 있으므로 문서를 보고 잘 따라하면 된다. 가령, 최적화의 끝판이라고 불리는 precompile 기능도 요즘엔 기본으로 문서에 추가되는 분위기다. 물론 템플릿 자체 최적화도 중요하지만 얼마나 효율적으로 템플릿을 나누고 구성하느냐는 전적으로 개발자 몫이다. 아무리 좋은 도구를 가지고 있다한들 제대로 활용을 못하면 무용지물인것 처럼 템플릿을 어떻게 구성해야하는지는 경험에 기인하는 경우가 많다. 따라서 지금이라도 당장 템플릿 사용 경험을 쌓는것이 좋다!  

References
[1] http://engineering.linkedin.com/frontend/client-side-templating-throwdown-mustache-handlebars-dustjs-and-more [2] http://engineering.linkedin.com/frontend/leaving-jsps-dust-moving-linkedin-dustjs-client-side-templates [3] http://coenraets.org/tutorials/mustache/
[4] http://akdubya.github.io/dustjs/  

유사배열 객체만들기 (making array-like object in JS)

평소에는 jQuery를 잘 쓰지 않는다. 이미 jindo를 이용해 원하는 형태로 얼마든지 개발할수있기 때문이다. 하나의 라이브러리를 딥하게 쓰다보면, 그 라이브러리에 매우 익숙하게 되고 무엇이 문제인지 무엇이 좋은지 잘 모를때가 있다. 그래서 종종 개인 프로젝트에는 jQuery를 일부러라도 찾아서 쓴다.

몇일전 부터 jQuery를 조금 딥하게 보고 있다가 특이한 점을 발견했다.
바로 jQuery() 객체로 인스턴스를 만들면 다음과 같이 배열로 반환한다는 점이다.

> jQuery()
[]

> jQuery('#test')
[<div id=​"test" class=​"this is a class">​OK​</div>​]

하지만 겉모습만 배열이지 실제로는 jQuery의 인스턴스다.

jQuery() instanceof Array  // false
jQuery() instanceof jQuery // true

오~! 신기한데 어떻게 저럴수 있지? 겉모습은 배열이지만 배열 아닌 유사배열!! 자바스크립트에서 내가 아는 유사배열은 두가지다!. 하나는 arguments 객체고 다른 하나는 셀렉터가 반환하는 NodeList다. 유사배열은 배열이 가지고 있는 배열 특유의 메소드들이 없다. 그래서 어떻게 보면 불편할수도 있다.

그런데 jQuery와 같이 인스턴스가 유사배열 형태로 반환이 되면 장점이 있다. 다음과 같이 DOM을 수정하면 바로 디버거 콘솔창에서 변화를 확인해볼수있다는 사실이다!!

> jQuery('<div>').addClass('test')
[<div class=​"test">​</div>​]

확인해본 결과 웹킷계열의 디버거에서만 위와같이 찍히고, 다른 브라우저들은 그냥 객체로 나온다. 그래도 개발은 크롬에서 주로 하기 때문에 위와같이 콘솔로그만 찍혀도 엄청 도움이 된다.

그래서 만들어봤다. 어떻게 저렇게 하면 나올수있을까?
혹시나 싶어 배열을 프로토타입 상속으로 만들어봤다.

var fine = function(){ };
fine.prototype = Array.prototype;
var x = new fine();

x instanceof fine;  // true
x instanceof Array; // true

그런데 배열을 프로토타입 상속으로 받으면 x는 배열의 인스턴스도 된다. 뭐 누구의 인스턴스가 되냐 안되냐가 그렇게 중요한 문제는 아니지만, 여기서 해당 인스턴스가 되면 필요없는 배열의 메소드들이 fine 객체에 들어갈수가 있다는 것이 문제가 된다.

그럼 어떻게 해야하나? 결론부터 말하면 만들고 싶은 객체에 length와 splice 프로퍼티를 넣으면된다.

> var foo = {length:0, splice:function(){}, x:1, y:1};
> console.log(foo);
[splice: function, x: 1, y: 1]

이런 트릭이 있었다니..대박!! 물론 웹킷계열의 디버거에서만 저렇게 나온다. 요거때메 몇일을 궁리하다가 검색으로 찾아냈는데… 사실은 jQuery 코드에도 위와 같은 코드가 숨겨져 있었다!!

// For internal use only.
// Behaves like an Array's method, not like a jQuery method.
push : core_push,
sort : [].sort,
splice : [].splice

꼼꼼한 레식사마~ ㅋ

내가 몰랐던 재밌는 자바스크립트 이야기

몇달전 자바스크립트 가든이라는 문서의 한글 번역본을 읽다가 포기하고 다시 원문을 보면서 재번역을 했다. 처음엔 그냥 오역이 몇가지 보여서 오역만 바로 잡아볼까하고 원번역자에게 메일을 보냈는데.. 결국엔 전체를 모두 다시 번역했다. 현재는 원저자에게 Pull Request를 보낸상태고 머지는 언제 될찌 모르겠다. 현재 상황에서 기억 남는 몇가지를 남겨보면,

1. 호이스팅(Hoisting)

이게 말이지 참 재밌다.  JSLint에서 이렇게 해라~ 라고 추천했던 가이드들이 몇가지 있었는데.. 예를 들면,

“var문은 맨위로 올려라” 하는 가이드가 있다. 왜 위로 올리라는 거지? 성능 문젠가? 스코핑 때문에 그런가? 그래.. 올리라면 올리지모.. 라고 하면서 var로 선언한 코드를 전부 위로 올렸던 기억이 있는데.. 여기엔 이유가 있었다. 바로 호이스팅!!! 호이스팅은 자바스크립트가 실행되기 전에 선언문을 스코프 위로 올리는 걸 얘기한다. 따라서 다음과 같이 for 문안에 선언한 i 변수는

for(var i=0; i<1000; ++i){
    // 어쩌구 저쩌구
}

  다음과 같은 스코프 상단으로 옮겨진다는 사실!!

var i=0;
for(i=0; i<1000; ++i){
    // 어쩌구 저쩌구
}

이렇게 자동으로 옮겨지는 것을 호이스팅이라고 한다. 이런 특징을 모르면 전역변수를 의도하고 쓰더라도 실제 지역변수에 있는 값을  쓰게 될지도 모른다!! 자세한 내용은 자바스크립트 가든의

함수 스코프를 참고하자!

2. 왜 new Array()는 안되는가?

이것도 역시 JSLint에서 가이드하는 내용중에 하나다. new Array()로 쓰지말고 [] 각괄호를 이용해 배열을 선언하라고 하는데, 이유는 new Array(3) 으로 넣을때 우리가 생각했던대로 동작하지 않기 때문이다. 보통은 배열에 3이라는 값을 하나 추가한 결과를 반환해줄꺼라 기대하지만, 요건 배열의 크기가 3인 빈배열을 반환한다. 심지어 인덱스는 초기화도 안한다는 사실!! 역시 자세한 내용은 자바스크립트 가든의

배열 생성자를 참고하자.

3. 계륵 같은 new 키워드의 비밀

함수를 만들때 new 키워드를 붙여서 생성하지만, new 키워드가 없어도 생성할수있다.

function Foo(){
    this.value = 1;
}
new Foo();
Foo();

위와 같이 호출하면 new 키워드의 유무에 따라 반환 값이 달라진다. new 키워드를 붙이면 Foo 함수가 생성자로써 동작하게 되면서 특별한 반환값이 없으면 this가 가리키는 값을 반환해준다. 즉, Foo 객체의 인스턴스를 반환해주는데,. Foo()는 그냥 함수를 실행한다. 그래서 this 객체는 Foo를 가리키는게 아니라 전역을 가리키게 되고, 반환하는 값이 없으므로 결과값은

undefined 가 된다. 뭐 여기까지는 누구나 아는 사실인데.. Foo 객체가 생성자로 동작할때 즉, new 키워드가 붙을때 , 이 생성자에 명시적인 return 값이 있다면 또 얘기가 달라진다. 명시적인 객체를 리턴하면 그값이 결과값으로 반환되지만, return 값이 없거나 참조타입이 아닌 프리미티브 타입을 반환하면 this가 가리키는 값을 반환한다. 반면에 new 키워드가 없는 경우, Foo 객체는 생성자가 아닌 일반 함수로 동작하면서 return 문의 값을 반환한다. 그래서 결론은 명시적인 반환값을 객체로 주면, new 키워드가 있든 말든 동일하게 동작한다는 사실!! 그래서 팩토리 패턴으로 만들 경우 자연히 new 키워드가 필요없게 된다! 자세한 내용은 역시 자바스크립트 가든의 함수 생성자를 참고하자!

4. 타이머는 스택킹 된다!

자바스크립트에서 타이머는 setTimeout과 setInterval 두가지가 있다. 어찌 생각해보면 setIntveral을 한번만 실행하고 없애면 setTimeout 은 필요없는거 아닌가? 하는 생각을 종종했는데.. 역시나 setTimeout은 ECMA 표준 스펙이 아니었다! 헉! 그랬꾸나.. 그래서 setTimeout의 첫번째 인자에 대한 해석도 엔진마다 다르다는 사실!… 여튼 이건 뭐 그렇게 중요한 얘기는 아니고.. 진짜 재밌는 얘기는 바로 setInterval 함수가 스택킹된다는 사실이다! setInterval 함수는 일정 주기마다 함수가 호출되는데.. 엄밀히 말하면 일정주기마다 함수를 실행하는 것은 아니고 일정 주기마다 실행할 함수를 쌓는다. 그래서 자바스크립트 쓰레드가 그 함수를 실행할때가 되면 실행하게 될테고, 뒤이어 실행되어질 함수는 앞에 함수가 다 끝날때까지 기다리게 된다. 이거다 싱글 쓰레드라서 발생하는 문젠데.. 그럼 한번 생각해보자!. 주기가 엄청 짧은데.. 실행되는 함수는 그 주기보다 길경우 어떻게 될까? 그렇다! 무조건 쌓이게 된다!! 이렇게 쌓인 함수는 시간이란 변수가 더이상 필요없게 된다.  즉, 내가 원하는 주기가 깨질수도 있다는 사실!! 자세한 내용은

타이머를 참고하자!

 5. 삼중 등호의 비밀

이중 등호(==)와 삼중 등호(===) 지금까지는 삼중 등호는 인스턴스를 비교해서 보다 정확하고, 이중 등호는 대충 (?) 비교해서 부정확하다라고 생각하고 있었다.. 하지만 이 둘의 차이는 그런게 아니었다.!!  바로 자동 타입 변환을 하느냐 마느냐의 차이!! 이중 등호는 자동 타입 변환을 해서 끝까지 비교하지만, 삼중 등호는 타입 변환 없이 바로 비교한다. 그래서 훨씬더 빠르다! 자세한 내용은

타입 비교를 봐라! 이 외에도 재밌는 내용이 참 많은데.. 여튼 난 위 5가지가 가장 새로웠다!