meteor2015 codelab
TRANSCRIPT
![Page 1: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/1.jpg)
MODERN WEB APPLICATIONWITH METEOR
이재호Appsoulute 대표
[email protected]://github.com/acidsound
http://spectrumdig.blogspot.kr
![Page 2: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/2.jpg)
INSTALL METEORLinux/OS X curl https://install.meteor.com/ | sh Windows https://install.meteor.com/windows
![Page 3: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/3.jpg)
첫 METEOR APP
• meteor로 시작하는 명령은 터미널이나 커맨드라인(시작>실행>cmd)에서 입력합니다.
• meteor create sogon2x
![Page 4: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/4.jpg)
APP 실행하기
• meteor run / meteor
![Page 5: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/5.jpg)
구현 목표관심사 기반 마이크로 블로깅 서비스
1. 화면생성
2. 포스트 입력
3. 이벤트 처리
4. 포스트 정렬
5. 사용자 계정
6. 구독/탈퇴
7. 대쉬보드
![Page 6: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/6.jpg)
백문불여일타(百聞不如一打)한타 한타 시작해봅시다
![Page 7: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/7.jpg)
TOOL
• 어떤 걸로 코드를 만드실 건가요?
• ATOM (무료 추천!)
• Sublime text (인기!)
• Webstorm (유료 최고!)
![Page 8: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/8.jpg)
JAVASCRIPT 구조CLIENT
if (Meteor.isClient) {
}SERVER
if (Meteor.isServer) {
Meteor.startup(function() {
// code to run on server at startup
});
}
사용자 브라우저에서실행합니다.
서버에서실행합니다.
![Page 9: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/9.jpg)
HTML TEMPLATE(mobile first!)
index.html
<head>
<title>sogon2x</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
{{> head}}
{{> main}}
</body>
head.html
<template name="head">
<h1>fixed header</h1>
</template>main.html
<template name="main">
<p>context</p>
</template>
emmet- meta:vp
![Page 10: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/10.jpg)
HEAD• https://atmospherejs.com/twbs/bootstrap
• meteor add twbs:bootstrap
• 적용 후 변화를 관찰
• navbar 사용http://bootstrapk.com/components/#navbar-brand-image
![Page 11: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/11.jpg)
HEAD - NAVBAR<template name="head">
<nav class="navbar navbar-default navbar-static-top">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="#">Sogon2X</a>
</div>
</div>
</nav>
</template>
![Page 12: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/12.jpg)
HEAD - NAVBAR<template name="main">
<nav class="navbar navbar-default navbar-static-top">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="#">Sogon2X</a>
</div>
</div>
</nav>
</template> 상단 네비게이션 바
![Page 13: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/13.jpg)
HEAD - NAVBAR<template name="main">
<nav class="navbar navbar-default navbar-static-top">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="#">Sogon2X</a>
</div>
</div>
</nav>
</template>기본 색상
navbar-inverse도 시도
![Page 14: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/14.jpg)
HEAD - NAVBAR<template name="main">
<nav class="navbar navbar-default navbar-static-top">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="#">Sogon2X</a>
</div>
</div>
</nav>
</template>상단 고정 (optional)
![Page 15: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/15.jpg)
HEAD - NAVBAR<template name="main">
<nav class="navbar navbar-default navbar-static-top">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="#">Sogon2X</a>
</div>
</div>
</nav>
</template> 컨테이너
http://bootstrapk.com/css/#overview-container
![Page 16: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/16.jpg)
HEAD - NAVBAR<template name="main">
<nav class="navbar navbar-default navbar-static-top">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="#">Sogon2X</a>
</div>
</div>
</nav>
</template> Header 영역
![Page 17: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/17.jpg)
HEAD - NAVBAR<template name="main">
<nav class="navbar navbar-default navbar-static-top">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="#">Sogon2X</a>
</div>
</div>
</nav>
</template> 로고 영역
![Page 18: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/18.jpg)
MAIN TEMPLATE
버튼 애드온을 사용하여 입력 창을 만듭니다.
http://bootstrapk.com/components/#input-groups-buttons
![Page 19: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/19.jpg)
MAIN TEMPLATE <div class="container"> <h2>Nobody's Page</h2> <form> <div class="input-group"> <input type="text" id="post" class="form-control" placeholder="Tell me something..."/> <div class="input-group-btn"> <button class="btn btn-primary"> <i class="glyphicon glyphicon-pencil"></i> Post </button> </div> </div> </form> </div>
![Page 20: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/20.jpg)
MAIN TEMPLATE <div class="container"> <h2>Nobody's Page</h2> <form> <div class="input-group"> <input type="text" id="post" class="form-control" placeholder="Tell me something..."/> <div class="input-group-btn"> <button class="btn btn-primary"> <i class="glyphicon glyphicon-pencil"></i> Post </button> </div> </div> </form> </div>
입력 그룹 http://bootstrapk.com/components/#input-groups
![Page 21: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/21.jpg)
MAIN TEMPLATE <div class="container"> <h2>Nobody's Page</h2> <form> <div class="input-group"> <input type="text" id="post" class="form-control" placeholder="Tell me something..."/> <div class="input-group-btn"> <button class="btn btn-primary"> <i class="glyphicon glyphicon-pencil"></i> Post </button> </div> </div> </form> </div> 폼 요소
![Page 22: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/22.jpg)
MAIN TEMPLATE <div class="container"> <h2>Nobody's Page</h2> <form> <div class="input-group"> <input type="text" id="post" class="form-control" placeholder="Tell me something..."/> <div class="input-group-btn"> <button class="btn btn-primary"> <i class="glyphicon glyphicon-pencil"></i> Post </button> </div> </div> </form> </div> 버튼 애드온
http://bootstrapk.com/components/#input-groups-buttons
![Page 23: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/23.jpg)
MAIN TEMPLATE <div class="container"> <h2>Nobody's Page</h2> <form> <div class="input-group"> <input type="text" id="post" class="form-control" placeholder="Tell me something..."/> <div class="input-group-btn"> <button class="btn btn-primary"> <i class="glyphicon glyphicon-pencil"></i> Post </button> </div> </div> </form> </div> 버튼 옵션
http://bootstrapk.com/css/#buttons-options
![Page 24: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/24.jpg)
MAIN TEMPLATE <div class="container"> <h2>Nobody's Page</h2> <form> <div class="input-group"> <input type="text" id="post" class="form-control" placeholder="Tell me something..."/> <div class="input-group-btn"> <button class="btn btn-primary"> <i class="glyphicon glyphicon-pencil"></i> Post </button> </div> </div> </form> </div> 아이콘
http://bootstrapk.com/components/#glyphicons
![Page 25: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/25.jpg)
POST TEMPLATE
• Main 아래 Post들의 목록을 열거하는 화면구성
• media를 사용하여 UI를 먼저 만든다.
• http://bootstrapk.com/components/#media-default
![Page 26: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/26.jpg)
POST TEMPLATE
• main template 아래에 {{> posts}} 를 추가하여 posts라는 템플릿을 붙여주도록한다.
<template name="main"> <div class="container">
….
{{> posts}}
</div>
</template>
![Page 27: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/27.jpg)
POST TEMPLATE
• Main 아래 Post들의 목록을 열거하는 화면구성
• media를 사용하여 UI를 먼저 만든다.
• http://bootstrapk.com/components/#media-default
![Page 28: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/28.jpg)
POST TEMPLATE<template name="posts"> <div class="media"> <div class="media-left"> <a href="#"> <img class="media-object" src="http://lorempixel.com/64/64/cats/" alt="nobody"> </a> </div> <div class="media-body"> <h4 class="media-heading">Master</h4> 집사야 내 밥은 어디있냐? </div> </div> <div class="media"> <div class="media-left"> <a href="#"> <img class="media-object" src="http://lorempixel.com/64/64/people/" alt="nobody"> </a> </div> <div class="media-body"> <h4 class="media-heading">Slave4U</h4> 배고파서 내가 먹었다. </div> </div> </template>
![Page 29: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/29.jpg)
POST TEMPLATE<template name="posts"> <div class="media"> <div class="media-left"> <a href="#"> <img class="media-object" src="http://lorempixel.com/64/64/cats/" alt="nobody"> </a> </div> <div class="media-body"> <h4 class="media-heading">Master</h4> 집사야 내 밥은 어디있냐? </div> </div> <div class="media"> <div class="media-left"> <a href="#"> <img class="media-object" src="http://lorempixel.com/64/64/people/" alt="nobody"> </a> </div> <div class="media-body"> <h4 class="media-heading">Slave4U</h4> 배고파서 내가 먹었다. </div> </div> </template>
반복구간
![Page 30: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/30.jpg)
POST TEMPLATE{{#each posts}}
….
{{/each}}
• 반복 구간 처리
![Page 31: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/31.jpg)
이제 코딩을 합시다.Let’s Do Some Coding!
![Page 32: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/32.jpg)
일단 이사 먼저!
• client 폴더를 만듭니다.
• 지금까지 만든 모든 html파일들을 client 아래로 이동합니다.
• 같은 곳에 posts.js를 만들어줍니다.
![Page 33: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/33.jpg)
POST TEMPLATE
가짜로 자료를 만듭니다.
posts.js 안에 반복 구간에 들어갈 값들을 JSON 형태로 만들어봅시다.
![Page 34: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/34.jpg)
POST TEMPLATETemplate.posts.helpers({ "posts": function() { return [ { author: { name: "Master", profile_image: "http://lorempixel.com/64/64/cats/" }, message: "집사야 내 밥은 어딨냐?" }, { author: { name: "Slave4U", profile_image: "http://lorempixel.com/64/64/people/" }, message: "배고파서 내가 먹었다." } ] } });
![Page 35: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/35.jpg)
POST TEMPLATE
• posts.html에 반복 구간을 정하고 값을 받을 helper들로 교체합니다.
![Page 36: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/36.jpg)
POST TEMPLATE<template name="posts"> <div class="media"> <div class="media-left"> <a href="#"> <img class="media-object" src="http://lorempixel.com/64/64/cats/" alt="nobody"> </a> </div> <div class="media-body"> <h4 class="media-heading">Master</h4> 집사야 내 밥은 어디있냐? </div> </div> </template>
![Page 37: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/37.jpg)
POST TEMPLATE<template name="posts"> {{#each posts}} <div class="media"> <div class="media-left"> <a href="#"> <img class="media-object" src="http://lorempixel.com/64/64/cats/" alt="nobody"> </a> </div> <div class="media-body"> <h4 class="media-heading">Master</h4> 집사야 내 밥은 어디있냐? </div> </div> {{/each}}</template>
![Page 38: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/38.jpg)
POST TEMPLATE<template name="posts"> {{#each posts}} <div class="media"> <div class="media-left"> <a href="#"> <img class="media-object" src="{{author.profile_image}}" alt="{{author.name}}"> </a> </div> <div class="media-body"> <h4 class=“media-heading”>{{author.name}}</h4> {{message}} </div> </div> {{/each}}</template>
![Page 39: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/39.jpg)
중간 결과물Mobile과 Desktop동일하게 나옵니까?
![Page 40: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/40.jpg)
CONNECT DB• lib/collection.js 에 추가
Posts = new Mongo.Collection('posts');
• client/server 양쪽에 적용
• 기존 posts.js 수정Template.posts.helpers({ "posts": function() { return Posts.find(); } });
![Page 41: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/41.jpg)
CONNECT DB• Browser Console에서 테스트
• Posts.insert({ author: { name: "Master", profile_image: "http://lorempixel.com/64/64/cats/" }, message: "집사야 내 밥은 어딨냐?"});
• Posts.find().fetch();
• 화면과 결과값을 확인
![Page 42: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/42.jpg)
SERVER METHOD보안이 필요한 시기
![Page 43: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/43.jpg)
REMOVE INSECURE
• meteor remove insecure
• insert failed: Access denied
• 사용자가 임의로 데이터 조작을 할 수 없음
![Page 44: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/44.jpg)
METHODS• server/methods.js - 서버에서만 insert
Meteor.methods({ "addPosts": function(obj) { Posts.insert({ author: { name: obj.name, profile_image: obj.profile_image }, message: obj.message }); } });
44
![Page 45: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/45.jpg)
CLIENT CALL
• Method.call 사용. 콘솔에서 테스트.Meteor.call("addPosts", { name: "Slave4U", profile_image: "http://lorempixel.com/64/64/people/", message: "배고파서 내가 다 먹었다."});
45
![Page 46: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/46.jpg)
EVENT HANDLING• Template.main.events({
"submit": function(event, template) { Meteor.call("addPosts", { name: "Slave4U", profile_image: "http://lorempixel.com/64/64/people/", message : template.find('#post').value }, function(err, result) { if (err) { throw(error); } else { console.log(result); template.find('#post').value = ""; } }); event.preventDefault(); } });
46
사용자 로그인과연동 필요
![Page 47: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/47.jpg)
EVENT HANDLING• Template.main.events({
"submit": function(event, template) { Meteor.call("addPosts", { name: "Slave4U", profile_image: "http://lorempixel.com/64/64/people/", message : template.find('#post').value }, function(err) { if (err) { throw(error); } else { console.log(result); template.find('#post').value = ""; } }); event.preventDefault(); } });
47
템플릿 안에서 post라는id를 가진 객체를 검색.그 값을 가져온다.
![Page 48: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/48.jpg)
EVENT HANDLING• Template.main.events({
"submit": function(event, template) { Meteor.call("addPosts", { name: "Slave4U", profile_image: "http://lorempixel.com/64/64/people/", message : template.find('#post').value }, function(err) { if (err) { throw(error); } else { template.find('#post').value = ""; } }); event.preventDefault(); } });
48
method call 후 오류처리
![Page 49: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/49.jpg)
EVENT HANDLING• Template.main.events({
"submit": function(event, template) { Meteor.call("addPosts", { name: "Slave4U", profile_image: "http://lorempixel.com/64/64/people/", message : template.find('#post').value }, function(err) { if (err) { throw(error); } else { template.find('#post').value = ""; } }); event.preventDefault(); } });
49
처리 성공 후 입력창 내용 삭제
![Page 50: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/50.jpg)
EVENT HANDLING• Template.main.events({
"submit": function(event, template) { Meteor.call("addPosts", { name: "Slave4U", profile_image: "http://lorempixel.com/64/64/people/", message : template.find('#post').value }, function(err) { if (err) { throw(error); } else { template.find('#post').value = ""; } }); event.preventDefault(); } });
50
기존 submit 이벤트를 금지페이지 이동이 안되도록 제한
![Page 51: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/51.jpg)
RESET DATABASE
• 서버 정지
• meteor reset
• 재기동
![Page 52: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/52.jpg)
ADDPOSTS• server/methods.js - 서버에서만 insert
Meteor.methods({ "addPosts": function(obj) { Posts.insert({ author: { name: obj.name, profile_image: obj.profile_image }, message: obj.message, createdAt: new Date() }); } });
52
반드시 서버 시간!
![Page 53: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/53.jpg)
SORT BY TIME DESC• 시간 역순 정렬. Server 시간 기준
• http://docs.meteor.com/#/full/sortspecifiers
• Posts.find({}, { sort: { createdAt: -1 } });
![Page 54: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/54.jpg)
POSTS HELPER• posts.js
Template.posts.helpers({ "posts": function() { return Posts.find({}, { sort: { createdAt: -1 } }); } });
정렬순서-1 : 내림차순1 : 오름차순
![Page 55: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/55.jpg)
SESSIONinsecure처럼
편리하지만 버려야할 것
계륵(鷄肋)…하지만 맛있다
![Page 56: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/56.jpg)
SESSION• Session의 장점전역으로 사용할 수 있다.브라우저 콘솔에서 사용이 자유롭다.서버 재시작 이후에도 값을 유지한다.
• Session의 단점전역으로 밖에 사용할 수 없다.Deprecated 예정
![Page 57: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/57.jpg)
SESSION 사용법
• Session의 읽기 Session.get('pageId');
• Session의 쓰기Session.set('pageId', 'catLover');
![Page 58: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/58.jpg)
SESSION 적용• main.js
Template.main.helpers({ 'page': function() { return Session.get('pageId'); } });
• main.html<template name="main"> <div class="container"> <h2>{{page}}'s Page</h2> …
![Page 59: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/59.jpg)
SESSION.SET
• 브라우저 콘솔에서Session.set('pageId', 'catLover')
• 바로 화면이 갱신되는 것을 관찰
• 어째서 이렇게 될까?Reactive Programming!http://docs.meteor.com/#/full/reactivity
![Page 60: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/60.jpg)
PUBLISH/SUBSCRIBE
• 보고싶은 것만 보고 싶어요.
• meteor remove autopublish
![Page 61: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/61.jpg)
AUTOPUBLISH?
• insecure 처럼 기본 설치 Meteor package
• Collection의 모든 내용을 서버로부터 가져온다.
• 하지만 우리는 page별로 따로따로 보고 싶다.
![Page 62: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/62.jpg)
BEFOREdefault Autopublish
![Page 63: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/63.jpg)
AFTERwith Publish/Subscribe
(https://www.discovermeteor.com/blog/understanding-meteor-
publications-and-subscriptions/)
![Page 64: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/64.jpg)
REMOVE AUTOPUBLISH
• meteor remove autopublish
• 어? 아무것도 안나와요?????
![Page 65: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/65.jpg)
DON’T PANIC• 원래대로 돌려놓아 봅시다.
• server/publish.js 추가Meteor.publish('getPage', function() { return Posts.find();});
• 브라우저 콘솔에서 확인해보자Meteor.subscribe('getPage');
![Page 66: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/66.jpg)
MANUAL SUBSCRIPTION
• main.js에 subscribe 추가Template.main.onCreated(function() { this.subscribe('getPage');});
• 원래대로 돌아왔다!
![Page 67: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/67.jpg)
PUB/SUB BASIC• Server에서 publish 한 데이터를...
Meteor.publish('publishName', function() { return YourCollection.find();});
• client에서 subscribe 에서 가져온다.Template.yourTemplate.onCreated(function() { this.subscribe('publishName');});
• 간단하죠?
![Page 68: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/68.jpg)
PUBLISH WITH PAGEID
• 조건을 주고 필요한 것들만 가져옵니다.(http://docs.meteor.com/#/full/selectors)
• server/publish.js 수정Meteor.publish('getPage', function(pageId) { return Posts.find({pageId: pageId});});
![Page 69: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/69.jpg)
SUBSCRIBE WITH PAGEID• client/main.js 수정
Template.main.helpers({ 'page': function() { return Session.get('pageId') || 'popular'; } });
• client/posts.js 수정Template.posts.onCreated(function() { this.subscribe('getPage', Session.get('pageId'));});
pageId가 없으면popular를 기본으로
pageId로 가입
![Page 70: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/70.jpg)
CALL WITH PAGEID• client/main.js 수정
Template.main.events({ "submit": function(event, template) { Meteor.call("addPosts", { name: "Slave4U", profile_image: "http://lorempixel.com/64/64/people/", pageId: Session.get('pageId'), message : template.find('#post').value }, function(err) { …
![Page 71: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/71.jpg)
METHOD WITH PAGEID• server/methods.js 수정
Meteor.methods({ "addPosts": function(obj) { Posts.insert({ author: { name: obj.name, profile_image: obj.profile_image }, pageId: obj.pageId, message: obj.message, createdAt: new Date() }); } })
![Page 72: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/72.jpg)
ROUTER어디로 가야하나요?
콘솔에서 Session.set은 그만
![Page 73: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/73.jpg)
KEYWORD별 POSTS• 같은 관심사를 가진 사람들끼리 이야기 할 수 있도록
POSTS를 분리
• 채널이나 대화방 같은 느낌
• Page라는 이름으로 분리
• URL로 구분 /page/keyword
![Page 74: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/74.jpg)
ROUTING
• Routing용 package 설치
• meteor add kadira:flow-router
![Page 75: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/75.jpg)
WARNING!
• Flow-router는 third-party package입니다.작성자가 꼭 업데이트를 보증하지 않습니다.
• 어떤 Router를 사용할지는 선택할 수 있습니다.
• Single Page Application에서 Routing(URL 경로)가 꼭 필수이진 않습니다.
![Page 76: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/76.jpg)
ROUTER 만들기• https://kadira.io/academy/meteor-routing-guide/content/
introduction-to-flow-router
• client/router.js 생성 (원래 이렇게 쓰는 건 아니에요!) FlowRouter.route('/page/:pageId', { name: 'main', action: function(params) { Session.set('pageId', params.pageId); } });
인자를 받아서 Session에 기록한다.
![Page 77: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/77.jpg)
ACCOUNTSmeteor add accounts-
password
사용자를 만들자
![Page 78: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/78.jpg)
ACCOUNTS PACKAGE• meteor add accounts-password
• http://docs.meteor.com/#/full/accounts_api
• Meteor.user() - 현재 접속중인 사용자
• Meteor.userId() - 접속 중인 사용자 ID
• Meteor.loginWithPassword(user, password, [callback]) 로그인하기, 성공 시 callback function 실행
• Meteor.logout() - 로그아웃
• Accounts.createUser(option, [callback]) - 사용자 생성
![Page 79: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/79.jpg)
ACCOUNTS PACKAGES
• meteor add accounts-passwordE-mail/password 인증
• meteor add ian:accounts-ui-bootstrap-3bootstrap3용 accounts UI
• Template에 {{> loginButtons}}
![Page 80: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/80.jpg)
LOGINBUTTONS<template name="head"> <nav class="navbar navbar-default navbar-static-top"> <div class="container"> <div class="navbar-header"> <a class="navbar-brand" href="#">Sogon2x</a> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target=".navbar-collapse"> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> </div> <div class="navbar-collapse collapse"> <ul class="nav navbar-nav navbar-right">{{> loginButtons}}</ul> </div> </div> </nav> </template> https://github.com/ianmartorell/meteor-
accounts-ui-bootstrap-3/#how-to-use
![Page 81: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/81.jpg)
LOGINBUTTONS<template name="head"> <nav class="navbar navbar-default navbar-static-top"> <div class="container"> <div class="navbar-header"> <a class="navbar-brand" href="#">Sogon2x</a> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target=".navbar-collapse"> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> </div> <div class="navbar-collapse collapse"> <ul class="nav navbar-nav navbar-right">{{> loginButtons}}</ul> </div> </div> </nav> </template>
모바일에서 접히는 영역
![Page 82: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/82.jpg)
LOGINBUTTONS<template name="head"> <nav class="navbar navbar-default navbar-static-top"> <div class="container"> <div class="navbar-header"> <a class="navbar-brand" href="#">Sogon2x</a> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target=".navbar-collapse"> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> </div> <div class="navbar-collapse collapse"> <ul class="nav navbar-nav navbar-right">{{> loginButtons}}</ul> </div> </div> </nav> </template>
loginButtons 삽입 (MAGIC!!)
![Page 83: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/83.jpg)
USERNAME• 사용자명 추가
• https://github.com/ianmartorell/meteor-accounts-ui-bootstrap-3/#custom-signup-options
• client/config.jsAccounts.ui.config({ extraSignupFields: [{ fieldName: "username", fieldLabel: "username", inputType: 'text' }]});
추가 입력 필드
![Page 84: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/84.jpg)
USER IN METHOD• server/methods.js 에 사용자 정보 적용
• 로그인 여부 검사 위해 check 사용 meteor add check
• username은 Meteor.user().username
• profile_image는 gravatar를 사용하자meteor add jparker :gravatar
![Page 85: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/85.jpg)
USER IN METHOD• client/main.js 에 Method.call 에 사용자 정보 제거
Template.main.events({ "submit": function(event, template) { Meteor.call('addPosts', { pageId: Session.get('pageId'), message: template.find("#post").value }, function(err, result) { if (err) { throw(err); } else { template.find('#post').value = ''; } }); event.preventDefault(); }
사용자 정보는 서버에서 추가하고 pageId와
Message만 전송
![Page 86: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/86.jpg)
USER IN METHOD• server/methods.js 에 사용자 정보 적용
Meteor.methods({ "addPosts": function(obj) { check(this.userId, String); Posts.insert({ author: { _id: this.userId, name: Meteor.user().username, profile_image: Gravatar.imageUrl(Meteor.user().emails[0].address)+"?d=retro" }, pageId: obj.pageId, message: obj.message, createdAt: new Date() }); } });
![Page 87: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/87.jpg)
USER IN METHOD• server/methods.js 에 사용자 정보 적용
Meteor.methods({ "addPosts": function(obj) { check(this.userId, String); Posts.insert({ author: { _id: this.userId, name: Meteor.user().username, profile_image: Gravatar.imageUrl(Meteor.user().emails[0].address, {d: "retro"}) }, pageId: obj.pageId, message: obj.message, createdAt: new Date() }); } });
로그인 여부 체크http://docs.meteor.com/#/full/check
![Page 88: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/88.jpg)
USER IN METHOD• server/methods.js 에 사용자 정보 적용
Meteor.methods({ "addPosts": function(obj) { check(this.userId, String); Posts.insert({ author: { _id: this.userId, name: Meteor.user().username, profile_image: Gravatar.imageUrl(Meteor.user().emails[0].address, {d: "retro"}) }, pageId: obj.pageId, message: obj.message, createdAt: new Date() }); } });
사용자 ID
![Page 89: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/89.jpg)
USER IN METHOD• server/methods.js 에 사용자 정보 적용
Meteor.methods({ "addPosts": function(obj) { check(this.userId, String); Posts.insert({ author: { _id: this.userId, name: Meteor.user().username, profile_image: Gravatar.imageUrl(Meteor.user().emails[0].address, {d: "retro"}) }, pageId: obj.pageId, message: obj.message, createdAt: new Date() }); } });
Accounts.ui.config에서 받은사용자 이름
![Page 90: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/90.jpg)
USER IN METHOD• server/methods.js 에 사용자 정보 적용
Meteor.methods({ "addPosts": function(obj) { check(this.userId, String); Posts.insert({ author: { _id: this.userId, name: Meteor.user().username, profile_image: Gravatar.imageUrl(Meteor.user().emails[0].address, {d: "retro"}) }, pageId: obj.pageId, message: obj.message, createdAt: new Date() }); } });
E-Mail 주소로 사용자 Image를 가져옴
![Page 91: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/91.jpg)
USER IN METHOD• server/methods.js 에 사용자 정보 적용
Meteor.methods({ "addPosts": function(obj) { check(this.userId, String); Posts.insert({ author: { _id: this.userId, name: Meteor.user().username, profile_image: Gravatar.imageUrl(Meteor.user().emails[0].address, {d: "retro"}) }, pageId: obj.pageId, message: obj.message, createdAt: new Date() }); } });
(선택사항) 등록된 이미지가 없을 때retro 아이콘을 임의로 생성
https://en.gravatar.com/site/implement/images/
![Page 92: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/92.jpg)
생성일 추가• posts.html
<template name="posts"> {{#each posts}} <div class="media"> <div class="media-left"> <a href="#"> <img class="media-object" src="{{author.profile_image}}" alt="{{author.name}}"> </a> </div> <div class="media-body"> <h5 class="media-heading">{{author.name}} - <i>{{createdAt}}</i> </h5> <div> {{message}} </div> </div> </div> {{/each}} </template>
![Page 93: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/93.jpg)
가독성이 떨어진다.좀 더 친근한 방법으로 표현할 수 없을까?
![Page 94: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/94.jpg)
MOMENT
• 글별 상대시간 표시
• meteor add momentjs:moment
![Page 95: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/95.jpg)
MOMENT
• Moment의 Time From을 사용한다.http://momentjs.com/docs/#/displaying/from/
• Template helper로 적용한다.http://docs.meteor.com/#/full/template_helpers
![Page 96: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/96.jpg)
생성일 추가• client/posts.js
Template.posts.helpers({ … "timeFrom": function(time) { return moment().from(time); } });
• posts.html 수정 … <h5 class="media-heading">{{author.name}} - <i>{{timeFrom createdAt}}</i> </h5> …
![Page 97: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/97.jpg)
REACTIVE살아있는 실시간 값
![Page 98: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/98.jpg)
FACEBOOK/TWITTER
• 별다른 행동을 하지 않았는데 가만히 보고 있으면...
• 알아서 시간이 변한다.
• Reactive Programming 을 활용해서 구현해보자.
![Page 99: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/99.jpg)
REACTIVE PROGRAMMINGDon’t imperate, Just delcare
https://en.wikipedia.org/wiki/Reactive_programming
![Page 100: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/100.jpg)
REACTIVE TIME• meteor add random 패키지 추가
• posts.jsTemplate.posts.onCreated(function() {… this.interval = Meteor.setInterval(function() { Session.set('live', Random.id()); }, 1000);});
• Session.set('live', ....) 하는 순간Session.get('live')가 helper 이나 autorun 같은 곳 안쪽에 있으면 전부 재실행한다.http://docs.meteor.com/#/full/reactivity
1초마다 live라는 키로 고유값을 생성
![Page 101: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/101.jpg)
REACTIVE TIME• posts.js
Template.posts.helpers({ … "timeFrom": function(time) { Session.get('live'); return moment().from(time); } });
• 이때 live의 값이 변경이 없으면 해당 구문을 실행하지 않는다!
live를 변경하면 timeFrom helper를 재실행
![Page 102: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/102.jpg)
REACTIVE COMPUTATION변경이 있을 때만 실행하여 효율적
![Page 103: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/103.jpg)
LOGIN 여부
• ClientMeteor.userId()
• Server this.userId()
• Template{{#if currentUser}}
![Page 104: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/104.jpg)
CURRENTUSER 적용• main.html - 로그인 사용자만 글을 쓸 수 있게
{{#if currentUser}} <form> <div class="input-group"> ........ </div> </form> {{/if}}
![Page 105: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/105.jpg)
FOLLOW/UNFOLLOW관심사 추적
![Page 106: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/106.jpg)
FOLLOW/UNFOLLOW
• main.html<h2>{{page}}'s Page {{#if currentUser}} {{#if isFollowing}} <button id="unfollow" class="btn btn-inverse">unfollow</button> {{else}} <button id="follow" class="btn btn-primary">follow</button> {{/if}} {{/if}} </h2>
접속여부 확인Follwing 여부
![Page 107: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/107.jpg)
FOLLOW/UNFOLLOW• main.js helper 구현사용자가 해당 토픽에 follow하고 있는지 검사 client에서 기본 접근 가능한 profile 객체를 사용 'isFollowing': function() { var followings = Meteor.user().profile.followings; return followings && followings[Session.get('pageId')];}
![Page 108: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/108.jpg)
FOLLOW/UNFOLLOW• main.js event 구현. follow/unfollow
Template.main.events({….. "click #follow": function() { Meteor.call('follow', Session.get('pageId')); }, "click #unfollow": function() { Meteor.call('unfollow', Session.get('pageId')); } });
![Page 109: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/109.jpg)
FOLLOW/UNFOLLOW• server/methods.js - Follow
"follow": function(pageId) { check(this.userId, String); var obj={}; obj["profile.followings."+pageId]={ createdAt: new Date() }; Meteor.users.update(this.userId, { $set: obj });},
• server/methods.js - Unfollow."unfollow": function(pageId) { check(this.userId, String); var obj={}; obj["profile.followings."+pageId]=""; Meteor.users.update(this.userId, { $unset: obj });}
사용자확인
![Page 110: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/110.jpg)
DASHBOARD
• 현재 사용자의 Follow한 Page를 모아 보는 기능
• Feeling Lucky - 무작위 포스트 이동 기능
![Page 111: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/111.jpg)
DASHBOARD• 홈 디렉토리 이동 시 Dashboard로
• head.html<a class="navbar-brand" href="/">Sogon2x</a>
• / 일때 pageId를 리셋
• client/router.jsFlowRouter.route('/', { action: function() { Session.set('pageId'); } });
![Page 112: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/112.jpg)
DASHBOARD• main.html 수정
• 페이지가 있으면 현재 페이지 (/page:pageId)없으면 Dashboard로 분기
• {{> post}} helper에 pageId 인자 추가
• <template name="main"> <div class="container"> {{#if page}} <h2>{{page}}'s Page …… {{> posts pageId=page}} {{else}} {{> dashboard}} {{/if}}
![Page 113: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/113.jpg)
DASHBOARD• main.js 수정
• {{> post}} helper에 pageId 인자 전달
• Template.main.helpers({ 'page': function() { return Session.get('pageId'); },
• default 제거
• main.html / main.js 수정
• 페이지가 있으면 현재 페이지 (/page:pageId)없으면 Dashboard로 분기
• <template name="main"> <div class="container"> {{#if page}} <div> <h2>{{page}}'s Page …… {{> posts pageId=page}} {{else}} {{> dashboard}} {{/if}}
![Page 114: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/114.jpg)
DASHBOARD• Template helper에서 받은 인자를 js에 적용
Session 에서 this.data.pageId로 변경
• posts.js 수정Template.posts.onCreated(function() { var pageId = this.data.pageId; pageId && this.subscribe('getPage', pageId); …Template.posts.helpers({ "posts": function () { return Posts.find({ pageId: Template.instance().data.pageId }, { …
this.data 로부터 상위템플릿의 인자를 받는다.
Template.instance는 this.data와 같다.
Scope 이유로 다르게 씀.
![Page 115: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/115.jpg)
DASHBOARD
• dashboard 화면 구성
• 필요한 데이터들을 Publish
• Reactive를 이용한 사용자 정보 변경 감지
![Page 116: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/116.jpg)
DASHBOARD• 운좋은 예감 - 무작위 Posts 추출전체 데이터 갯수-count()이용-를 기준으로 랜덤만큼 skip하고 limit을 이용해 1개만 값을 find한다.
• Meteor.publish('feelingLucky', function() { return Posts.find({}, { skip: Math.random()*Posts.find().count(), limit: 1 });});
![Page 117: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/117.jpg)
DASHBOARD
• dashboard 생성 시 feelingLucky 를 구독(subscribe)한다.
• client/dashboard.js 생성 후Template.dashboard.onCreated(function() { this.subscribe('feelingLucky');});
![Page 118: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/118.jpg)
DASHBOARD• helper 정보 - luckyPage / pages
• dashboard.jsTemplate.dashboard.helpers({ 'luckyPage': function() { var post = Posts.findOne() return post && post.pageId; }, 'pages': function() { var result = []; for (var i in Meteor.user().profile.followings) { result.push({ pageId: i }); } return result; } });
posts가 없을 때 오류 방지
![Page 119: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/119.jpg)
DASHBOARD• helper 정보 - luckyPage / pages
• dashboard.jsTemplate.dashboard.helpers({ 'luckyPage': function() { var post = Posts.findOne() return post && post.pageId; }, 'pages': function() { var result = []; for (var i in Meteor.user().profile.followings) { result.push({ pageId: i }); } return result; } });
following 정보를 가져온다.
![Page 120: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/120.jpg)
DASHBOARD• helper 정보 - luckyPage / pages
• dashboard.jsTemplate.dashboard.helpers({ 'luckyPage': function() { var post = Posts.findOne() return post && post.pageId; }, 'pages': function() { var result = []; for (var i in Meteor.user().profile.followings) { result.push({ pageId: i }); } return result; } });
pageId로 배열로 밀어넣는다.
![Page 121: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/121.jpg)
DASHBOARD• 화면 구성 - 사용자 여부에 따라 Feeling lucky와 최근 Posts를 나눠서 보여준다.
• dashboard.html<template name="dashboard"> <div class="well"> <h2>Welcome to Sogon</h2> <p>What do you want to talk about?</p> <a href="/page/{{luckyPage}}" class="btn btn-primary">Feeling lucky</a> </div> {{#if currentUser}} <h2>Recent Posts</h2> {{#each pages}} <h3><a href="/page/{{pageId}}">{{pageId}}</a></h3> {{> posts pageId=pageId}} {{/each}} {{/if}} </template>
운좋은 예감(랜덤링크)
사용자 정보가 “있으면”following 중인 page들 목록
![Page 122: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/122.jpg)
더 생각해 볼 것들
![Page 123: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/123.jpg)
더 좋은 서비스를 위해• MongoDB Operator의 사용. (ex: $addToSet, $pull 등)
• OAuth를 사용한 외부 서비스(페이스북/네이버/카카오) 로그인 연동
• 수정/삭제 기능
• 외부 공유와 검색엔진 최적화
• iOS/Android Hybrid Apps 제작
• Deploy …
![Page 124: Meteor2015 codelab](https://reader031.vdocument.in/reader031/viewer/2022020314/58e743b81a28ab49038b60d1/html5/thumbnails/124.jpg)
참고 사이트• https://github.com/MeteorKorea/meteor2015codelab본 문서의 소스 코드 github 저장소
• http://meteorjs.rk Meteor Korea
• http://www.meetup.com/Meteor-SeoulMeteor Seoul Meetup 모임
• http://kr.discovermeteor.com/Discover Meteor 한글
• https://www.facebook.com/groups/meteorschool/Facebook Meteor School