-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy path03-templates.md.erb
230 lines (155 loc) · 11.7 KB
/
03-templates.md.erb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
---
title: 템플릿(Template)
slug: templates
date: 0003/01/01
number: 3
contents: 미티어의 템플릿 언어인 Spacebars에 대하여 배운다.|첫 세 개의 템플릿을 만들어 본다.|미티어 매니저가 어떻게 작동하는 지를 배운다.|정적 데이터를 처리하는 기본 프로토타입을 만들어 본다.
paragraphs: 46
---
미티어 개발에 편하게 진입할 수 있도록 우리는 바깥에서 안으로 접근하는 방식을 취할 것이다. 바꾸어 말하면, 우리는 "아무 기능도 없는" HTML/JavaScript 파일의 외형을 먼저 구축하고, 그 다음에 이를 앱의 내부 동작과 연결한다.
이 장에서는 `/client` 디렉토리 내부에서 일어나는 것들에 대해서만 다룬다는 의미이다.
새로운 파일 `main.html`을 `/client` 디렉토리에 생성한 다음, 아래 코드를 채워 넣는다:
~~~html
<head>
<title>Microscope</title>
</head>
<body>
<div class="container">
<header class="navbar navbar-default" role="navigation">
<div class="navbar-header">
<a class="navbar-brand" href="/">Microscope</a>
</div>
</header>
<div id="main" class="row-fluid">
{{> postsList}}
</div>
</div>
</body>
~~~
<%= caption "client/main.html" %>
이것이 주 템플릿이 될 것이다. 보는 바와 같이, 이것은 `{{> postsList}}` 태그를 제외하면 모두 HTML이다. 이 때, `{{> postsList}}` 태그는 곧 보게 될 `postsList` 템플릿을 삽입할 자리이다. 이제 몇 개의 템플릿을 더 만들어보자.
### 미티어 템플릿
핵심은, 소셜 뉴스 사이트는 목록 구조의 포스트들로 이루어지는데, 템플릿을 구성하는 방식도 정확하게 일치한다.
`/client` 디렉토리 하위에 `/templates` 디렉토리를 만들자. 이곳에 모든 템플릿을 두는데, 깔끔하게 `/templates` 디렉토리 하위에 `/posts` 디렉토리를 만들고 post 관련 템플릿들을 넣을 것이다.
<% note do %>
### 파일 찾기
미티어는 파일을 잘 찾는다. `/client` 디렉토리의 어디에 넣든지 미티어는 이를 찾아서 컴파일한다. 이것은 Javascript나 CSS 파일 경로를 직접 입력할 필요가 없다는 것을 의미한다.
이것은 동일한 디렉토리에 모든 파일을 넣을 수도 있고, 하나의 파일에 모든 코드를 넣을 수도 있다는 의미이기도 하다. 그러나 미티어가 모든 것을 컴파일하여 하나의 최적화된 파일로 만드니, 전체를 잘 구조화하고 보기 좋은 파일 구조를 사용하기 바란다.
<% end %>
마침내 두 번째 템플릿을 만들 준비가 되었다. `client/templates/posts` 디렉토리에 `posts_list.html` 파일을 만든다:
~~~html
<template name="postsList">
<div class="posts">
{{#each posts}}
{{> postItem}}
{{/each}}
</div>
</template>
~~~
<%= caption "client/templates/posts/posts_list.html" %>
그리고 `post_item.html`도 만든다:
~~~html
<template name="postItem">
<div class="post">
<div class="post-content">
<h3><a href="{{url}}">{{title}}</a><span>{{domain}}</span></h3>
</div>
</div>
</template>
~~~
<%= caption "client/templates/posts/post_item.html" %>
템플릿 엘리먼트의 속성인 `name="postsList"`에 주목하라. 이것은 미티어가 템플릿의 위치를 추적하는 데 사용된다 (실제 *파일*의 이름은 상관없다).
이제 미티어의 템플릿 시스템인 **Spacebars**를 소개할 차례다. Spacebars는 단순 HTML에 세 가지가 추가된 형태이다:
(“partials”이라고 부르기도 하는) *inclusions*, *expressions*, 그리고 *block helpers*가 있다.
*Inclusions*는 `{{> templateName}}`의 문법을 사용하는 데, 미티어에게 해당 위치에 같은 이름의 템플릿으로 대체하라는 의미이다(예제의 경우, `postItem`).
*Expressions*는 `{{title}}`와 같이 현재 객체의 속성값이나, 또는 현재 템플릿 매니저(나중에 더 자세하게 다룬다)에 정의된 템플릿 헬퍼의 리턴 값을 지정한다.
마지막으로, *block helpers*는 템플릿의 흐름을 제어하는 특별한 태그로, `{{#each}}…{{/each}}` 또는 `{{#if}}…{{/if}}` 같은 것들이 있다.
<% note do %>
### 더 깊이있게
Spacebars에 대하여 더 상세한 공부를 원하면 [Spacebars 문서](https://github.com/meteor/meteor/blob/devel/packages/spacebars/README.md)를 참조하기 바란다.
<% end %>
이 정도만 알면, 여기서 다루는 내용은 이해할 수 있다.
먼저, `postsList` 템플릿에서는 `posts` 객체를 `{{#each}}…{{/each}}` 블록 헬퍼로 반복하고 있다. 그리고 반복할 때마다 `postItem` 템플릿을 삽입한다.
`posts` 객체는 어디서 왔을까? 좋은 질문이다. 이것은 실제로는 **템플릿 헬퍼**로서, 동적으로 값을 지정하는 수단으로 생각하면 된다.
`postItem` 템플릿은 바로 알 수 있다. 이것은 세 가지 expression만을 사용한다: `{{url}}`과 `{{title}}`은 둘 다 도큐먼트의 속성값을 리턴한다. 그리고 `{{domain}}`은 템플릿 헬퍼를 호출한다.
### 템플릿 헬퍼
지금까지 우리는 Spacebars에 대하여 알아보았는데, 이것은 이것은 몇 개의 태그를 가진 HTML과 다를 게 없다. PHP(또는 Javascript를 포함하는 정규 HTML페이지)와 같은 다른 언어와는 달리, 미티어에서는 템플릿과 그 로직이 분리되어 있으며, 템플릿 자체는 아무 일도 하지 않는다.
템플릿이 제 기능을 하려면 **헬퍼**가 있어야 한다. 헬퍼는 요리사에 비유할 수 있는데 요리사는 원재료(데이터)를 가져와서, 이를 가공하고, 완성된 요리가 담긴 접시를 웨이터(템플릿)에게 전달하고 웨이터는 그것을 고객에게 제공한다.
지금까지 Spacebars로 작업을 해 왔는데, 이것은 몇 개의 태그를 가진 HTML과 다를 게 없다. PHP(또는 Javascript를 포함하는 정규 HTML페이지)와 같은 다른 언어와는 달리, 미티어에서는 템플릿과 그 로직이 분리되어 있으며, 템플릿 자체는 아무 일도 하지 않는다.
바꾸어 말하면, 템플릿의 역할이 변수의 값을 보여주거나, 루프를 도는 것이라면, 헬퍼는 실제로 각 변수에 값을 들고 날라 지정하는 일을 한다.
<% note do %>
### 콘트롤러?
모든 템플릿 헬퍼들을 포함하는 파일을 일종의 콘트롤러라고 생각할 수도 있다. 하지만, 이것은 혼동을 줄 수도 있다. 왜냐면 콘트롤러란 (적어도 MVC 관점에서는) 일반적으로는 다소 다른 역할을 하기 때문이다.
그러므로 우리는 용어 정의를 유보하고, 템플릿의 JavaScript 코드에 대하여 이야기할 때, 단순하게 “그 템플릿의 헬퍼“ 또는 “그 템플릿의 로직”이라 부르기로 했다.
<% end %>
일을 쉽게 하기위해, 우리는 템플릿 이름을 따라서 헬퍼 이름을 짓고 확장자를 **.js**로 하는 관례를 채택할 것이다. 이에 따라 `/client/templates/posts` 디렉토리에 `posts_list.js`를 만들어 첫 헬퍼를 작성해보자:
~~~js
var postsData = [
{
title: 'Introducing Telescope',
url: 'http://sachagreif.com/introducing-telescope/'
},
{
title: 'Meteor',
url: 'http://meteor.com'
},
{
title: 'The Meteor Book',
url: 'http://themeteorbook.com'
}
];
Template.postsList.helpers({
posts: postsData
});
~~~
<%= caption "client/templates/posts/posts_list.js" %>
코드가 올바로 작성되었다면 다음과 같는 모습을 브라우저에서 볼 수 있을 것이다:
<%= screenshot "3-1", "정적 데이터로 작성한 첫 템플릿" %>
<%= commit "3-1", "기본적인 posts 목록 템플릿과 정적 데이터를 추가했다." %>
우리는 여기서 두 가지 작업을 한다. 첫째, `postsData` 배열에 초기 데이터를 지정했다. 이 데이터는 보통은 데이터베이스에서 오지만, 그 방법을 아직 다루지 않았기 때문에(다음 장까지 기다려라) 정적 데이터로 "임시 처리"하고 있다.
둘째, 우리는 `Template.postsList.helpers()` 함수를 사용하여 `posts`라는 이름의 템플릿 헬퍼를 정의한다. 이 헬퍼는 단순히 `postsData` 배열을 리턴한다.
기억하겠지만, 우리는 `postsList` 템플릿에서 `posts`라는 이름의 헬퍼를 사용하고 있다:
~~~html
<template name="postsList">
<div class="posts">
{{#each posts}}
{{> postItem}}
{{/each}}
</div>
</template>
~~~
<%= caption "client/templates/posts/posts_list.html" %>
`posts` 헬퍼를 정의함으로써 템플릿을 사용할 수 있다. 따라서 템플릿은 `postsData` 배열을 반복처리하고, 그 안에 들어있는 각 배열 객체를 `postItem` 템플릿으로 보낸다.
<%= commit "3-1", "기본적인 posts 목록 템플릿과 정적 데이터를 추가했다." %>
### `domain` 헬퍼
유사한 방법으로, 이제 우리는 `post_item.js`를 만들어 `postItem` 템플릿의 로직을 처리한다:
~~~js
Template.postItem.helpers({
domain: function() {
var a = document.createElement('a');
a.href = this.url;
return a.hostname;
}
});
~~~
<%= caption "client/templates/posts/post_item.js" %>
이 경우 `domain` 헬퍼의 값은 배열이 아니고, 익명 함수이다. 이러한 패턴은 매우 흔히 나타난다. 그리고 이전의 단순화된 더미 데이터 예제에 비교해서 훨씬 일반적이다 (그리고 더 유용하다).
<%= screenshot "3-2", "Displaying domains for each links." %>
`domain` 헬퍼는 URL을 가져와서 약간의 JavaScript 마술을 통해서 그 URL의 도메인 값을 리턴한다. 그런데, 이 URL은 처음에 어디서 왔을까?
이 질문의 답을 알려면 `posts_list.html` 템플릿으로 돌아가야 한다. `{{#each}}` 블록 헬퍼는 그 배열을 반복할 뿐 아니라 **이 블럭 내부의 `this`의 값을 그 반복되는 객체에 지정한다**.
이것은 `{{#each}}` 태그 내부에서 각 `post`는 순차적으로 `this`로 지정되며, 그 안에 포함된 템플릿의 매니저인 (`post_item.js`) 내부에서 내내 사용할 수 있다는 의미이다.
이제 `this.url`이 어떻게 현재 post의 URL을 리턴하는 지 알았다. 또한, `post_item.html` 템플릿 내부에서 `{{title}}`과 `{{url}}`을 사용하면, 미티어가 이것이 `this.title`과 `this.url`을 의미한다는 것을 인지하여 그 올바른 값을 리턴한다.
<%= commit "3-2", "`postItem`의 `domain` 헬퍼 설정." %>
<% note do %>
### JavaScript 마술
이것이 미티어에 특정된 것은 아니지만, 여기서 위의 "JavaScript 마술"에 대하여 설명하고 넘어간다. 먼저 빈 anchor (`a`) HTML 엘리먼트를 생성하고 이를 메모리에 저장한다.
그리고 그 `href` 속성을 현재 post의 URL로 지정한다(우리가 본 바와 같이, 헬퍼에서 `this`는 현재 작동중인 객체이다).
마지막으로, `a` 엘리먼트의 `hostname` 속성을 사용하여 URL에서 도메인 이름만을 추출한다.
<% end %>
올바르게 따라왔다면, 브라우저에서 목록을 볼 수 있을 것이다. 이 목록은 정적인 데이터일뿐, 아직은 미티어의 실시간 기능을 이용한 것은 아니다. 다음 장에서 이를 바꾸는 방법을 보게 될 것이다!
<% note do %>
### Hot Code Reload
독자는 파일이 변경될 때마다 브라우저 창을 수동으로 새로고침할 필요가 없었다는 사실을 눈치챘을 지 모르겠다.
이것은 미티어가 프로젝트 디렉토리에 있는 모든 파일을 추적하기 때문이다. 그리고 그 파일들 중에 하나라도 변경이 감지되면 브라우저를 자동으로 새로고침한다.
미티어의 hot code reload는 매우 똑똑해서 두 새로고침 사이의 애플리케이션의 상태도 유지한다!
<% end %>