기본 콘텐츠로 건너뛰기

vulcanJS - 8. Posts View 상세보기

지금까지 Posts 의 목록을 조회하고 생성하는 걸 해보았습니다.
목록을 봤으니 목록 중 하나를 선택하여 상세 내용을 보고 수정/삭제를 할 수 있으면 데이터 처리 한 바퀴를 온전히 돌 수 있다고 볼 수 있습니다.

먼저, 상세보기를 만들어봅시다.
시작은 Component부터 하도록 하지요.
PostsViewComponent라는 걸 만들어 봅시다.
$ vulcan g component
? Package name spectrum-simplebb
? Component name PostsViewComponent
? Component type Class Component
? Register component Yes
   create packages/spectrum-simplebb/lib/components/PostsViewComponent.jsx
 conflict packages/spectrum-simplebb/lib/components/index.js
? Overwrite packages/spectrum-simplebb/lib/components/index.js? overwrite this and all others
    force packages/spectrum-simplebb/lib/components/index.js
슥삭 만들고 Route를 만들어 URL을 통한 접근을 시도해봅니다.
Route이름은 postsView라고 하고 /posts/:_id 와 같은 형식으로 접근하게 해보죠.
$ vulcan g route
? Package name spectrum-simplebb
? Route name postsView
? Route path /posts/:_id
? Component name PostsViewComponent
? Layout name
 conflict packages/spectrum-simplebb/lib/modules/routes.js
? Overwrite packages/spectrum-simplebb/lib/modules/routes.js? overwrite
    force packages/spectrum-simplebb/lib/modules/routes.js
전에 만들었던 home하곤 다르게 조회물에 따라 달라지는 주소를 구현해야합니다.
Vulcan은 react-router(https://reacttraining.com/react-router/web/guides/philosophy)를 사용하니 깊이 알고 싶은 분들은 해당 문서를 참조 바랍니다.
아마 서버쪽에서 express나 프론트에서 blaze, backbone 등에서 router류를 써보셨으면 금방 눈치채신 분들도 있을테지만
/posts/:_id 에서 뒷부분인 :_id는 /posts/로 시작하고 그 다음의 임의의 문자는 _id를 받아올 수 있다는 의미입니다.
즉, /posts/a10fc8a 인 경우 _id로 a10fc8a를 받아오겠지요.
:으로 시작하는 부분은 변할 수 있다는 점을 기억하도록 합니다.

전에도 말씀 드렸듯이 React의 문제인지 Package manager의 문제인지는 모르겠는데 새로운 Component 등록 후에 잘 못찾아가는 경우가 있습니다.
그럴 땐 다시 재시동을 해봅시다. 서버 중지 후 npm start 혹은 yarn start.

http://localhost:3000/posts/1000 로 한번 접근해봅니다.
뭔가 나온 다면 성공입니다.
http://localhost:3000/posts 로 접근하면 Sorry, we couldn't find what you were looking for. 라고 나올지도 모릅니다.
정상입니다. /posts 인 route를 만든 적 없으니까요.
실제로 _id가 잘 넘어오는지 관찰하기 위해 PostsViewComponent를 약간 수정해봅니다.
import React, { Component } from 'react';
import { registerComponent } from 'meteor/vulcan:core';
class PostsViewComponent extends Component {
  render () {
    return (
      <div>
        _id: {this.props.params._id}
      </div>
    );
  }
}
registerComponent('PostsViewComponent', PostsViewComponent);
export default PostsViewComponent;
그리고 다시 http://localhost:3000/posts/1000 접근.
route: 100
브라우저에서 이렇게 나오면 오케이.
그러면 실제 _id를 넣어서 구현도 해봅시다.
한번 더 graphiql 연습을!
http://localhost:3000/graphiql 에 가서 
{
  PostsList(terms:{}) {
    _id
    createdAt
    title
    body
  }
}
이렇게 넣고 조회해보니. 제 경우엔
{
  "data": {
    "PostsList": [
      {
        "_id": "ve6pEfkGzr6YpwbKY",
        "createdAt": "2017-08-28T17:57:27.415Z",
        "title": "first thing",
        "body": "Booo"
      },
      {
        "_id": "8NhhY8TAoewp2hPXi",
        "createdAt": "2017-08-29T02:42:20.878Z",
        "title": "second thing",
        "body": "my favorite things"
      },
      {
        "_id": "6Sysi9DEaRccCqSqy",
        "createdAt": "2017-08-29T02:56:42.503Z",
        "title": "third thing",
        "body": "textarea is awesome.\nit's better than input when you need a multiline-text"
      }
    ]
  }
}
이렇게 나오네요. 첫번째 아이를 뽑아봅시다.
_id를 "ve6pEfkGzr6YpwbKY" 가지고 Component에서 조회하도록 해보죠.
http://localhost:3000/posts/ve6pEfkGzr6YpwbKY 에서 first thing 과 Booo 를 볼 수 있으면 되겠네요.
여러분들은 아마 저랑 다른 주소일테니 그대로 복붙하지 마시고 graphiql에서 조회해보시고 하세요.
withList(http://docs.vulcanjs.org/data-loading.html#withList)는 지난 시간에 사용해보았는데 이번엔 withDocument(http://docs.vulcanjs.org/data-loading.html#withDocument)를 사용합니다.

withDocument는 개별 건을 가져온다고 하는군요. 대신 documentId를 props에서 받아 사용한다고 하네요.
그럼 documentId를 component에 주입해봅시다.
PostsViewComponent는 scope 밖에서 이미 documentId를 받아올 수 없으니 documentId를 this.props로 받을 Component를 하나 만들어봅시다.
이름은 PostsSingleComponent 정도가 좋겠군요.
$ vulcan g component
? Package name spectrum-simplebb
? Component name PostsSingleComponent
? Component type Class Component
? Register component Yes
   create packages/spectrum-simplebb/lib/components/PostsSingleComponent.jsx
 conflict packages/spectrum-simplebb/lib/components/index.js
? Overwrite packages/spectrum-simplebb/lib/components/index.js? overwrite
    force packages/spectrum-simplebb/lib/components/index.js
이런 건 이제 10초도 안걸립니다.
결국 _id: {this.props.params._id} 로 받던 걸 _id: {this.props.params.documentId} 로 받으려고 이러는거죠.
그러면 지금 만든 PostsSingleComponent에 documentId를 주입하도록 PostsViewComponent를 이렇게 고쳐봅니다.
import React, { Component } from 'react';
import { registerComponent, withSingle, Components } from 'meteor/vulcan:core';
class PostsViewComponent extends Component {
  render () {
    return (
      <Components.PostsSingleComponent documentId={this.props.params._id} />
    );
  }
}
registerComponent('PostsViewComponent', PostsViewComponent);
export default PostsViewComponent;
역시 잘 안된다면 재시작.
PostsSingleComponent까지 잘 인계가 되면 한번 documentId도 찍어보구요.
import React, { Component } from 'react';
import { registerComponent } from 'meteor/vulcan:core';
class PostsSingleComponent extends Component {
  render () {
    return (
      <div>
        documentId: {this.props.documentId}
      </div>
    );
  }
}
registerComponent('PostsSingleComponent', PostsSingleComponent);
export default PostsSingleComponent;
뭔가 비슷한 코드가 뱅글뱅글도는 것 같지만 기분탓일 겁니다.
props에 documentId를 받아왔군요!
이제 withDocument를 써도 되겠네요.
withList하고 비슷한데 인자가 아니라 props로 받아오는게 좀 낯설고 재밌는 부분입니다.
this.props.document로 넘어온 값들을 최종 화면에 보여주기 위해 Posts schema도 가져오고 withDocument도 import 해봅시다.
이제 documentId는 필요없으니 바로 title을 받아봅시다.
import React, { Component } from 'react';
import { registerComponent, withDocument } from 'meteor/vulcan:core';
import Posts from "../modules/posts/collection.js";
class PostsSingleComponent extends Component {
  render () {
    return (
      <div>
        <h1>{this.props.document.title}</h1>
      </div>
    );
  }
}
registerComponent('PostsSingleComponent', PostsSingleComponent, [withDocument, {
  collection: Posts
}]);
export default PostsSingleComponent;
제목이 보이시나요?
이제 온전히 다 구현해 봅시다.
body도 넣어서 보여주죠. 멀티라인이 있으니까 편의상 <pre>를 써봅니다.
import React, { Component } from 'react';
import { registerComponent, withDocument } from 'meteor/vulcan:core';
import Posts from "../modules/posts/collection.js";
class PostsSingleComponent extends Component {
  render () {
    return (
      <div>
        <h1>{this.props.document.title}</h1>
        <pre>
          {this.props.document.body}
        </pre>
      </div>
    );
  }
}
registerComponent('PostsSingleComponent', PostsSingleComponent, [withDocument, {
  collection: Posts
}]);
export default PostsSingleComponent;
하면
멀티라인 본문도 잘 보입니다.
구현한 코드는 얼마 되지 않네요.

본문 목록으로 돌아가는 기능하고 본문 목록 중 하나를 클릭해서 여기로 들어오게끔 구현하는 걸 해야겠네요.
다음엔 route간을 이동하는 연결을 만들어봅시다.

댓글

이 블로그의 인기 게시물

cURL로 cookie를 다루는 법

http://stackoverflow.com/questions/22252226/passport-local-strategy-and-curl 레거시 소스를 보다보면 인증 관련해서 cookie를 사용하는 경우가 있는데 가령 REST 서버인 경우 curl -H "Content-Type: application/json" -X POST -d '{"email": "aaa@bbb.com", "pw": "cccc"}' "http://localhost/login" 이렇게 로그인이 성공이 했더라도 curl -H "Content-Type: application/json" -X GET -d '' "http://localhost/accounts/" 이런 식으로 했을 때 쿠키를 사용한다면 당연히 인증 오류가 날 것이다. curl의 --cookie-jar 와 --cookie 옵션을 사용해서 cookie를 저장하고 꺼내쓰자. 각각 옵션 뒤엔 저장하고 꺼내쓸 파일이름을 임의로 지정하면 된다. 위의 과정을 다시 수정해서 적용하면 curl -H --cookie-jar jarfile "Content-Type: application/json" -X POST -d '{"email": "aaa@bbb.com", "pw": "cccc"}' "http://localhost/login" curl -H --cookie jarfile "Content-Type: application/json" -X GET -d '' "http://localhost/accounts/" 이렇게 사용하면

MQTT 접속해제 - LWT(Last will and testament)

통신에서 중요하지만 구현이 까다로운 문제로 "상대방이 예상치 못한 상황으로 인하여 접속이 끊어졌을때"의 처리가 있다. 이것이 까다로운 이유는 상대방이 의도적으로 접속을 종료한 경우는 접속 종료 직전에 자신의 종료 여부를 알리고 나갈 수 있지만 프로그램 오류/네트웍 연결 강제 종료와 같은 의도치 않은 상황에선 자신의 종료를 알릴 수 있는 방법 자체가 없기 때문이다. 그래서 전통적 방식으로는 자신의 생존 여부를 계속 ping을 통해 서버가 물어보고 timeout 시간안에 pong이 안올 경우 서버에서 접속 종료를 인식하는 번거로운 방식을 취하는데 MQTT의 경우 subscribe 시점에서 자신이 접속 종료가 되었을 때 특정 topic으로 지정한 메시지를 보내도록 미리 설정할 수 있다. 이를 LWT(Last will and testament) 라고 한다. 선언을 먼저하고 브로커가 처리하게 하는 방식인 것이다. Last Will And Testament 라는 말 자체도 흥미롭다. 법률용어인데  http://www.investopedia.com/terms/l/last-will-and-testament.asp 대략 내가 죽으면 뒷산 xx평은 작은 아들에게 물려주고 어쩌고 하는 상속 문서 같은 내용이다. 즉, 내가 죽었을(연결이 끊어졌을) 때에 변호사(MQTT Broker - ex. mosquitto/mosca/rabbitMQ등)로 하여금 나의 유언(메시지)를 상속자(해당 토픽에 가입한 subscriber)에게 전달한다라는 의미가 된다. MQTT Client 가 있다면 한번 실습해보자. 여러가지가 있겠지만 다른 글에서처럼  https://www.npmjs.com/package/mqtt  을 사용하도록 한다. npm install mqtt --save 로 설치해도 되고 내 경우는 자주 사용하는 편이어서 npm install -g mqtt 로 전역설치를 했다. 호스트는 무료 제공하고 있는 test.mosquitto.org 를

MQTT Broker Mosquitto 설치 후 설정

우분투 기준 $ sudo apt-add-repository ppa:mosquitto-dev/mosquitto-ppa $ sudo apt-get update 하고 $ sudo apt-get install mosquitto 으로 설치하면 서비스까지 착실하게 올라간다. 설치는 간단한데 사용자를 만들어야한다. /etc/mosquitto/mosquitto.conf 파일에서 권한 설정을 변경하자. allow_anonymous false 를 추가해서 아무나 못들어오게 하자. $ service mosquitto restart 서비스를 재시작. 이제 사용자를 추가하자. mosquitto_passwd <암호파일 경로명> <사용자명> 하면 쉽게 만들 수 있다. # mosquitto_passwd /etc/mosquitto/passwd admin Password:  Reenter password:  암호 넣어준다. 두번 넣어준다. 이제 MQTT 약을 열심히 팔아서 Broker 사글세방 임대업을 하자.