Skip to content

Commit

Permalink
bug fixes, 0.8 update, chapter 14 rewritten
Browse files Browse the repository at this point in the history
  • Loading branch information
Lee Sangwon authored and Jisu Park committed Sep 2, 2014
1 parent 7eb0540 commit aa8e84b
Show file tree
Hide file tree
Showing 12 changed files with 224 additions and 166 deletions.
10 changes: 8 additions & 2 deletions 01-introduction.md.erb
Expand Up @@ -42,13 +42,19 @@ paragraphs: 35

이 책을 읽은 후에, 더 공부하길 원하면 같은 방식을 따르는 Telescope의 코드를 쉽게 이해할 수 있을 것이다.

### 이 책은 누가 읽을 수 있나?

이 책을 쓰는 동안 우리 목표중의 한 가지는 쉽게 접근할 수 있고, 쉽게 이해할 수 있도록 하는 것이었다. 그래서 독자가 Meteor, Node, MVC 프레임워크, 또는 심지어 일반적인 서버 코딩까지도 경험이 없어도 따라갈 수 있을 것이다.

한 편, 우리는 독자가 기본적인 JavaScript 문법과 개념에는 익숙하다고 가정하고 있다. 하지만, 독자가 약간의 jQuery 코드를 만져보았거나 혹은 브라우저의 개발자 콘솔에서 이런 저런 조작을 해 본 정도라면 가능할 것이다.

### 저자 소개

우리가 누군지, 신뢰할만한 지 궁금한 분들을 위해서, 우리에 대한 약간의 배경 설명을 하겠다.

<%= image "tom-photo.jpg", "portrait" %>

**Tom Coleman**은 품질과 사용자 체험에 초점을 두는 웹 개발 샵인 [Percolate Studio](http://percolatestudio.com/)의 일원이다. 그는 또한 [Meteorite](https://github.com/oortcloud/meteorite)와 [Atmosphere](http://atmosphere.meteor.com) 패키지 저장소의 공동 창업자이다. 그리고 많은 다른 미티어 오픈 소스 프로젝트([Router](https://github.com/tmeasday/meteor-router)와 같은)를 지원하고 있다.
**Tom Coleman**은 품질과 사용자 체험을 중시하는 웹 개발사인 [Percolate Studio](http://percolatestudio.com/)의 일원이다. 그는 [Meteorite](https://github.com/oortcloud/meteorite)와 [Atmosphere](http://atmosphere.meteor.com) 패키지 저장소의 공동 창업자이다. 그리고 많은 다른 미티어 오픈 소스 프로젝트([Router](https://github.com/tmeasday/meteor-router)와 같은)를 배후에서 지원하는 핵심 중의 일원이기도 하다.

<%= image "sacha-photo.jpg", "portrait" %>

Expand All @@ -69,7 +75,7 @@ paragraphs: 35

프로그래밍 책을 따라 읽어가다가 어느 순간 독자가 작성한 코드가 예제와 맞지 않음을 깨닫게 되고 더 이상 제대로 작동하지 않는 것보다 나쁜 경우는 없다.

이렇게 되지 않도록, 우리는 [Microscope를 위한 Github 저장소](https://github.com/SachaG/Microscope)를 설치했다. 그리고 모든 코드 변경시마다 이를 커밋한 git에 대한 링크를 제공할 것이다. 또한, 각 커밋은 그 커밋 시점의 앱의 실제 인스턴스에 대한 링크를 제공하여 독자가 자신의 코드와 비교할 수 있게 하였다. 아래는 실제 예제이다:
이렇게 되지 않도록, 우리는 [Microscope를 위한 Github 저장소](https://github.com/DiscoverMeteor/Microscope)를 설치했다. 그리고 모든 코드 변경시마다 이를 커밋한 git에 대한 링크를 제공할 것이다. 또한, 각 커밋은 그 커밋 시점의 앱의 실제 인스턴스에 대한 링크를 제공하여 독자가 자신의 코드와 비교할 수 있게 하였다. 아래는 실제 예제이다:

<%= commit "11-2", "Display notifications in the header." %>

Expand Down
4 changes: 2 additions & 2 deletions 02-getting-started.md.erb
Expand Up @@ -23,7 +23,7 @@ $ curl https://install.meteor.com | sh

미티어를 독자의 컴퓨터에 설치할 수 없다면(혹은 설치를 원하지 않으면), [Nitrous.io](http://nitrous.io)를 방문하여 살펴보기를 권한다.

Nitrous.io는 앱을 실행하거나 브라우저에서 바로 코드를 편집하게 하는 서비스이다. 우리는 독자가 설치하는데 도움이 되는 [간단한 안내서](https://www.discovermeteor.com/2013/10/04/meteor-nitrous/)를 작성하여 제공한다.
Nitrous.io는 앱을 실행하거나 브라우저에서 바로 코드를 편집하게 하는 서비스이다. 우리는 독자가 설치하는데 도움이 되는 [간단한 안내서](https://www.discovermeteor.com/blog/meteor-nitrous/)를 작성하여 제공한다.

이 안내서의 “Installing Meteor & Meteorite” 섹션까지 읽은 다음, 이 장의 “간단한 앱 만들기” 섹션에서 시작하여 이 책을 다시 따라가면 된다.

Expand Down Expand Up @@ -64,7 +64,7 @@ $ sudo -H npm install -g meteorite

이것으로 되었다! 이제부터 Meteorite가 모든 것을 처리할 것이다.

주: Meteorite에 대한 윈도우 지원은 아직 없다. 그렇지만, 대신 [our windows tutorial](http://themeteorbook.com/2013/03/20/using-meteor-and-atmopshere-on-windows/)를 한 번 살펴보기 바란다.
주: Meteorite에 대한 윈도우 지원은 아직 없다. 그렇지만, 대신 [our windows tutorial](http://www.discovermeteor.com/blog/using-meteor-and-atmopshere-on-windows/)를 한 번 살펴보기 바란다.


<% note do %>
Expand Down
16 changes: 2 additions & 14 deletions 02s-deploying.md.erb
Expand Up @@ -26,27 +26,15 @@ paragraphs: 46

### Meteor.com에 배포하기

미티어 서브도메인(즉, `http://myapp.meteor.com`)에 배포하는 것은 가장 쉽고, 가장 먼저 시도해 볼 것이다. 이곳은 초기에 앱을 시연하거나, 또는 스테이징 서버를 신속히 설치하기에 유용하다.
미티어 서브도메인(즉, `http://myapp.meteor.com`)에 배포하는 것이 가장 쉬운 선택이며, 처음 시도해 볼 것이다. 이곳은 초기에 앱을 시연하거나, 또는 스테이징 서버를 신속히 설치하기에 유용하다.

미티어에 배포하는 것은 매우 간단하다. 터미널을 열고, 앱 디렉토리로 이동하여, 다음을 입력하면 된다:

~~~bash
$ meteor deploy myapp.meteor.com
~~~

물론, "myapp" 은 독자가 정한 이름으로 바꾸되, 이미 사용되지 않은 이름이어야 할 것이다. 지정한 이름이 이미 사용중이면 미티어는 비밀번호를 입력하라고 요구한다. 이렇게 되면 `ctrl+c`로 작동을 중지하고 다른 이름으로 재시도하기 바란다.

모든 것이 잘 진행되면, 몇 초 후에는 `http://myapp.meteor.com` 에서 앱에 접속할 수 있다:

### 비밀번호 보호

처음에는 미티어 서브도메인에 제한은 없다. 누구든 자신의 정한 도메인 이름을 사용할 수 있고, 기존 이름에 덮어쓸 수도 있다. 따라서, 아마도 다음과 같이 도메인 이름에 -p 옵션을 사용하여 비밀번호를 걸어 보호하길 원할 것이다:

~~~~bash
$ meteor deploy myapp.meteor.com -p
~~~~

위 명령어를 수행하면, 미티어는 비밀번호를 입력하라고 요구한다. 그리고 이 시점부터 이 앱을 배포할 때마다 비밀번호를 요구할 것이다.
물론, "myapp" 은 독자가 정한 이름으로 바꾸되, 이미 사용되지 않은 이름이어야 한다. 처음 앱을 배포할 때에는 Meteor 계정을 생성하도록 하는 과정이 진행될 것이다. 모든 것이 잘 진행되면, 몇 초 후에는 `http://myapp.meteor.com` 에서 앱에 접속할 수 있다:

이 호스트 인스턴스의 데이터베이스에 직접 접속하거나 앱의 도메인을 커스텀 도메인으로 설정하는 것 같은 일에, 더 자세히 알고 싶다면 [Meteor 공식 문서](http://docs.meteor.com/#deploying)를 참조하기 바란다.

Expand Down
2 changes: 2 additions & 0 deletions 03s-using-github.md.erb
Expand Up @@ -114,6 +114,8 @@ HEAD is now at c7af59e... Augmented the postsList route to take a limit
$ git checkout master
~~~

이 과정의 어느 시점에서나, “detached HEAD” 상태에서도, 앱을 `meteor` 명령어로 실행시킬 수도 있다. 만약 Meteor가 패키지가 누락되었다는 메시지를 보이면 `mrt update` 명령을 바로 실행시키면 되는데, 이것은 Microscope의 Git 저장소에는 패키지가 포함되어 있지 않기 때문이다.

### History 관점

또 다른 일반적인 시나리오가 있다: 독자가 어떤 파일을 보고 있는데 독자가 전에 보지 못했던 변경 사항이 있는 것을 발견했다. 문제는 *언제* 파일이 변경되었는지 기억을 못한다는 것이다. 그러면 올바른 버전을 찾을 때까지 각 커밋을 하나씩 찾아보려고 할 것이다. 하지만, GitHub의 **History** 기능 덕분에 쉽게 할 수 있다.
Expand Down
2 changes: 1 addition & 1 deletion 04-collections.md.erb
Expand Up @@ -34,7 +34,7 @@ Posts = new Meteor.Collection('posts');

### Var을 적용할까 말까?

미티어에서, `var` 키워드는 해당 객체의 영역(scope)을 현재의 파일로 제한한다. 우리는 `Posts` 컬렉션을 앱 전체에서 이용할 수 있도록, 여기서 `var` 키워드 적용을 생락한 것이다.
미티어에서, `var` 키워드는 해당 객체의 영역(scope)을 현재의 파일로 제한한다. 여기서, 우리는 `Posts` 컬렉션을 앱 전체에서 이용하기를 원한다. 이것이 우리가 `var` 키워드를 사용하지 *않는* 이유이다.

<% end %>

Expand Down
2 changes: 1 addition & 1 deletion 07-creating-posts.md.erb
Expand Up @@ -233,7 +233,7 @@ Router.onBeforeAction('loading');
Router.onBeforeAction(requireLogin, {only: 'postSubmit'});
~~~
<%= caption "lib/router.js" %>
<%= highlight "19~26,28" %>
<%= highlight "19~26,29" %>

접근 거부 페이지를 위한 템플릿도 만든다:

Expand Down
6 changes: 4 additions & 2 deletions 08-editing-posts.md.erb
Expand Up @@ -13,7 +13,9 @@ paragraphs: 29

~~~js
Router.configure({
layoutTemplate: 'layout'
layoutTemplate: 'layout',
loadingTemplate: 'loading',
waitOn: function() { return Meteor.subscribe('posts'); }
});

Router.map(function() {
Expand Down Expand Up @@ -48,7 +50,7 @@ var requireLogin = function() {
Router.onBeforeAction(requireLogin, {only: 'postSubmit'});
~~~
<%= caption "lib/router.js" %>
<%= highlight "12~15" %>
<%= highlight "14~17" %>

### Post 수정 템플릿

Expand Down
18 changes: 2 additions & 16 deletions 11s-advanced-reactivity.md.erb
Expand Up @@ -76,23 +76,9 @@ Meteor.setInterval(function() {
~~~
<%= highlight "1~7,14~17" %>

우리가 한 것은 `_currentLikeCountListeners`의 의존성을 설정한 것인데, 이는 `currentLikeCount()`이 사용된 그 내부의 모든 컴퓨테이션을 추적한다. `_currentLikeCount`값이 변경되면, 그 의존성에서 changed() 함수를 호출하는데 이는 모든 추적된 컴퓨테이션을 무효화(invalidate)시킨다.
우리가 한 것은 `_currentLikeCountListeners`의 의존성을 설정한 것인데, 이는 `currentLikeCount()`이 내부에서 사용된 모든 컴퓨테이션을 추적한다. `_currentLikeCount`값이 변경되면, 그 의존성에 따라 changed() 함수를 호출하는데 이는 모든 추적된 컴퓨테이션을 무효화(invalidate)시킨다.

이 컴퓨테이션들은 진행하면서 각 케이스별로 그 변경 사항을 처리한다. 템플릿의 컴퓨테이션의 경우, 이는 템플릿이 자신을 다시 그리는 것을 의미한다.

### 템플릿 컴퓨테이션과 다시 그리기 제어

각 템플릿이 각자의 컴퓨테이션을 가지는 이유는 화면에서 일어나는 다시 그리기의 양을 제어하기 위해서다.

템플릿 내부에서 다른 템플릿을 호출한다면, 첫째 컴퓨테이션 내부에서 둘째 컴퓨테이션을 설정하게 된다. 그래서 안쪽의 템플릿에서 사용되는 반응형 데이터가 변경되면, 그 안쪽 템플릿은 *바깥쪽* 템플릿이 그대로 있는 상태에서 다시 그리기가 일어난다. 이런 방식으로 컴퓨테이션들은 반응형 변화의 적용 범위를 제어하는데 사용된다.

미티어는 또한 템플릿 내부에 템플릿이 존재하는 형태의 구성을 증가시키는 데 약간의 추가 도움을 제공한다.

첫째, `{{#constant}}` 블록 헬퍼는 스스로 반응성을 *제거한다*. 그래서 그 블록 내부에서 헬퍼들이 모은 데이터는 딴 한 번만 사용된다. 그리고 이를 포함하는 템플릿이 다시 그리기를 해도, constant 영역의 HTML은 그대로 남아 있는다. 이 constant 영역은 미티어가 그 내부의 DOM을 다시 그리기를 기대하지 않는 써드파티 위젯을 다루기 아주 좋은 방식이다.

반응성을 제어하는 두 번째 기능은 {{#isolate}} 블록 헬퍼인데, 이것은 템플릿 내부에서 *새* 컴퓨테이션을 설정한다. 다른 말로 표현하면, 이것은 템플릿의 영역을 반응성과 다시그리기의 관점에서 하위 템플릿으로 이동한 것과 같은 효과를 낸다.

그래서 반응형 데이터 소스중의 하나가 그 isolate 블록 내부에서 변경되면, 그 영역은 다시그리기가 이루어지지만, 그것을 포함하는 템플릿의 나머지 영역은 그대로 남아있게 된다. 그런데 전체 템플릿이 다시그리기를 하게 되면 isolated 영역도 함께 다시그리기가 진행된다.
이 컴퓨테이션들은 진행하면서 각 케이스별로 그 변경 사항을 처리한다.

### Deps와 Angular 비교

Expand Down
20 changes: 6 additions & 14 deletions 12-pagination.md.erb
Expand Up @@ -161,6 +161,8 @@ Meteor.publish('posts', function(sort, limit) {

~~~js
Router.map(function() {
//..

this.route('postsList', {
path: '/:postsLimit?',
waitOn: function() {
Expand All @@ -174,12 +176,10 @@ Router.map(function() {
};
}
});

//..
});
~~~
<%= caption "lib/router.js" %>
<%= highlight "8~13" %>
<%= highlight "10~15" %>

라우터 수준에서 데이터 컨텍스트를 지정하였으므로, 우리는 `posts_list.js` 파일 내에 있는 `posts` 템플릿 헬퍼를 안전하게 제거할 수 있다. 그리고 우리가 데이터 컨텍스트를 `posts`라고 (헬퍼와 동일한 이름으로) 명명하였으므로 `postsList` 템플릿을 건들 필요도 없다!

Expand Down Expand Up @@ -315,7 +315,7 @@ PostsListController = RouteController.extend({
return Posts.find({}, this.findOptions());
},
data: function() {
var hasMore = this.posts().fetch().length === this.limit();
var hasMore = this.posts().count() === this.limit();
var nextPath = this.route.path({postsLimit: this.limit() + this.increment});
return {
posts: this.posts(),
Expand All @@ -325,7 +325,7 @@ PostsListController = RouteController.extend({
});
~~~
<%= caption "lib/router.js" %>
<%= highlight "16~23" %>
<%= highlight "13~15,17~22" %>

이 마술같은 라우터를 깊이있게 들여다보자. (현재 작동하는 `PostsListController controller`에서 상속받을) `postsList` route는 매개변수 `postsLimit`를 가진다는 점을 기억하기 바란다.

Expand All @@ -337,7 +337,7 @@ PostsListController = RouteController.extend({

`this.limit()`는 우리가 보여주려는 post의 현재 갯수를 리턴하는데 이 값은 현재 URL에 있는 값이거나 또는 URL에 매개변수가 없을 경우의 초기 설정값 (5)이다.

한 편, `this.posts`는 현재 커서를 가리키므로, `this.posts.fetch().length`는 커서에 실제로 존재하는 posts의 갯수를 가리킨다.
한 편, `this.posts`는 현재 커서를 가리키므로, `this.posts.count()`는 커서에 실제로 존재하는 posts의 갯수를 가리킨다.

그러므로 여기서 우리가 말하려는 것은 `n`개의 post를 요구하면 `n`개를 얻고, 계속 “load more” 버튼을 보여준다. 그러나 `n`개를 요구했는데 `n`개보다 *적은* 갯수를 얻게되면, 한계에 온 것을 의미하고 버튼을 보여주는 것을 중단하라는 것이다.

Expand Down Expand Up @@ -369,14 +369,6 @@ Post 목록 화면은 다음과 같이 보일 것이다:
<%= commit "12-4", "Controller에 nextPath()을 추가하고 이를 post의 페이지 이동을 구현했다." %>

<% note do %>

### Count 대 Length

this.posts.count()를 사용하지 않고 this.posts.fetch().length을 사용하는 것에 궁금해 할 지 모르겠다. 이것은 현재의 미티어에 존재하는 [버그](https://github.com/meteor/meteor/issues/654)를 피하기 위한 임시 방편이다. 이 버그가 빠른 시일에 해소되기를 바란다!

<% end %>

### 더 나은 프로그레스 바

페이징은 현재 잘 작동하지만, 한 가지 짜증나는 일이 있다: “load more” 버튼을 누를 때마다 라우터는 더 많은 post를 요구하고 새로운 데이터를 받을 때까지 loading 템플릿이 구동된다. 이 결과로 로딩될 때마다 페이지의 탑으로 이동해서 아랫쪽으로 스크롤해야 하는 사태가 일어난다.
Expand Down
38 changes: 32 additions & 6 deletions 13-voting.md.erb
Expand Up @@ -269,7 +269,7 @@ Template.postItem.events({
다음, 투표수가 1인 post는 "1 vote**s**"라고 표시되는 것을 볼 수 있다. 이 부분을 적절하게 복수처리를 하도록 하자. 복수처리는 복잡한 프로세스가 될 수 있지만, 우리는 매우 단순한 방법으로 할 것이다. 어디에서나 사용할 수 있는 일반적인 Handlebars helper를 만든다:

~~~js
Handlebars.registerHelper('pluralize', function(n, thing) {
UI.registerHelper('pluralize', function(n, thing) {
// fairly stupid pluralizer
if (n === 1) {
return '1 ' + thing;
Expand Down Expand Up @@ -396,7 +396,7 @@ PostsListController = RouteController.extend({
return Posts.find({}, this.findOptions());
},
data: function() {
var hasMore = this.posts().fetch().length === this.limit();
var hasMore = this.posts().count() === this.limit();
return {
posts: this.posts(),
nextPath: hasMore ? this.nextPath() : null
Expand Down Expand Up @@ -437,13 +437,23 @@ Router.map(function() {
});
~~~
<%= caption "lib/router.js" %>
<%= highlight "8,16,21~34,36~49" %>
<%= highlight "8,20,25~37,40~53" %>

이제 route가 하나 이상이 되었으므로, `nextPath` 로직을 `PostsListController`의 외부로 뽑아내어 `NewPostsListController`와 `BestPostsListController`의 내부로 넣는다. 이 각각의 경로가 다르기 때문이다.

추가적으로, `votes`로 정렬을 할 때, 정렬이 올바르게 처리되도록 두 번째 정렬 조건에 시간을 넣는다.

그리고 header에도 링크를 추가한다:
새 컨트롤러를 넣었으면, 우리는 이제 이전의 `postsList` route를 안전하게 제거할 수 있다. 다음 코드를 삭제하면 된다:

```
this.route('postsList', {
path: '/:postsLimit?',
controller: PostsListController
})
```
<%= caption "lib/router.js" %>

그리고 header에 링크를 추가한다:

~~~html
<template name="header">
Expand Down Expand Up @@ -481,9 +491,25 @@ Router.map(function() {
</template>
~~~
<%= caption "client/views/include/header.html" %>
<%= highlight "9, 15~21" %>
<%= highlight "9, 12~17" %>

또한 post를 삭제하는 이벤트 핸들러를 수정한다:

~~~html
'click .delete': function(e) {
e.preventDefault();

if (confirm("Delete this post?")) {
var currentPostId = this._id;
Posts.remove(currentPostId);
Router.go('home');
}
}
~~~
<%= caption "client/views/posts_edit.js" %>
<%= highlight "7" %>

이 모두가 완료되면 최고 post 목록은 다음과 같다:
이 모두가 완료되면 베스트 post 목록은 다음과 같다:

<%= screenshot "13-4", "포인트에 의한 순위" %>
Expand Down

0 comments on commit aa8e84b

Please sign in to comment.