본문 바로가기

node.js (OctoberSkyJs)

[node.js 따라배우기 04] 템플릿, 파셜, 그리고 문서 생성과 수정


안녕하세요? 따라배우기 네 번째 입니다. 이번에는 번역이 조금 힘들었습니다. 몇 가지 이유가 있는데, 예전 원본글이 쓰였을때와는 버전들이 조금씩 어긋나는데 그걸 맞추는 작업을 해야 했고, 저 혼자만의 공부가 되지 않기 위해서 관련 자료들도 함께 추가해야 했기 때문입니다. 그리고 보기도 편하고, 자료로서의 가치도 함께 유지하기 위해 중간 중간 원문을 크게 해치지 않는 범위내에서 내용을 좀 추가하였습니다. 슬슬 창번(=창작번역)으로 가기 시작한걸까요? :D

아마 이 글 보다 나중에 등록될 예정이지만, 여유가 되신다면 다음 글을 먼저 보시고 진행하시면 이해하기엔 더 좋습니다.

Jade - 템플릿 엔진 for Node.js [메뉴얼 번역]
http://blog.doortts.com/223

뭔 따라 해 보다가 모르겠다 싶을 때 보셔도 큰 상관은 없습니다.

그리고 이번부터는 본격적으로 코드 전체가 나오지 않기 시작합니다. 전체 소스코드를 중간 중간 살펴볼 필요가 있습니다. 아래 주소를 적극! 이용하세요!

nodepad 최종소스코드
https://github.com/alexyoung/nodepad

자, 그럼 이번에도 화이팅! 입니다.

원문 페이지 :
http://dailyjs.com/2010/11/22/node-tutorial-4/

 "Webapp
을 만들어 봅시다! Nodepad" 파트4에 오신걸 환영합니다. 본 시리즈는 "Node.js"를 이용해서 웹 애플리케이션(이하 웹앱)을 만들어보는 따라배우기(tutorial)시리즈입니다. node.js를 이용해서 웹앱을 만드는 과정을 따라가면서, 자신만의 애플리케이션을 만들 때 접하게 될 모든 영역을 다룰 예정입니다.

이전 내용

Part 1: 소개 (Introduction)

Part 2: 설치와 애플리케이션 뼈대 만들기 (Installation and Skeleton App)

Part 3: RESTful 메소드와 테스트 (RESTful Methods and Testing)


이번 파트에서는 추가할 내용이 많습니다
. 그러니까 우선은 일어나서 차을 한잔 먼저 타오시면 좋을 것 같습니다. 튜터리얼의 끝에 (혹은 git으로부터 코드를 체크아웃 해온다면 지금 바로) 아래와 같은 모습의 앱을 보게 될 겁니다.



이 튜터리얼안에서 모든 코드를 다루진 않습니다. 몇 몇 코드 예제들은 축약해서 담았고, CSS는 제외했습니다. git 저장소에 들어 있으니까 필요하다면 다운로드 받아서 에디터로 열어볼 수 있습니다. 
 
엑스프레소 업데이트 하기 (Updating Expresso)

엑스프레소는 계속 업데이트 되고 있습니다. 현재 최신버전은 0.9.0 입니다.

(옮긴이 주)
관련해서는 한글로 매뉴얼을 번역해 놓았으니 참고하세요. :)
[Node.js 따라배우기] Node.js TDD 프레임워크 Expresso 매뉴얼 번역 (http://blog.doortts.com/213)

 

필요하다면 npm update expresso 명령어로 엑스프레소를 업그레이드 하세요.
(옮긴이주. 최근 따라하기 시작하신 분들은 업데이트할 내용이 없을 겁니다)

엑스프레소(expresso)?


엑스프레소는 node.js용 자바스크립트 TDD 프레임워크입니다. 사이트의 설명에 의하면 Expresso는 빠르고 추가적인 테스트 메소드와 코드 커버리지 레포팅, CI지원 등의 기능을 가진다고 합니다. 이하 특징입니다.


- 경량

- 직관적인 비동기 지원

- 직관적인 테스트 실행

- node-jscoverage를 통한 테스트 커버리지 지원과 레포팅

- 코어 assert 모듈을 사용하거나 확장함

- assert.deepEqual()의 축약으로 assert.eql()

- assert.response() http response 유틸리티

- assert.includes()

- assert.isNull()

- assert.isUndefined()

- assert.isNotNull()

- assert.isDefined()

- assert.match()

- assert.length()


 
템플릿 렌더링 (Rendering Templates)

문서 목록을 보여주는 메소드(/documents)는 수정할 수 있는 문서 목록을 렌더링해야 합니다. 그렇게 하기 위해, 적절한 render 호출을 추가합니다.

다음 코드는 app.js 의 일부입니다.

res.render('documents/index.jade', {
  locals: { documents: documents }
});


 위 코드와 함께 쓰는 Jade 템플릿(index.jade)은 다음과 같습니다. 

ul
  - for (var d in documents)
    li= d.title 



제이드(Jade)

Jade를 처음 쓰게 되면 좀 낯설게 느껴집니다만 실제로는 매우 쉽게 요령이 붙습니다. 다음 기억해야 할 키 요소입니다.

-  들여쓰기는 태그 중첩(nesting)을 의미합니다.

-  등호(=) 표시는 변수 포함을 의미합니다.

-  부등호(!=) 표시는 이스케이프(escape)되지 않은 변수를 포함했다는 뜻입니다. 
(옮긴이 주: 이스케이프: <. > 등의 특수문자를 &lt; &gt; 등으로 변환하는 것)

-  하이픈(-)은 자바스크립트 포함을 허용합니다.


일반적으로 가능한 한 많이 이스케이프 하는 것이 좋습니다. 크로스사이트스크립트(XSS) 공격의 가능성을 줄여주기 때문입니다.


파셜 (Partials)

제이드와 expresso는 파셜(=재사용 가능한 작은 템플릿 조각)을 쉽게 만들 수 있게 해줍니다. 다음은 새로운 문서 템플릿입니다. 

views/documents/new.jade

h2 New Document

form(method='post', action='/documents')

  !=partial('documents/fields', { locals: { d: d } })


파셜은 partial(템플릿파일이름, 옵션) 메소드를 호출하면 렌더링됩니다. 결과물은 이스케이프(escape)되지 않습니다. 방금 우리가 봤던 HTML 태그들은 사용자 정의 필드 안에서 이스케이프 될 것이기 때문입니다. 따라서 여전히 안전합니다.


생성/수정 폼(New and Edit Forms)

마음을 흔들만한 어썸~한 Ajax 인터페이스를 만들기 전에, 간단한 템플릿들을 만들어 봅시다. 우리의 REST API는 생성(create) 메소드와 업데이트(update) 메소드를 정의했습니다. 따라서 사용자 인터페이스를 제공하기 위해 이에 대응하는 new와 edit 메소드를 만들어야 합니다.

저는 보통 폼을 세 개의 템플릿으로 쪼갭니다. 하나는 폼 필드들을 담고 있는 재사용 가능한 파셜입니다. 다른 두 개는 new와 edit 템플릿으로, 필드들을 감싸기에 적당한 폼 코드를 가지고 있습니다. 

new 폼은 위에서 썼습니다. Edit 폼은 다음과 같습니다.

views/documents/edit.jade

h2 Edit Document

form(method='post', action='/documents/' + d.id)

  input(name='document[id]', value=d.id, type='hidden')

  input(name='_method', value='PUT', type='hidden')

  !=partial('documents/fields', { locals: { d: d } })

 
new와 동일하지만 숨겨진 input 필드가 있습니다. _method 필드가 POST를 put 라우팅으로 처리할 수 있게 해줍니다. 지난번에 살펴봤던 RESTful API를 따릅니다.

필드에 해당하는 파셜은 간단합니다.

views/partials/documents/fields.jade

div
  label Title:
    input(name='document[title]', value=d.title || '')
div
  label Note:
    textarea(name='document[data]')
      =d.data || ''
div
  input(type='submit', value='Save')


이 시점에서 Jade에 대한 느낌이 와야 합니다. 저는 haml이나 Jade의 팬이 아닙니다만, 보시다시피 이런 예제들은 문법이 꽤 간단하다는 걸 알 수 있을겁니다. 

(옮긴이 주)
그래서 Jade 관련해서는 따로 페이지를 만들었습니다. node와 express를 쓴다면 한 번 배워 볼만합니다.
:)



New와 Edit에 대한 백엔드 메소드 (New & Edit Back-end Methods)

new와 edit에 대한 서버사이드 메소드는 문서를 읽어들여서 폼을 렌더링 하는 겁니다.

app.get('/documents/:id.:format?/edit', function(req, res) {
  Document.findById(req.params.id, function(d) {
    res.render('documents/edit.jade', {
      locals: { d: d }
    });
  });
});

app.get('/documents/new', function(req, res) {
  res.render('documents/new.jade', {
    locals: { d: new Document() }
  });
});


new 메소드는 폼 템플릿을 유지하기 위한 빈 문서를 만듭니다.


몽고 IDs (Mongo IDs)

템플릿이 d.id를 참조한다는 걸 눈치채셨나요? 몽구스(Mongoose)는 기본적으로 ObjectID 데이터타입을 갖는 _id 필드를 만듭니다. 제가 보기엔 별로 좋은 것 같진 않아서, getter에 해당하는 메소드를 만들어 mode.js에 추가했습니다. 다음은 model.js의 일부입니다.

  Document.virtual('id')
    .get(function() {
      return this._id.toHexString();
    });

 
toHexString 을 사용해서 4cd733fb20a558cee5000001 와 같은 나이스~한 ID값을 갖게 되었습니다. 
(옮긴이 생각. 나이스~한 ID? 대체 어딜봐서???-_-)

(옮긴이 주)
위 코드는 현재 버전에 맞게 바꾼 코드입니다. :)

nodepad앱이 버전차이에 따라 어떻게 바뀌었나 보려면 여길
결국 최종적인 model.js가 어떻게 되었나 보려면 여길



업데이트와 삭제(Update and Delete)

업데이트와 삭제 메소드는 둘 다 우선 문서를 먼저 읽어 들이고, 그 다음 save 혹은 remove를 합니다. 일반적인 패턴은 다음과 같습니다.
 

app.put('/documents/:id.:format?', function(req, res) {

  // 문서 읽어오기

  Document.findById(req.body.document.id, function(d) {

    // 무언가 작업하기

    d.title = req.body.document.title;

    d.data = req.body.document.data;


    // 변경된 내용 저장

    d.save(function() {

      // 요청한 포맷에 맞게 응답

      switch (req.params.format) {

        case 'json':

          res.send(d.__doc);

         break;


         default:

          res.redirect('/documents');

      }

    });

  });

});


 

삭제(Delete)도 기본적으로 동일하다. save 대신에 remove를 호출하게 됩니다.


자바스크립트를 이용한 삭제(Delete JavaScript)

우리가 Express를 이용해서 만들고 있는 API 중에서 쫌 괴상한 부분은 del 메소드입니다. POST의 파라미터로 _method="delete"가 있는걸 볼 겁니다. 대부분의 프레임워크들은 클라이언트 쪽의 자바스크립트를 사용해서 이 부분을 구현합니다.

첫 번째 따라하기에서 이야기 했듯인 우리는 jQuery를 쓸 생각입니다. layout.jade 템플릿을 수정해서 jQuery를 포함시킬 수 있습니다. 다음 코드를 보겠습니다.

!!!
html
  head
    title= 'Nodepad'
    link(rel='stylesheet', href='/stylesheets/style.css')
    script(type='text/javascript', src='https://ajax.googleapis.com/

      ajax/libs/jquery/1.4.4/jquery.min.js')
  body!= body
    script(type='text/javascript', src='/javascripts/application.js')


끝 부분에 우리가 만드는 자바스크립트를 포함하고 있습니다. Express는 public 폴더를 이용해서 정적 파일(html, js, image 등의 변화없는 파일)을 서비스 합니다.

클라이언트 측 삭제 자바스크립트는 다음과 같은 식으로 동작합니다.

1.   confirm()을 사용해서 해당 문서를 삭제하길 원하는지 여부를 체크한다.
 
2.   _method라는 이름의 히든 입력 폼을 동적으로 끼워 넣는다. 이때 _method의 값은 delete로 한다.

3.   폼을 submit한다.

물론 jQuery를 쓰면 쉽다. 저는 위 내용을 명령어 체인 한방으로 다음처럼 작성했습니다.:

 $('.destroy').live('click', function(e) {
  e.preventDefault();
  if (confirm('Are you sure you want to delete that item?')) {
    var element = $(this),
        form = $('<form></form>');
    form
      .attr({
        method: 'POST',
        action: element.attr('href')
      })
      .hide()
      .append('<input type="hidden" />')
      .find('input')
      .attr({
        'name': '_method',
        'value': 'delete'
      })
      .end()
      .submit();
  }
});



(옮긴이 주)
위 코드는 아래 코드 하단부에서 확인 가능


live이벤트 델리게이트를 사용했습니다. 덕분에 클라이언트 측 HTML을 인라인 자바스크립트로 어지럽히지 않을 수 있었습니다. (옮긴이 주. inline 자바스크립트 : 폼에 onClick이벤트 같은 항목을 넣는 것)

방문자 첫 페이지(index page)

 /documents로 리다이렉팅 하는걸 기본 액션으로 정했었습니다. document 인덱스 액션은 현재 다음과 같습니다.

h1 Your Documents

p
  a(class='button', href='/documents/new') + New Document

ul
  - for (var d in documents)
    li
      a(class='button', href='/documents/' + documents[d].id 

        + '/edit') Edit
      a(class='button destroy', href='/documents/' 

        + documents[d].id) Delete
      a(href='/documents/' + documents[d].id)
        =documents[d].title



위 내용은 Jade에서 반복문을 사용법의 한가지 예 입니다. 좀 더 선호하는 방식은 파셜의 컬렉션 지원방식을 사용하는 것입니다만, Jade에서 템플릿을 사용할 때 블록을 다루는 방법을 보여주기에는 적절하다고 생각합니다.

결론(Conclusion)

이제 기본동작은 하는 nodepad가 만들어졌습니다. 다음 시리즈를 이어가지 전에 최신 버전의 nodepad 를 살펴보고 어떤 쿨~한 기능을 넣으면 좋을지 생각해 보세요.

파트4가 끝났습니다. 다음 따라배우기는 "Part 5: 인증과 세션, 접근제어 미들웨어 (Authentication, Sessions, Access Control Middleware)"입니다. 오프라인 모임 전에 파트5까지 진도가 나갈 수 있기를 희망해 봅니다. 


파트4가 끝났습니다. 다음 따라배우기는 "Part 5: 인증과 세션, 접근제어 미들웨어 (Authentication, Sessions, Access Control Middleware)"입니다. 오프라인 모임 전에 파트5까지 진도가 나갈 수 있기를 희망해 봅니다.