Meteor로 만드는 modern web application

585
-1

Published on

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
585
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
5
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Meteor로 만드는 modern web application

  1. 1. Meteor로 만드는 Modern Web Application 이재호 (Founder of Appsoulute) jhlee@appsoulute.com http://github.com/acidsound http://spectrumdig.blogspot.com @acidsound
  2. 2. Meteor application create 1. npm install -g meteorite 2. mrt create sogon2x
  3. 3. Meteor application launch 1. cd sogon2x 2. mrt 3. http://localhost:3000
  4. 4. 구현 목표 관심사 Page단위 SNS 서비스 1. 2. 3. 4. 5. 6. 7. 8. 9. 화면 생성 포스트 입력 저장 입력 이벤트 처리 포스트 정렬 및 페이지 지정 페이지별 라우터 생성 스마트 패키지 이용 시간 처리 사용자 계정 적용 페이지별 가입/탈퇴 처리 마이페이지 구현
  5. 5. Let's rock 백문불여일타 百聞不如一打
  6. 6. JS >> client directory if (Meteor.isClient) { } >> server directory if (Meteor.isServer) { Meteor.startup(function () { // code to run on server at startup }); }
  7. 7. HTML/시작 <body> {{> head}} {{> main}} </body> <template name="head"> </template> <template name="main"> </template>
  8. 8. Head 1/2 <template name="head"> <div class="navbar navbar-fixed-top"> <div class="navbar-inner"> <div class="container"> <!-- .btn-navbar is used as the toggle for collapsed navbar content --> <a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse"> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </a>
  9. 9. Head 2/2 <!-- Be sure to leave the brand out there if you want it shown --> <a class="brand" href="/">Sogon</a> <!-- Everything you want hidden at 940px or less, place within here --> <div class="nav-collapse collapse"> <!-- .nav, .navbar-search, .navbar-form, etc --> </div> </div> </div> </div> </template>
  10. 10. Main template <template name="main"> <div class="container"> <ul class="unstyled"> <li class="row"> <h2>nobody's Page</h2> <form class="form-inline"> <textarea class="postText input-block-level" placeholder="Press shift+enter to post"></textarea> <button type="reset" class="btn pull-right"><i class="icon-trash" /></button> <button type="submit" class="btn-primary submit btn pull-right"><i class="icon-white icon-pencil"/></button> </form> </li> </ul> </div>
  11. 11. Post template <li class="row post"> <div class="postHead"><span class="label labelsuccess author">User</span><span class="badge timeAgo badge-info pull-right">Now</span> </div> <div class="postBody"> <pre>Tell me something 어서 말을 해.<span class="pull-right label label-important tags">page </span></pre> </div> </li>
  12. 12. CSS /* CSS declarations go here */ .form-inline button { margin-top: 5px; margin-left: 5px; } form { margin-bottom: 50px; } /* fixed top Scroll */ body { padding-top: 60px; } @media (max-width: 979px) { body { padding-top: 0; }
  13. 13. Posts template {{#each posts}} <li class="row post"> .... </li> {{/each}} * 반복 구간
  14. 14. Posts Template.main.posts=function() { return [ { text : 'First post' } ]; }
  15. 15. Posts collection var Posts = new Meteor.Collection ('posts');
  16. 16. Posts Template.main.posts=function() { return Posts.find(); } > Posts.insert({'text':'First Post'});
  17. 17. Client-side security package 제거 mrt remove insecure 거칠게 구현하고 Scaffold 빼내기의 반복 Posts.insert({'text':'say something'}); "f595d61e-fad3-4a33-8a19-cfc667e5b672" insert failed: Access denied
  18. 18. Call/Method Meteor.methods({ "postText": function(text) { if(text) { Posts.insert({'text':text}); } } }); > Meteor.call('postText', 'say something');
  19. 19. Event Handling Template.main.events({ 'submit': function () { var input = $('.postText'); Meteor.call('postText',input.val(), function(err,result) { sendSubmit으 로 refactoring if(err) throw 'server error'; }); input.val(''); return false; }, 'keydown .postText':function (e) { return (e.shiftKey && e.which === 13) && sendSubmit() || true; } });
  20. 20. Session Session.set('page', '...'); Template.main.pageTitle=function() { return Session.get('page'); } {{#if pageTitle}} <h2>{{pageTitle}}'s Page</h2> {{/if}}
  21. 21. Page Call/Method Meteor.call('postText', input.val(), Session.get ('page'), function(... Meteor.methods({ "postText": function(text, page) { if(text) { Posts.insert({'text':text, 'created_at': Date. now(), 'page':page});
  22. 22. Page template {{#each posts}} <li class="row post"> <div class="postHead"><span class="label author" >User</span><span class="badge timeAgo pull-right" >Now</span> </div> <div class="postBody"> <pre>{{{text}}}<span class="pull-right label tags"> {{page}} </span></pre> </div> </li>
  23. 23. Subscribe/Publish mrt remove autopublish Meteor.autosubscribe(function() { Meteor.subscribe('posts', Session.get('page')); }); Template.main.posts=function(){ return Posts.find({}, {sort:{created_at:-1}}); }; Meteor.publish('posts', function (page) { return Posts.find({page:page}, {sort:{created_at:-1}}); });
  24. 24. Router mrt add router <body> {{> head}} {{renderPage}} </body>
  25. 25. 비 로그인 시 Title 추가 <template name="title"> <div class="container hero-unit"> <h1>Hello Sogon!</h1> <p> Simple and Robust SNS </p> <button class="btn btn-info pull-right">Read More..</button> </div>
  26. 26. Router 정의 Meteor.Router.add({ '/':function() { Session.set('page',''); return 'title'; }, '/page/:page':function(args) { Session.set('page',args[0]); return 'main'; } })
  27. 27. Moment 시간을 트위터처럼 a few seconds ago, 10 hours ago
  28. 28. Moment 설치 mrt add moment > moment().from() "a few seconds ago" > moment(Date.now()-60000).from() "a minute ago"
  29. 29. timeago helper Handlebars.registerHelper('timeago',function (time) { return moment(time).from(); }); <span class="badge pull-right"> {{timeago created_at}} </span>
  30. 30. Account <template name="head"> .... <ul class="nav pull-right"> <li> <a href="#">{{loginButtons}}</a> </li> </ul>
  31. 31. User Collection > Meteor.user() // 현재 접속 유저 * login 이전 null null * user/password login id : "<UUID>" emails : Array * facebook login iid : "<UUID> profile : name : <User Name>
  32. 32. Post with User() Meteor.methods({ "postText": function(text, page) { if(text && page && Meteor.user()) { Posts.insert({'text':text, 'page':page, 'author': Meteor.user(), 'created_at': Date.now() }); } else { throw "access denied"; } } });
  33. 33. Post template <li class="row post"> <div class="postHead"><span class=" label label-success author">{{author.profile. name}}</span><span class="badge timeAgo pull-right">{{timeago created_at}}</span>
  34. 34. Form with User() * template main {{#if currentUser}} <li class="row"> <form class="form-inline"> <textarea class="postText input-block-level" placeholder="shift+enter to post.."></textarea> <button type="reset" class="btn pull-right"><i class=" icon-trash"/></button> <button type="submit" class="btn-primary submit btn pull-right"><i class="icon-white icon-pencil"/></button> </form> </li>
  35. 35. Subscribers 구조 JSON Key/Value 구조 user() ㄴ profile ㄴ subscribers ㄴ page1 ㄴ timestamp ㄴ page2 ㄴ timestamp >> 가입 여부 확인 !!Subscribers['page1'] -> 있으면 true 없으면 null이니까 false >> 검색 Posts.find({page: {$in : [ 유저가 가입한 Page들의 이름 Array ]});
  36. 36. Method Subscribe/Unsubscribe "subscribe": function(page) { var subscribers={}; subscribers["profile.subscribers."+page]={ dateTime:Date.now() }; Meteor.users.update(this.userId, {$set:subscribers}); }, "unsubscribe": function(page) { var subscribers={}; subscribers["profile.subscribers."+page]=false; Meteor.users.update(this.userId, {$unset:subscribers}); }
  37. 37. Helper Subscribe/Unsubscribe Template.main.helpers({ 'isSubscribe': function (subscribers) { return subscribers && subscribers[Session. get('page')]; } });
  38. 38. Template Subscribe/Unsubscribe <h2>{{pageTitle}}'s page {{#if currentUser}} {{#unless isSubscribe currentUser.profile. subscribers}} <button class="btn btn-primary subscribe"> Subscribe </button> {{else}} <button class="btn btn-inverse unsubscribe"> Unsubscribe </button> {{/unless}}
  39. 39. Event subscribe/unsubscribe 'click .subscribe' : function () { Meteor.call('subscribe', Session.get('page')) }, 'click .unsubscribe' : function () { Meteor.call('unsubscribe', Session.get ('page')) }
  40. 40. Posts collection subscribe Page 에서 볼때 page 기준 유저의 MyPage 에선 User 하는 기준으로 following Meteor.autosubscribe(function() { Meteor.subscribe('posts', Session.get ('page'), Meteor.user()); });
  41. 41. Posts collection publish Meteor.publish('posts', function (page, user) { return Posts.find( page && {page:page} || user && { page:{$in: _.map(user.profile && user.profile.subscribers, function(v,k) { return k; }) } } || {}, {sort:{created_at:-1}} ); });
  42. 42. MyPage 내가 Subscribe 한 곳의 글을 모아볼 수 있게 template main 에서 {{#each posts}} 부분을 posts template 으로 분리
  43. 43. MyPage template <template name="posts"> {{#each posts}} ... {{/each}} </template> <template name="mypage"> <div class="container"> <ul class="unstyled"> <li> <h2>My page</h2> </li> {{> posts}} </ul> </div> </template>
  44. 44. Posts Collection Template.main.posts=function(){ return Posts.find({}, {sort:{created_at:-1}}); }; 에서 Template.posts.posts=function(){ return Posts.find({}, {sort:{created_at:-1}}); }; 로 변경
  45. 45. MyPage Router Meteor.Router.add({ '/':function() { Session.set('page',''); return 'title'; }, '/page/:page':function(args) { Session.set('page', args[0]); return 'main'; }, '/mypage':function() { Session.set('page',''); return 'mypage'; } });
  46. 46. MyPage filter Meteor.Router.filters({ 'login' : function() { if (Meteor.user()) { Session.set('page', ''); return 'mypage'; } else { return 'title'; } } }); Meteor.Router.filter('login', {only: 'title'});
  47. 47. Posts template link <template name="posts"> {{#each posts}} <li class="row post"> <div class="postHead"><span class="label label-success author">{{author. profile.name}}</span><span class="badge badge-info timeAgo pull-right"> {{timeago created_at}}</span> </div> <div class="postBody"> <pre>{{{text}}} <a href="/page/{{page}}"><span class="pull-right label label-important tags">{{page}} </span></a></pre> </div> </li> {{/each}} </template>
  48. 48. ONE MORE THING?
  49. 49. less? mrt add less > sogon.css를 sogon.less로 변경 http://www.bootstrapcdn.com/#bootswatch 중 //netdna.bootstrapcdn.com/bootswatch/2.1.0 /amelia/bootstrap.min.css 를 적용해보자. > theme를 적용해보자! @import "http://netdna.bootstrapcdn. com/bootswatch/2.1.0/united/bootstrap.min. css";
  50. 50. FORK ME!! PULL ME!! http://github.com/acidsound/sogon2x
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×