Backbone.js 시작하기 – 3. Collection

Collection

Collection은 model들을 모아 놓은 것입니다. 배열처럼 생각하실 수 있는데요, model들을 만들어 놓고 collection에 넣어서 사용할 수 있습니다. model을 설명드릴 때 model이 DB에서 레코드 하나와 비교될 수 있다고 말씀드렸는데요, collection은 DB 쿼리 결과로 받은 레코드들이라고 생각하실 수 있습니다.

하나의 model이 하나의 collection에 속할 수 있지만, 여러 collection에 속할 수도 있습니다.

http://backbonetutorials.com/what-is-a-collection를 보시면 아래와 같이 설명이 되고 있습니다.

  • Model: Student, Collection: ClassStudents
  • Model: Todo Item, Collection: Todo List
  • Model: Animals, Collection: Zoo

이렇게 할 수도 있고, 아래처럼 하나의 model이 여러 collection에 속할 수도 있습니다.

  • Model: Student, Collection: Gym Class
  • Model: Student, Collection: Art Class
  • Model: Student, Collection: English Class

이제 실제 코드를 작성해가면서 Collection에 대해 알아보겠습니다.

[참고] 이전 글에서 backbone.js를 사용하기 위해서는 먼저 underscore.js를 포함시키고 backbone.js를 넣어야 한다고 말씀드렸는데요, 오늘 살펴볼 예제중에는 jQuery에 의존성이 있는 .fetch() 메서드가 있습니다. 그래서 HTML파일에 backbone.js를 넣기 전에 jQuery도 포함시키고 예제를 실행해 주십시오

생성하기

collection은 model이 있어야 만들 수 있으니 먼저 model을 만들어 보겠습니다.

var Book = Backbone.Model.extend({
	defaults: {
		sort: 'default',
		title: 'default Title'
	}
});

var book1 = new Book({sort: 'JavaScript', title: 'JavaScript Book 1'}),
	book2 = new Book({sort: 'JavaScript', title: 'JavaScript Book 2'}),
	book3 = new Book({sort: 'JavaScript', title: 'JavaScript Book 3'});

book1, book2, book3 라는 model 세 개를 만들었습니다. 그러면 collection을 만들고 이 model 들을 넣겠습니다.

var Book = Backbone.Model.extend({
	defaults: {
		sort: 'default',
		title: 'default Title'
	}
});

var book1 = new Book({sort: 'JavaScript', title: 'JavaScript Book 1'}),
	book2 = new Book({sort: 'JavaScript', title: 'JavaScript Book 2'}),
	book3 = new Book({sort: 'JavaScript', title: 'JavaScript Book 3'});
	
var Books = Backbone.Collection.extend({		// Books 라는 collection을 만듭니다.
	model: Book
});

var books = new Books();	// books 라는 Books collection의 인스턴스를 만듭니다.

books.add([book1, book2, book3]);	// add 메서드를 사용해서 model들을 books에 넣습니다.

model을 만들때와 유사한 방법으로 collection은 Backbone.Collection.extend 를 통해 만들 수 있습니다. 그리고 이때 이 collection이 참조할 model을 model 프로퍼티에 명시해줍니다. 이렇게 만든 collection으로 books라는 인스턴스를 만든 후 거기에 실제 model들을 넣어서 사용하면 됩니다. add 메서드에는 model들을 배열로 넣어주면 됩니다.

collection에서 model을 삭제하고 싶을때에는 remove 메서드를 사용하면 됩니다. books.remove([book1, book2, book3]); 이런 형태로 model들을 삭제할 수 있습니다.

[참고] 하나의 model만 add하거나 remove하고 싶을 때에는 배열을 사용하지 않고 books.add(book1); 또는 books.remove(book1); 하시면 됩니다. 그러나 두 개 이상의 model을 다룰 때에는 배열형태를 사용하셔야 합니다. books.add(book1, book2, book3); 은 제대로 작동하지 않습니다. (book1만 추가됩니다.)

model 참조

이렇게 collection에 model들을 넣어놓으면 model들이 collection의 models라는 프로퍼티에 배열형태로 있는 것을 보실 수 있습니다.

books.models[0];	// book1
books.models[1];	// book2
books.models[2];	// book3

이와 유사한 방법으로 collection의 at 메서드를 사용할 수도 있습니다.

books.at(0);	// book1
books.at(1);	// book2
books.at(2);	// book3

이 방법 외에 collection에 있는 model에 접근하는 방법이 몇 가지 더 있습니다. 먼저, model에 id라는 attribute가 있다면 collection에서 get 메서드를 사용해서 model에 접근할 수 있습니다. 위의 예제코드에서 book1, book2, book3 model에는 id attribute가 없었으니, 지금 추가하고 테스트해보겠습니다.

book1.set({id: 0});
book2.set({id: 1});
book3.set({id: 2});		// Book model들에 id attribute를 추가해줬습니다.

books.get(0);		// book1
books.get(1);		// book2
books.get(2);		// book3

위의 코드처럼, collection의 get메서드는 id attribute르 model에 접근할 수 있게 해줍니다.

[참고] collection은 model을 참조하고 있습니다. 그래서 model을 collection에 넣은 후 나중에 model을 수정(id attribute 추가)해도 collection에 바로 반영되어 get() 을 사용할 수 있습니다.

getByCid 라는 메서드도 있습니다. 이전 글에서 model을 말씀드릴때 cid를 말씀 안드린것 같은데요, model을 생성하면 Backbone이 자동으로 cid라는 고유한 값을 가진 프로퍼티를 만들어줍니다. 그래서 book1.cid 값을 가져올 수 있습니다. cid는 ‘c0’, ‘c1’, ‘c2’ 이런 식으로 c와 숫자를 붙여서 안겹치게 자동 생성이 됩니다. 그래서 model의 cid를 알고 있다면 getByCid 메서드를 사용해서 다음과 같이 model을 가져올 수 도 있습니다.

books.getByCid('c0');		// book1
books.getByCid('c1');		// book2
books.getByCid('c2');		// book3

지금까지 books collection을 만든 다음에 model을 추가하는 방법을 보여드렸는데요, 처음 만들때 model을 넣어서 만들 수도 있습니다.

var Book = Backbone.Model.extend({
	defaults: {
		sort: 'default',
		title: 'default Title'
	}
});

var book1 = new Book({sort: 'JavaScript', title: 'JavaScript Book 1'}),
	book2 = new Book({sort: 'JavaScript', title: 'JavaScript Book 2'}),
	book3 = new Book({sort: 'JavaScript', title: 'JavaScript Book 3'});
	
var Books = Backbone.Collection.extend({		// Books 라는 collection을 만듭니다.
	model: Book
});

var books = new Books([book1, book2, book3]);	// books 라는 Books collection의 인스턴스를 만들면서 model도 넣어줍니다.

이제 collection을 만들었으니 이 collection이 어떤 기능들을 할 수 있는지 알아보겠습니다.

이벤트

model과 마찬가지로 collection도 이벤트 리스너를 등록할 수 있습니다. collection에 model이 추가되면 add 이벤트가 발생합니다. model과 마찬가지로 collection도 initialize 메서드에서 collection이 생성될 때 수행할 일을 넣을 수 있는데 여기에서 이벤트 리스너를 등록하겠습니다.

var Book = Backbone.Model.extend({
	defaults: {
		sort: 'default',
		title: 'default Title'
	}
});

var book1 = new Book({sort: 'JavaScript', title: 'JavaScript Book 1'});
	
var Books = Backbone.Collection.extend({		// Books 라는 collection을 만듭니다.
	model: Book,
	initialize: function () {
		this.bind('add', function (book) {		// add 이벤트는 model이 추가될 때 발생합니다.
												// 이벤트 리스너는 인자로 추가된 model을 받습니다.
			console.log('방금 ' + book.get('title') + '라는 제목을 가진 책이 추가되었습니다.');
		});
	}
});

var books = new Books();
books.add(book1);		// '방금 JavaScript Book 1라는 제목을 가진 책이 추가되었습니다.'

books collection에 book1을 add하면 이벤트 리스너가 실행되고 이때 book.get(‘title’)로 방금 추가된 model의 title attribute을 가져올 수 있습니다.

model들의 attribute 변화에 대해 change 이벤트 리스너를 등록할 수 있습니다. model때와 마찬가지로 ‘change’는 모든 변경에서 이벤트 리스너가 실행되고 ‘change:title’ 과 같이 하면 title attribute가 변경될 때만 이벤트 리스너가 실행됩니다.

var Books = Backbone.Collection.extend({
	model: Book,
	initialize: function () {
		this.bind('change:title', function (book) {
			console.log('방금 ' + book.get('title') + '으로 제목이 수정되었습니다.');
		});
	}
});

var books = new Books();
books.add(book1);

book1.set({title: 'test'});		// title을 수정하면 아래의 메시지가 console에 나옵니다.
								// '방금 test으로 제목이 수정되었습니다.'

정렬

comparator 메서드를 사용하여 collection의 model들을 정렬할 수 있습니다. 이렇게 하면 model들을 add한 순서에 상관없이 comparator에서 정한 규칙에 따라 model들이 자동으로 정렬됩니다.

var Book = Backbone.Model.extend({
	defaults: {
		name: 'default',
		title: 'default Title'
	}
});

var book1 = new Book({name: 'book1', title: 'Hello'}),
	book2 = new Book({name: 'book2', title: 'JavaScript'}),
	book3 = new Book({name: 'book3', title: 'Book'});
	
var Books = Backbone.Collection.extend({
	model: Book,
	comparator: function (book) {		// comparator 는 정렬을 해줍니다.
										// 인자로 model을 받게됩니다.
		return book.get('title').toLowerCase();		// return 값을 model의 title을 소문자로 바꾼 걸로 해줍니다.
	}
});

var books = new Books();
books.add([book1, book2, book3]);

comparator 메서드에서 어떤 값을 return 하느냐에 따라 정렬 규칙이 만들어집니다. 위 코드에서는 comparator가 model의 title을 return 하고 있습니다. 이렇게 하면 title의 알파벳 순으로 model 들이 자동 정렬됩니다.

books.models[0];	// book3
books.models[1];	// book1
books.models[2];	// book2

그래서 title이 ‘Book’인 book3가 제일 먼저, ‘Hello’인 book1가 다음, ‘JavaScript’인 book2이 제일 끝으로 정렬이 됩니다. 만약, book model들이 order라는 attribute가 있고 거기에 값이 0,1,2등으로 들어가 있을 때 collection의 comparator에서 return book.get(‘order’); 이런 식으로 처리해줬으면 order 값이 작은 순으로 정렬이 되었을 것입니다.

fetch

collection에서는 fetch라는 신기한 메서드를 사용할 수 있습니다. 이 메서드를 사용하면 서버의 DB나 브라우저의 localStorage등에 저장된 데이터를 받아와서(그 데이터를 model로 삼아) 바로 collection에 추가해 줄 수 있습니다. 이때는 collection을 만들때 model 프로퍼티가 있어도 되고 없어도 됩니다. 대신 url 프로퍼티가 있어야 합니다. fetch() 메서드를 실행시키면 url 프로퍼티에 있는 url로 ajax request를 합니다. 그 결과 받아온 데이터들이 collection에 들어가는 것입니다. model은 자바스크립트의 객체이기 때문에, 서버에서 출력해주는 데이터의 형태는 JSON(객체의 배열)이어야 할 것입니다.

먼저, 다음과 같이 collection을 만들어 보겠습니다.

var Books = Backbone.Collection.extend({
	url: 'test.php'
});

var books = new Books();

test.php의 소스코드는 다음과 같습니다. 그냥 아무것도 안하고 JSON만 출력해주는 파일입니다.

<?php

$item1 = array('name' => 'book1', 'title' => 'Hello');
$item2 = array('name' => 'book2', 'title' => 'JavaScript');
$item3 = array('name' => 'book3', 'title' => 'Book');

$arr = array($item1, $item2, $item3);

echo json_encode($arr);

?>

그러면 이제 fetch() 메서드를 사용해보겠습니다.

books.fetch();		// fetch 메서드가 url에 등록된 url로 ajax request를 해서 받아온 데이터들을
					// books collection에 model로 넣어줍니다.
					
books.models[0];	// name: 'book1', title: 'Hello' attribute를 가지고 있는 Backbone Model 객체
books.models[1];	// name: 'book2', title: 'JavaScript' attribute를 가지고 있는 Backbone Model 객체
books.models[2];	// name: 'book3', title: 'Book' attribute를 가지고 있는 Backbone Model 객체

이렇게 fetch() 메서드를 사용하면 서버와 통신해서 간단하게 collection을 만들 수 있습니다. 이것은 Backbone으로 애플리케이션을 만들 때 서버에서 주는 데이터들을 Backbone의 Collection으로 만들어서 사용하면 된다는 것을 의미합니다. fetch() 메서드는 ajax request를 할 때 jQuery의 ajax 메서드를 사용합니다. 이 글의 처음 부분에서 오늘 살펴볼 예제는 jQuery를 포함시켜야 한다고 말씀드렸었는데요 바로 fetch() 메서드 부분에서 사용해서 그렇게 말씀드렸습니다.

fetch() 메서드에 인자로 jQuery에서 ajax를 할 때 사용했던 옵션들을 넘겨줄 수 있습니다. 먼저, 서버측의 test.php를 다음과 같이 바꿔보겠습니다.

<?php

$item1 = array('sort' => $_GET['sort'], 'name' => 'book1', 'title' => 'Hello');
$item2 = array('sort' => $_GET['sort'], 'name' => 'book2', 'title' => 'JavaScript');
$item3 = array('sort' => $_GET['sort'], 'name' => 'book3', 'title' => 'Book');

$arr = array($item1, $item2, $item3);

echo json_encode($arr);

?>

jQuery의 ajax가 기본적으로 GET 방식을 사용하듯, fetch() 메서드도 기본적으로 GET 방식으로 통신합니다. 그래서 test.php에서 GET으로 넘겨받은 데이터를 출력하도록 수정했습니다. 그러면 fetch() 메서드 부분도 아래와 같이 바꿔보도록 하겠습니다.

var Books = Backbone.Collection.extend({
	url: 'test.php'
});

var books = new Books();

books.fetch({
	data: {
		sort: 'JavaScript'		// 요청할 때 sort라는 데이터를 넘김
								// test.php에서는 $_GET['sort'] 로 받음
	},
	success: function () {
		alert('success!');		// 요청 성공하면 'success!' alert 띄우기
	}
});
					
books.models[0];	// sort: 'JavaScript', name: 'book1', title: 'Hello' attribute를 가지고 있는 Backbone Model 객체
books.models[1];	// sort: 'JavaScript', name: 'book2', title: 'JavaScript' attribute를 가지고 있는 Backbone Model 객체
books.models[2];	// sort: 'JavaScript', name: 'book3', title: 'Book' attribute를 가지고 있는 Backbone Model 객체

위 코드에서는 fetch() 메서드에 jQuery ajax에서 사용햇던 data, success 등의 프로퍼티와 메서드를 인자로 넘겨서 호출했습니다. ajax request가 성공하고 ‘success!’라는 alert가 떴고, books collection의 model들에는 ‘JavaScript’라는 값을 가진 sort attribute가 등록되었습니다.

[참고] POST방식으로 ajax request를 하기위해 fetch()에 인자로 type: ‘POST’를 한뒤 test.php에서는 $_GET을 $_POST로 바꾸고 테스트해봤는데 정상적으로 동작하지 않았습니다. fetch() 에서는 GET 방식 request만 사용할 수 있는 것 같습니다. 혹시, POST를 사용할 수 있는 방법을 아시면 댓글을 달아주시면 감사하겠습니다.

  • 감사해용

    11년도 글에 너무 늦은 댓글인거 같지만요 ㅎ Backbone.js 1.1.2 버전에서는 type: ‘post’ 이렇게 하니 되네요, ;; 강의 시리즈 정말 잘 듣고 가요 ㅎ 쪼금 Backbone에 대해 감 잡고 갑니다 감사해요 ^^

    • codefactory

      안녕하세요..^^
      요즘 Backbone.js 를 사용하지 않아서 그런 변화가 있었는지 몰랐습니다. 알려주셔서 감사합니다. 제가 쓴 글이 도움이 되셨다니 감사합니다..^^

  • 채용석

    정말 좋은 자료 감사드립니다. 저두 댓글 달기 늦었지만 정독하면서 감을 잘 찾아간것 같습니다.

    • codefactory

      감사합니다..^^