기본 콘텐츠로 건너뛰기

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간을 이동하는 연결을 만들어봅시다.

이 블로그의 인기 게시물

ESP32 DevBoard 개봉기

오늘 드디어 손에 넣었다. ESP32 DevBoard!
Adafruit 에서 15개 한정 재입고 트윗을 보고 광속 결제.
그리고 1주일의 기다림. 사랑해요 USPS <3
알리를 이용하다보니 1주일 정도는 광속 배송임.
물론 배송비도 무자비함 -_ㅜ
15개 한정판 adafruit 발 dev board
그놈이 틀림없으렸다.
오오 강려크한 포스
ESP32_Core_board_V2라고 적혀있군요.
ESP32 맞구요. 네네. ESP32-D0WDQ6 라고 써있는데 D → Dual-core 0 → No internal flash W → Wi-Fi D → Dual-mode Bluetooth Q → Quad Flat No-leads (QFN) package 6 → 6 mm × 6 mm package body size 라고 함.
길이는 이정도
모듈크기는 이정도
코어는 6mm밖에 안해! 여기에 전기만 넣으면 BLE+WIFI!
밑에 크고 발 8개 달린 놈은 FM25Q32라고 32Mbit 플래시메모리
ESP8266 DevBoard 동생이랑 비교 크고 아름다운 레귤레이터랑 CP2102 USB Driver가 붙어있음.
ESP8266 DevBoard엔 CH340G 인데 확 작아졌네.
머리를 맞대어 보았음.
모듈크기는 아주 약간 ESP32가 더 큰데 워낙에 핀이 많고 촘촘함. ESP8266인 ESP12는 핀 간격이 2.00mm인데 비해
ESP32는 1.27mm 밖에 안함.
딱봐도 비교가 될 정도.
https://www.sparkfun.com/news/2017 크고 아름다운 Pinouts

ESP8266 보드랑 별로 안달라보인다.
http://www.silabs.com/products/mcu/pages/usbtouartbridgevcpdrivers.aspx#mac
에서 CP2102 드라이버를 설치하고
screen 으로 연결해보자.
내 경우엔 tty.SLAB_USBtoUART 로 잡혔다.
어디서 기본 속도가 115200bps 라고 들은 적이 있어서
screen /dev/tty.SLAB_USBtoUART …

즐거운 Online Prototyping Tool 들

jsbin, codepen, jsfiddle 이런 것들은 일단 생략. 너무 유명한 것들이라.

https://launchpad.graphql.com - node.js 기반 graphQL 연습장. 이것만으로도 충분히 훌륭한 백엔드
https://codesandbox.io/ npm 사용이 가능한 클라이언트 사이드 연습장. webpackbin이 너무 문제가 많아서 찾아본 것.

https://scrimba.com 이건 codesandbox+ asciinema(https://asciinema.org/) 같은 느낌인데 키 녹화와 음성 녹화 기능이 추가되었다. 다 좋은데 화살표 키로 빨리감기 뒤로감기 기능이 안되고 익스포트(youtube등)으로 지원이 없는게 아쉽다.

이 둘이 만나면? https://codesandbox.io/s/jvlrl98xw3?from-embed
뭐야 이거 무서워 하지마 ㄷㄷ;  graphql+react-native-web(부왘ㅋㅋ)

https://repl.it/languages 전통을 자랑하는 REPL 도구. 지원 언어 종류가 -_-;;;;;

https://tio.run/# repl.it? 장난함? 얘는 지원 언어가 무려 386종류. J랑 아희도 있다.

https://play.golang.org/ 즐거운 go playground. 소스 포멧팅 넘 좋아.

http://decaffeinate-project.org/repl/ 최고의 coffeescript REPL. 원래 용도는 coffee를 ecma6코드로 바꾸는 것이지만...

https://scaphold.io
https://www.graph.cool/ graphql backend service. scaphold.io는 설치도 필요없는 클라우드. graphcool은 호스팅+클라우드 다있음. 둘 다 막상막하. 푸쉬서버도 되고 뭐 미친득.

https://glitch.com/ gomix에서 결국 glitch로 안착.  node.js

https://www.shadertoy.com 잘하고 싶다! 쉐이다! 오디오도 된다!

http:/…

graphql 연습 /w launchpad

https://launchpad.graphql.com/mw9wkzv99
단순 전체쿼리+조건쿼리+추가

http://graphql.org/graphql-js/passing-arguments/
참고. random ID는 crypto 1.0.1 사용
  type Query {
    Members: [member]
    getMember(id: ID!): member
  }
  type member {
    id: ID!
    text: String
  }
  input memberInput {
    text: String
  }
  type Mutation {
    addMember(member: memberInput): member
  } SQL 정의. facebook 쪽은 스트링에 지지는 거 진짜 좋아하네. *.gql 파일이 있다고 하니 이해해주자.
resolver는 var buffer = [];
const resolvers = {
  Query: {
    Members: (root, args, context) => {
      return buffer;
    },
    getMember: (id)=> {
      return buffer.find(o=>o.id)
    }
  },
  Mutation: {
    addMember(_, {member}) {
      const mm = { ...member, id:randomBytes(10).toString('hex') };
      buffer.push(mm);
      return mm;
    }
  }
}; 평범 평범.
https://dev-blog.apollodata.com/tutorial-graphql-subscriptions-server-side-e51c32dc2951 다음으로 pub/sub 연습.
https://launchpad.graphql.com/xvn94n3ql   type Subscription {
    memberAdded: member
  } member가 added되는 순간을 감시. imp…