Blog

Backbone.js 간단하게 시작하기

April 1, 2014

Backbone.js 간단하게 시작하기

Backbone.js

1월달에 Backbone.js 를 이용해서 자그마한 프로젝트를 진행 했었는데, 내용이 가물가물해지네요. 그 오묘함을 잊어먹기 전에 글로 납깁니다. 간단히 적는거라 생략된 내용이 많을 수 있습니다. 인터넷에서 구할 수 있는 여러 자료를 참고했습니다. 문제가 있거나 제가 작성한 자료에 틀린 부분이 있다면 댓글로 피드백 해주시면 감사하겠습니다.

1. What Backbone gives

백본 홈페이지는 이런 글로 시작합니다.

Backbone.js gives structure to web applications by providing models with key-value binding and custom events, collections with a rich API of enumerable functions, views with declarative event handling, and connects it all to your existing API over a RESTful JSON interface.

백본은 DOM Accessor 나 Control Suite, 혹은 All in one package 라기 보다는 6.5kb (v1.1.2) 의 경량 프레임워크입니다. (물론 실제로 필요한 underscore 등을 더하면 좀 더 되긴 합니다.) 웹 애플리케이션에게 구조 적인 제어 방법을 제공함으로서 커다란 사이즈의 JS 웹 애플리케이션을 작성할 때 발생할 수 있는 문제들을 효율적으로 해결할 수 있습니다. 여기에 의하면

1. Ajax 로 꼬일대로 꼬인 부분, 도저히 이해할 수 없는 Call path 를 알아보기 쉽게 해 줍니다.
2. Event 갯수가 많아지고, 복잡해지면서 핸들링하는 코드들. 백본은 감당할 수 있게 해줍니다. 아름다운 방법으로!
3. 흔히 Deadview 라고 말하는, 뿌리고 나서 죽어버린 HTML 들. 이 것들을 이용해 다시 무언가를 처리하려면 Attribute 방식으로 접근해야 하는데, 백본은 Model-View 구조를 통해서 보이는 부분과 실제 데이터를 연동시킵니다. 더 이상 Mark-up 내에 data를 담을 필요가 없습니다.

Backbone 은 크게 4개의 부분으로 구성되어있습니다. View, Model, Collection, Router 이 구조는 다른 MVC 프레임워크를 공부해 보셨다면 금방 이해하실 수 있는 구조입니다. 여기 에 의하면, 각 컴포넌트들을 다음과 같은 그림으로 표현할 수 있습니다.

1. View

View 는 보여지는 부분입니다. 그림에서 보실 수 있듯이 Router에서 # 해쉬 뒤에 있는 경로들이 라우팅되서 적절한 View 가 렌더링 됩니다. 중요한 것은, View 는 보여지는 부분이므로 보여질 데이터 와 데이터를 담을 템플릿 을 가지고 있습니다. 그리고 사용자로부터 이벤트를 받아 처리할 핸들러 도 담고 있구요. 사실 이런 관점에서 볼 때 백본은 컨트롤러 가 없으므로 MVC 보다는 MV* (MV Whatever) 프레임워크라 부르는 것도 납득할만 합니다.

백본의 View 가 화면에 뿌려줄 데이터 는 View 와 연결된 Model 이 가지고 있습니다. (모든 경우는 아닙니다만.) 예를 들어 다음과 같은 리스트를 상상한다면, View를 3부분으로 나눌 수 있겠습니다.

– Todos 입력부분
– Lists
– 맨 아랫부분의 Result

그리고 다시 가운데 Listsa List 의 집합으로 볼 수 있습니다. 즉 Lists 라는 View 자체는, List View 여러개, 즉 Item 여러개로 구성되어 있다는 말입니다. 그리고 이 아이템들은 각각의 데이터를 가지고 있습니다. string = “write a post” 라던가, check = false 처럼요. 그리고 이런 데이터들은 Model 에 저장됩니다. View 가 데이터를 가지고 있다면, 하나의 모델과 1:1 로 바인딩 될 수 있다는 뜻입니다. 이런 관점에서 본다면 View 는 HTML이며 실제 데이터는 Javascript 객체인 Model 이 가지고 있다고 생각할 수 있습니다.

그렇다면 Event 는 어떻게 처리되는걸까요? View 가 Event 입력을 받는다는건 View 가 HTML 이므로 당연한건데 말이지요. 아래의 View 코드에서 확인하실 수 있습니다.

app.TodoView = Backbone.View.extend({
        tagName:  'li',
        template: _.template($('#item-template').html()),
		events: {
			'click .toggle': 'toggleCompleted',
			'dblclick label': 'edit',
			'click .destroy': 'clear',
			'keypress .edit': 'updateOnEnter',
			'keydown .edit': 'revertOnEscape',
			'blur .edit': 'close'
		},
		initialize: function () {
			this.listenTo(this.model, 'change', this.render);
			this.listenTo(this.model, 'destroy', this.remove);
			this.listenTo(this.model, 'visible', this.toggleVisible);
		},
		// Re-render the titles of the todo item.
		render: function () {
			if (this.model.changed.id !== undefined) {
				return;
			}
			this.$el.html(this.template(this.model.toJSON()));
			this.$el.toggleClass('completed', this.model.get('completed'));
			this.toggleVisible();
			this.$input = this.$('.edit');
			return this;
        },
		edit: function () {
			this.$el.addClass('editing');
			this.$input.focus();
        }
});

보셨듯이 각각의 View 객체에는 events 라는 멤버가 있습니다. 여기에 event-type 과 이벤트가 일어났을때 실행될 함수를 넣어주면 되는 것이지요. 예를들어 현재 View 의 하위에 있는 label 요소에 dbclick 이라는 이벤트가 발생하면 edit 라는 함수를 실행시키게 됩니다.

View 가 Model에 담긴 데이터들을 뿌려주게 될때, View에 마크업을 직접 지정해서 뿌려줄 수도 있고 Template 를 활용할 수도 있습니다. 템플릿은 재활용될 수 있는 마크업 같은 건데, 백본은 주로 Underscore.js 의 템플릿 기능을 _.template 를 통해서 이용하므로 이것을 설명하겠습니다.

<script type="text/template" id="item-template">
<div class="view">
    <input class="toggle" type="checkbox" <%= completed ? 'checked' : '' %>>
    <label><%= title %></label>
    <button class="destroy"></button>
</div>
    <input class="edit" value="<%- title %>">
</script>

이것이 템플릿인데요, HTML 내부에 독특하게 <= > 로 감싸진 부분이 보이실 겁니다. 예를들어 <label> 요소 내부의 텍스트로 <= title > 을 사용하는데, 이 title 이란 값은 View 와 연결된 Model 이 가지고 있는 값입니다. 정리하자면, 똑같은 View 가 뿌려질때 HTML이 담겨있는 template 에, 바뀔 부분만 Model 의 값을 집어 넣고 뿌려주게 됩니다.

2. Model

Mode 은 데이터를 담고 있습니다. 일단 코드부터 보시지요.

app.Todo = Backbone.Model.extend({
        // Default attributes for the todo
        // and ensure that each todo created has `title` and `completed` keys.
        defaults: {
            title: '',
            completed: false
        },
        // Toggle the `completed` state of this todo item.
        toggle: function () {
            this.save({
                completed: !this.get('completed')
            });
        }
    });

아까 템플릿에 집어 넣었던 title 이란 값을 가지고 있는걸 볼 수 있습니다. 중요한점은, Model 은 가지고 있는 데이터를 자바스크립트 객체로서 보관하는데(JSON이라 보시면 되겠습니다.), 이 값을 Backend 에 넘겨줄 수 있습니다. 보시면 알겠지만, save 함수가 그 역할을 담당합니다. 보내주는 것 말고, CRUD 연산처럼 삭제하거나 업데이트하거나 하는 함수들도 지원됩니다. 중요한점은 어느 URL 로 그 요청이 가느냐인데, 그건 아래 Collection 에서 보시겠습니다. 그리고 Collection 으로 넘어가기 전에 앞부분에서 설명드리지 않았던 부분에 대해서 조금 더 설명 드리고 가도록 하겠습니다.

initialize: function () {
    this.listenTo(this.model, 'change', this.render);
    this.listenTo(this.model, 'destroy', this.remove);
    this.listenTo(this.model, 'visible', this.toggleVisible);
},

위 코드는 View 부분에서 따온 코드입니다. View 가 초기화 될때 자동으로 호출되는 initialize 함수구요. 여기 보면 첫번째 줄에서, View 에서 초기화 될때 자신과 연동된 모델이 변화하면 다시 그려라 라는 이벤트를 등록하는걸 볼 수 있습니다. 즉, 사용자가 데이터를 입력을 하면, Viewevents 에서 등록된 핸들러를 통해 처리를 하고, 변경될 데이터가 있으면 Model 의 값을 수정할 겁니다. 그렇게 되면 위의 코드에 의해서 자동으로 View 가 새로 그려지는 거구요. 즉, 사용자는 View 에서 입력받은 데이터를 통해 Mode 만 변경하면 View 는 신경쓰지 않아도 됩니다.

3. Collection

CollecitonModel 객체의 집합입니다. 모아 놓았다는 뜻이지요. 예를 들어서 고양이에 대한 데이터를 가지고 있는 cat 이라는 model 이 있다면, 우리는 100 마리의 고양이를 나타내기 위해 100개의 cat Model 을 만들 수 있습니다. 클래스에서 객체 찍어 내듯이요. 그리고 이런 100개의 cat Model 들을 모아놓은 것이 Collection 입니다.

Collection 이란 Backbone 객체는, Model 들이 어느 URL 로 POST/GET/DELETE/PUT 될지를 결정합니다. Collection 내에 URL이란 속성이 있거든요. 일단 코드부터 보시겠습니다. 위와는 조금 다른 샘플을 들어보겠습니다.

app.Book = Backbone.Model.extend({
    defaults: {
        title: 'No title',
        author: 'Unknown'
    }
});
app.Library = Backbone.Collection.extend({
    model: app.Book,
    url: '/api/books'
});

여러분들이 Backbone 의 Book Model 을 이용해서 새로운 Model 객체, 그러니까 위에서는 Book Model 객체를 var book1 = new Book; 과 같은 코드를 이용해 만드신다면, 이 Model 객체에는 JSON 객체와, 그걸 조작할 수 있는 메소드가 생기게 됩니다. 재미있는 사실은, 처음에는 그냥 이 두가지를 담고 있는 모델인데, book1.save() 를 호출하면 /api/books 에 POST 요청이 날라가게 되고, 백엔드가 넘겨준 것을 받아 Modelid 란 속성을 가지게 됩니다. 이게 핵심입니다. 이 모델과, 다른 모델을 구분할 수 있는 PK인 것이지요. 자세한 내용은 여기를 참고하시는게 더 빠르겠습니다.

요약해서 말씀드리면, 컬렉션은 모델의 집합이며 컬렉션의 URL에 따라 모델의 save, fetch, destory 등을 호출하게 되면 모델의 id 여부에 따라 적절한 POST/PUT/DELETE/GET 요청이 날라가게 됩니다. 컬렉션에서 fetch 등을 호출하시면 컬렉션 내부 전체 모델을 대상으로 연산이 처리되겠지요. 이것도 마찬가지로 더 자세한 내용은 여기 를 참고하시면 되겠습니다.

4. Router

라우터는 생략하겠습니다.

References

  1. http://www.slideshare.net/serrynaimo/backbonejs-a-brief-introduction
  2. http://www.slideshare.net/gyutaejo/backbonejs-m-v
  3. http://nodeqa.com/nodejs_ref/49
  4. http://www.wekeroad.com/2011/08/12/the-backbone-js-todo-list-refactored-part-2/
Array