<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-8182501247262747119</id><updated>2026-08-11T18:54:46.756+09:00</updated><category term="meteor"/><category term="javascript"/><category term="node.js"/><category term="coffeescript"/><category term="android"/><category term="vulcan"/><category term="tutorial"/><category term="vulcanjs"/><category term="socketcluster"/><category term="DDP"/><category term="iOS"/><category term="RxJS"/><category term="esp8266"/><category term="golang"/><category term="reactive"/><category term="IoT"/><category term="cordova"/><category term="firebase"/><category term="meteorite"/><category term="publish"/><category term="REST"/><category term="auth"/><category term="mqtt"/><category term="object"/><category term="OS X"/><category term="Rx"/><category term="XCode"/><category term="arduino"/><category term="coffee"/><category term="dotcloud"/><category term="git"/><category term="go"/><category term="html5"/><category term="mobile"/><category term="osx"/><category term="reactiveX"/><category term="subscribe"/><category term="tip"/><category term="ubuntu"/><category term="web"/><category term="BLE"/><category term="GraphQL"/><category term="HTML"/><category term="WiFi"/><category term="bluetooth"/><category term="chrome"/><category term="css"/><category term="driver"/><category term="functions"/><category term="google"/><category term="hosting"/><category term="js"/><category term="json"/><category term="less"/><category term="linux"/><category term="map"/><category term="mrt"/><category term="package"/><category term="prototype"/><category term="raspberryPi"/><category term="redmine"/><category term="reduce"/><category term="router"/><category term="swift"/><category term="vim"/><category term="webstorm"/><category term="NAVER"/><category term="PaaS"/><category term="RK3188"/><category term="UI"/><category term="ajax"/><category term="amok"/><category term="array"/><category term="async"/><category term="audio"/><category term="broker"/><category term="bug"/><category term="build"/><category term="cc2540"/><category term="cloud9"/><category term="cyclejs"/><category term="debugging"/><category term="docker"/><category term="drag and drop"/><category term="email"/><category term="expo"/><category term="facebook"/><category term="fiber"/><category term="flash"/><category term="forEach"/><category term="func"/><category term="function"/><category term="functional"/><category term="future"/><category term="game"/><category term="gpio"/><category term="graphiQL"/><category term="handlebars"/><category term="helper"/><category term="http"/><category term="java"/><category term="jquery"/><category term="lua"/><category term="mithril"/><category term="mongodb"/><category term="music"/><category term="nodemcu"/><category term="npm"/><category term="objective-C"/><category term="observable"/><category term="openShift"/><category term="pico-8"/><category term="radio"/><category term="react"/><category term="remote"/><category term="renoise"/><category term="route"/><category term="server"/><category term="smartForm"/><category term="storyboard"/><category term="subject"/><category term="svg"/><category term="tcp"/><category term="troubleshoot"/><category term="upload"/><category term="vr"/><category term="vue"/><category term="webserver"/><category term="websocket"/><category term="windows"/><category term="x64"/><category term="x86"/><category term="#constant"/><category term="#cookie"/><category term="#curl"/><category term=".net"/><category term="4.0.3"/><category term="404"/><category term="API"/><category term="Bluetooth LE"/><category term="CHIP"/><category term="CVS"/><category term="FRP"/><category term="FileReader"/><category term="GCM"/><category term="HFS"/><category term="HoC"/><category term="ICS"/><category term="IDEA"/><category term="IE"/><category term="ListAdapter"/><category term="MK809"/><category term="RethinkDB"/><category term="SEO"/><category term="SMD"/><category term="SPA"/><category term="SRP"/><category term="SoX"/><category term="WYSWYG"/><category term="access"/><category term="accounts"/><category term="actionscript"/><category term="aframe"/><category term="ai-thinker"/><category term="air"/><category term="algorithm"/><category term="amok.js"/><category term="angular"/><category term="angular.js"/><category term="animation"/><category term="arc"/><category term="arm"/><category term="armv7l"/><category term="as3"/><category term="atmosphere"/><category term="attributedString"/><category term="authentication"/><category term="babel"/><category term="backup"/><category term="basic"/><category term="binary"/><category term="bitcoin"/><category term="blaze"/><category term="blockchain"/><category term="boilerplate"/><category term="bootstrap"/><category term="bot"/><category term="build farm"/><category term="bundle"/><category term="c#"/><category term="c9"/><category term="calendar"/><category term="callback"/><category term="camera"/><category term="carousel"/><category term="cc2541"/><category term="chocolat"/><category term="circuit"/><category term="client"/><category term="clojure"/><category term="cloud9ide"/><category term="cloudatcost"/><category term="cloudflare"/><category term="cocoapod"/><category term="collection"/><category term="combineLatest"/><category term="conference"/><category term="connection"/><category term="contenteditable"/><category term="coroutine"/><category term="crosswalk"/><category term="crypto"/><category term="cycle.js"/><category term="datachannel"/><category term="daydream"/><category term="debian"/><category term="debug"/><category term="decaffeinate"/><category term="delete"/><category term="detail"/><category term="devOps"/><category term="devtool"/><category term="disconnect"/><category term="display"/><category term="distinct"/><category term="django"/><category term="dot2obj"/><category term="dualcore"/><category term="es6"/><category term="esp-1"/><category term="esp-12"/><category term="esp32"/><category term="esptool"/><category term="ethereum"/><category term="events"/><category term="everyAuth"/><category term="example"/><category term="exchange"/><category term="export"/><category term="filter"/><category term="flashing"/><category term="flatMap"/><category term="flex"/><category term="for"/><category term="fork"/><category term="fragment"/><category term="fuse"/><category term="garageband"/><category term="generator"/><category term="genymotion"/><category term="geolocation"/><category term="geth"/><category term="gps"/><category term="group by"/><category term="hashbang"/><category term="haxe"/><category term="head"/><category term="header"/><category term="headless"/><category term="hotkey"/><category term="https"/><category term="humidity"/><category term="i18n"/><category term="iconv"/><category term="ide"/><category term="intelliJ"/><category term="intro"/><category term="iron-router"/><category term="iteration"/><category term="javasciprecore"/><category term="javscript"/><category term="jsbin"/><category term="jsdom"/><category term="jsonp"/><category term="jsx"/><category term="kha"/><category term="kodestudio"/><category term="korea"/><category term="korean"/><category term="lcd"/><category term="led"/><category term="lg"/><category term="light"/><category term="load-cell"/><category term="loading"/><category term="loadtest"/><category term="lodash"/><category term="logic analyzer"/><category term="lux"/><category term="lwt"/><category term="mac"/><category term="microservice"/><category term="model"/><category term="module"/><category term="moment"/><category term="mongo"/><category term="mosca"/><category term="mosquitto"/><category term="mount"/><category term="multiple"/><category term="mutation"/><category term="mysql"/><category term="network"/><category term="new"/><category term="nginx"/><category term="no au"/><category term="no sample"/><category term="no vst"/><category term="node_modules"/><category term="nodebb"/><category term="non-text"/><category term="nosql"/><category term="nougat"/><category term="now"/><category term="nw.js"/><category term="nwjs"/><category term="oauth"/><category term="option"/><category term="password"/><category term="path"/><category term="pattern"/><category term="peatio"/><category term="phantomjs"/><category term="picker"/><category term="pipe"/><category term="pl2303"/><category term="player"/><category term="plugin"/><category term="pocketCHIP"/><category term="pressure"/><category term="process"/><category term="promise"/><category term="prototyping"/><category term="pull request"/><category term="python"/><category term="rapid prototyping"/><category term="raspberry"/><category term="raspbian"/><category term="raw"/><category term="react-native"/><category term="realtime"/><category term="recommend"/><category term="release"/><category term="rendering"/><category term="repeat"/><category term="repl"/><category term="requestAnimFrame"/><category term="requestAnimationFrame"/><category term="require"/><category term="restore"/><category term="rf-bm-s02"/><category term="rgb"/><category term="root"/><category term="routing"/><category term="safari"/><category term="saleae"/><category term="sample"/><category term="secured"/><category term="select"/><category term="sensor"/><category term="sequencing"/><category term="serial"/><category term="serialization"/><category term="session"/><category term="shooter"/><category term="shortcut"/><category term="smart package"/><category term="sourcemap"/><category term="spider"/><category term="splash"/><category term="starling"/><category term="stm32"/><category term="stream"/><category term="stresstest"/><category term="struct"/><category term="subscription"/><category term="table"/><category term="takeUntil"/><category term="task"/><category term="temperature"/><category term="terminal"/><category term="test board"/><category term="testing"/><category term="tests"/><category term="time"/><category term="tmux"/><category term="tracker"/><category term="tree"/><category term="truffles"/><category term="twitter"/><category term="typescript"/><category term="underscore"/><category term="unicode"/><category term="union"/><category term="unroot"/><category term="update"/><category term="usb"/><category term="user"/><category term="user-agent"/><category term="uwebsocket"/><category term="ux"/><category term="variable"/><category term="view"/><category term="virtualbox"/><category term="vm"/><category term="webRTC"/><category term="webview"/><category term="webvr"/><category term="weibo"/><category term="wemo"/><category term="wildcard"/><category term="xs"/><category term="yarn"/><category term="zero"/><category term="µWebsocket"/><category term="기초"/><category term="네이버"/><category term="코인거래소"/><title type='text'>spectrum learns to dig</title><subtitle type='html'>Meteor evangelist, IoT, Renoise, Lua, Javascript, Coffeescript</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://spectrumdig.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://spectrumdig.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default?start-index=26&amp;max-results=25&amp;redirect=false'/><author><name>spectrum</name><uri>http://www.blogger.com/profile/06120635035623961975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>274</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-8182501247262747119.post-5748849508160010291</id><published>2024-09-03T14:32:00.006+09:00</published><updated>2024-09-04T01:36:57.591+09:00</updated><title type='text'>NeoVim + LazyVim 사용기 - 자주 쓰는 단축 포함.</title><content type='html'>&lt;p&gt;주로 쓰는 컴터들이 죄다 리모트로만 연결해서 iSH나 Termux 같은 모바일 터미널에서 간단하게 개발환경을 쓰고 싶었음.&lt;/p&gt;&lt;p&gt;사실 vi (vim 도 아님) 으로 충분하지만 &lt;a href=&quot;https://github.com/yetone/avante.nvim&quot;&gt;avante.nvim&lt;/a&gt;&amp;nbsp;이라는 녀석이 Cursor 처럼 외부 LLM 모델을 쓸 수 있다고 해서 삽질을 시작함;;;&lt;/p&gt;&lt;p&gt;macOS 에서야 대충해도 되니 약간 삽질이 필요했던 windows 기준으로 기록.&lt;/p&gt;&lt;h4 style=&quot;text-align: left;&quot;&gt;NeoVim 설치&lt;/h4&gt;&lt;a href=&quot;https://winstall.app/apps/Neovim.Neovim&quot;&gt;https://winstall.app/apps/Neovim.Neovim&lt;/a&gt;&lt;br /&gt;winget 이 별도로 설치 없이 쓸 수 있는 package manager 라서 간단하게&lt;br /&gt;&lt;blockquote&gt;winget install --id=Neovim.Neovim&amp;nbsp; -e&lt;/blockquote&gt;&lt;p&gt;로 끝나는 점이 좋았음.&amp;nbsp;&lt;/p&gt;&lt;h4 style=&quot;text-align: left;&quot;&gt;LazyVim 설치&lt;/h4&gt;&lt;a href=&quot;https://www.lazyvim.org/installation&quot;&gt;https://www.lazyvim.org/installation&lt;/a&gt;&amp;nbsp;보고 Powershell 로 설치.&lt;br /&gt;깔끔하다!&lt;br /&gt;&lt;table cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;float: left;&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhlfkxZLd9lJ08oDc9iraYVtiOcRBVOvov2MGJB6epk_TzBPZAbiWO0siACDdOu6AEV7w5vOwc9Diob89SINQRt9DzneTiXPAT8OmU1kIpDIeDGa1TLhXABJi0CqZheTQ06SHmCkJXVaJBkLimf-W2FSOyFr43uq-NJKqCHKFr3BJuHieYE5bTN1RGvgsKL&quot; style=&quot;clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;&quot;&gt;&lt;img alt=&quot;lazyvim&quot; data-original-height=&quot;1339&quot; data-original-width=&quot;1780&quot; height=&quot;481&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhlfkxZLd9lJ08oDc9iraYVtiOcRBVOvov2MGJB6epk_TzBPZAbiWO0siACDdOu6AEV7w5vOwc9Diob89SINQRt9DzneTiXPAT8OmU1kIpDIeDGa1TLhXABJi0CqZheTQ06SHmCkJXVaJBkLimf-W2FSOyFr43uq-NJKqCHKFr3BJuHieYE5bTN1RGvgsKL=w640-h481&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;생각보다 빠르다!&lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4 style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/h4&gt;&lt;h4 style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/h4&gt;&lt;h4 style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/h4&gt;&lt;h4 style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/h4&gt;&lt;h4 style=&quot;text-align: left;&quot;&gt;nvim-treesitter 관련 오류 수정&lt;/h4&gt;&lt;div style=&quot;text-align: left;&quot;&gt;시작할 때 마다 nvim-treesitter 에서 오류 발생. 실행 속도도 느려지고 매우 불편!&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;다행스럽게도 reddit 에서 해결법을 알려줌.&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;a href=&quot;https://www.reddit.com/r/neovim/comments/14oozmu/neovim_cant_find_c_compiler/&quot;&gt;https://www.reddit.com/r/neovim/comments/14oozmu/neovim_cant_find_c_compiler/&lt;/a&gt;&lt;/div&gt;&lt;blockquote style=&quot;text-align: left;&quot;&gt;choco install mingw&amp;nbsp;&lt;/blockquote&gt;&lt;blockquote style=&quot;text-align: left;&quot;&gt;refreshenv&amp;nbsp;&amp;nbsp;&lt;/blockquote&gt;&lt;p&gt;관리자모드에서 mingw 를 설치하고 (winget 으로는 설치 안되는 듯?) refreshenv 하니 해결.&lt;/p&gt;&lt;h4 style=&quot;text-align: left;&quot;&gt;필요한 외부 유틸들 설치&lt;/h4&gt;&lt;a href=&quot;https://www.nerdfonts.com/font-downloads&quot;&gt;https://www.nerdfonts.com/font-downloads&lt;/a&gt;&amp;nbsp;Nerdfonts 설치. ligature를 쓰고 싶어 FiraCode Nerd Font를 선택.&lt;br /&gt;&lt;a href=&quot;https://github.com/BurntSushi/ripgrep&quot; rel=&quot;noopener noreferrer&quot; style=&quot;box-sizing: border-box; font-family: system-ui, -apple-system, &amp;quot;Segoe UI&amp;quot;, Roboto, Ubuntu, Cantarell, &amp;quot;Noto Sans&amp;quot;, sans-serif, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Helvetica, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;; font-size: 16px; text-decoration: var(--ifm-link-decoration); transition: color var(--ifm-transition-fast) var(--ifm-transition-timing-default);&quot; target=&quot;_blank&quot;&gt;ripgrep&lt;/a&gt;,&amp;nbsp;&lt;a href=&quot;https://github.com/sharkdp/fd&quot; rel=&quot;noopener noreferrer&quot; style=&quot;box-sizing: border-box; font-family: system-ui, -apple-system, &amp;quot;Segoe UI&amp;quot;, Roboto, Ubuntu, Cantarell, &amp;quot;Noto Sans&amp;quot;, sans-serif, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Helvetica, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;; font-size: 16px; text-decoration: var(--ifm-link-decoration); transition: color var(--ifm-transition-fast) var(--ifm-transition-timing-default);&quot; target=&quot;_blank&quot;&gt;fd&lt;/a&gt;&amp;nbsp;도 설치. search &amp;amp; replace 를 편하게 쓸 수 있다.&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;:, esc 눌렀을 때 모달 팝업 같은 UI가 나와서 당황.&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=N93cTbtLCIM&quot;&gt;https://www.youtube.com/watch?v=N93cTbtLCIM&lt;/a&gt;&amp;nbsp;보면서 사용법을 확인해본다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h4 style=&quot;text-align: left;&quot;&gt;&amp;lt;space&amp;gt; 키 활용&lt;/h4&gt;&lt;div&gt;NORMAL (command 모드) 상태에서 space 를 누르면 단축 기능들이 나온다.&lt;/div&gt;&lt;div&gt;&lt;ol style=&quot;text-align: left;&quot;&gt;&lt;li&gt;e/E: neoTree 를 연다. Tree 형태로 파일을 찾아볼 수 있으며 e 는 root 기준, E 는 nvim 을 실행한 경로 기준으로 Tree 를 가져오고 토글로 껐다켰다 할 수 있다.&lt;/li&gt;&lt;li&gt;-: 창 가로 분할&lt;/li&gt;&lt;li&gt;|: 창 세로 분할&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;# 창 제어하기&lt;/div&gt;&lt;ol style=&quot;text-align: left;&quot;&gt;&lt;li&gt;^h, ^l, ^j, ^k: 창 간 이동&lt;br /&gt;&lt;/li&gt;&lt;li&gt;^좌 ^우 ^상 ^하: 창 크기 조절&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;나머지는 vim 에서 쓰던 습관대로 하면 되었다.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h4 style=&quot;text-align: left;&quot;&gt;Avante.nvim 설치로 LLM 도움 받기&lt;/h4&gt;&lt;div&gt;avante.nvim 은 생각보다 어렵지 않았는데 홈디렉토리 기준에서 ./nvim/lua/plugins (windows 의 경우&amp;nbsp;C:\Users\&amp;lt;사용자계정&amp;gt;\AppData\Local\nvim\lua\plugin) 으로 진입하여 아래와 같은 파일을 `avante.lua` 와 같이 .lua 파일로 넣어준다. 윈도우여서 powershell 관련 부분을 수정했다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style=&quot;border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;&quot;&gt;&lt;div&gt;&lt;div&gt;return {&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&amp;nbsp; &quot;yetone/avante.nvim&quot;,&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&amp;nbsp; event = &quot;VeryLazy&quot;,&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&amp;nbsp; build = &quot;powershell -ExecutionPolicy Bypass -File Build-LuaTiktoken.ps1&quot;,&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&amp;nbsp; opts = {&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; -- add any opts here&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&amp;nbsp; dependencies = {&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &quot;nvim-tree/nvim-web-devicons&quot;, -- or echasnovski/mini.icons&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &quot;stevearc/dressing.nvim&quot;,&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &quot;nvim-lua/plenary.nvim&quot;,&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &quot;MunifTanjim/nui.nvim&quot;,&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; --- The below is optional, make sure to setup it properly if you have lazy=true&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; {&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &#39;MeanderingProgrammer/render-markdown.nvim&#39;,&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; opts = {&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; file_types = { &quot;markdown&quot;, &quot;Avante&quot; },&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; },&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; ft = { &quot;markdown&quot;, &quot;Avante&quot; },&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; },&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&amp;nbsp; },&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;}&lt;/div&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;그리고 nvim 을 실행하면 설치를 진행한다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;설치 후 command( : 키 누르기) 상태에서 Avante 로 시작하는 command 들이 추가된다.&lt;/div&gt;&lt;div&gt;OpenAI 의 GPT-4o mini 가 저렴해서 환경변수 OPENAI_API_KEY 에 API키를 넣고 AvanteSwitchProvier openai 라고 command를 입력하여 전환하고 사용했다.&lt;/div&gt;&lt;div&gt;:AvanteChat 으로 채팅 창을 생성할 수 있다.&lt;/div&gt;&lt;div&gt;멀티라인 입력이 가능하고 Ctrl+S 입력으로 LLM API 서버에 채팅을 보낸다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://spectrumdig.blogspot.com/feeds/5748849508160010291/comments/default' title='댓글'/><link rel='replies' type='text/html' href='http://spectrumdig.blogspot.com/2024/09/neovim-lazyvim.html#comment-form' title='0개의 덧글'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/5748849508160010291'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/5748849508160010291'/><link rel='alternate' type='text/html' href='http://spectrumdig.blogspot.com/2024/09/neovim-lazyvim.html' title='NeoVim + LazyVim 사용기 - 자주 쓰는 단축 포함.'/><author><name>spectrum</name><uri>http://www.blogger.com/profile/06120635035623961975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/a/AVvXsEhlfkxZLd9lJ08oDc9iraYVtiOcRBVOvov2MGJB6epk_TzBPZAbiWO0siACDdOu6AEV7w5vOwc9Diob89SINQRt9DzneTiXPAT8OmU1kIpDIeDGa1TLhXABJi0CqZheTQ06SHmCkJXVaJBkLimf-W2FSOyFr43uq-NJKqCHKFr3BJuHieYE5bTN1RGvgsKL=s72-w640-h481-c" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8182501247262747119.post-6079581510011899943</id><published>2024-09-03T14:26:00.000+09:00</published><updated>2024-09-03T14:26:42.560+09:00</updated><title type='text'>저지연 오디오 생성 LLVC 사용기</title><content type='html'>&lt;p&gt;https://github.com/KoeAI/LLVC 써봄&lt;/p&gt;&lt;p&gt;CUDA 11.8 버전 쓰고 있어&lt;/p&gt;&lt;p&gt;pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;VC 없어서 설치&lt;/p&gt;&lt;p&gt;https://wikidocs.net/168848 참조&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;FAIRSEQ 관련 오류&lt;/p&gt;&lt;p&gt;https://github.com/facebookresearch/fairseq/issues/2908&lt;/p&gt;&lt;p&gt;pip install editdistance&amp;nbsp;&lt;/p&gt;&lt;p&gt;설치&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;No CUDA runtime is found, using CUDA_HOME=&#39;C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.8&#39;&lt;/p&gt;&lt;p&gt;오류&lt;/p&gt;&lt;p&gt;멀쩡하게 있는데 안됨.&lt;/p&gt;&lt;p&gt;pip install torch==2.1.0+cu118 torchvision==0.16.0+cu118 torchaudio==2.1.0+cu118 --extra-index-url https://download.pytorch.org/whl/cu118&lt;/p&gt;&lt;p&gt;다시 설치&lt;/p&gt;&lt;p&gt;set DISTUTILS_USE_SDK=1&lt;/p&gt;&lt;p&gt;이게 문젠가 설정하고 재도전&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Using cached ruamel.yaml.clib-0.2.8-cp311-cp311-win_amd64.whl (118 kB)&lt;/p&gt;&lt;p&gt;Building wheels for collected packages: fairseq&lt;/p&gt;&lt;p&gt;&amp;nbsp; Building wheel for fairseq (pyproject.toml) ... error&lt;/p&gt;&lt;p&gt;&amp;nbsp; error: subprocess-exited-with-error&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; × Building wheel for fairseq (pyproject.toml) did not run successfully.&lt;/p&gt;&lt;p&gt;&amp;nbsp; │ exit code: 1&lt;/p&gt;&lt;p&gt;&amp;nbsp; ╰─&amp;gt; [1829 lines of output]&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; No CUDA runtime is found, using CUDA_HOME=&#39;C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.8&#39;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;..............................&amp;nbsp;&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; match = self._regex.search(version)&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ^^^^^^^^^^^^^^^^^^^^^^^^^^^&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; TypeError: expected string or bytes-like object, got &#39;NoneType&#39;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; [end of output]&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; note: This error originates from a subprocess, and is likely not a problem with pip.&lt;/p&gt;&lt;p&gt;&amp;nbsp; ERROR: Failed building wheel for fairseq&lt;/p&gt;&lt;p&gt;Failed to build fairseq&lt;/p&gt;&lt;p&gt;ERROR: Could not build wheels for fairseq, which is required to install pyproject.toml-based projects&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;여기서 안넘어감.&lt;/p&gt;&lt;p&gt;pip install fairseq&lt;/p&gt;&lt;p&gt;로 그냥 설치&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;tensorboard&lt;/p&gt;&lt;p&gt;scipy&lt;/p&gt;&lt;p&gt;torchmetrics&lt;/p&gt;&lt;p&gt;speechbrain&lt;/p&gt;&lt;p&gt;auraloss&lt;/p&gt;&lt;p&gt;librosa&lt;/p&gt;&lt;p&gt;# fairseq @ git+https://github.com/One-sixth/fairseq.git&lt;/p&gt;&lt;p&gt;fairseq&lt;/p&gt;&lt;p&gt;numpy&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;# I think these are the changes needed for minimal_rvc to run&lt;/p&gt;&lt;p&gt;gradio==3.36.1&lt;/p&gt;&lt;p&gt;faiss-cpu==1.7.3&lt;/p&gt;&lt;p&gt;pyworld==0.3.2&lt;/p&gt;&lt;p&gt;soundfile==0.12.1&lt;/p&gt;&lt;p&gt;ffmpeg-python==0.2.0&lt;/p&gt;&lt;p&gt;pydub==0.25.1&lt;/p&gt;&lt;p&gt;torchcrepe==0.0.20&lt;/p&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;p&gt;fairseq@git+https://github.com/One-sixth/fairseq.git&lt;/p&gt;&lt;p&gt;버전을 포기했더니 넘어는 감.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://spectrumdig.blogspot.com/feeds/6079581510011899943/comments/default' title='댓글'/><link rel='replies' type='text/html' href='http://spectrumdig.blogspot.com/2024/09/llvc.html#comment-form' title='0개의 덧글'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/6079581510011899943'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/6079581510011899943'/><link rel='alternate' type='text/html' href='http://spectrumdig.blogspot.com/2024/09/llvc.html' title='저지연 오디오 생성 LLVC 사용기'/><author><name>spectrum</name><uri>http://www.blogger.com/profile/06120635035623961975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8182501247262747119.post-8085084873601630841</id><published>2020-09-20T22:11:00.002+09:00</published><updated>2020-09-20T22:11:23.759+09:00</updated><title type='text'>Google Daydream 소감</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
드디어 V30 사은품인 Daydream이 왔다.&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
낭랑한 벨과 함께 무심한 듯 시크하게 툭 던지고 가시는 택배느님.&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEeKTxwngaD2Z5wVGA6LQPsWXfYgU5T1QNXP1iOzNPrulHjlTvUfJ-6PhZ_uX_PRKuX-m5jw5JGvFIWB2zYr3O3qfo0UMl_A4ugVteot7vFuryk6kI7BH6gwnXCKC58ql4fgGRoVUsmBLQ/s1600/IMG_8083.JPG&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1600&quot; data-original-width=&quot;1600&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEeKTxwngaD2Z5wVGA6LQPsWXfYgU5T1QNXP1iOzNPrulHjlTvUfJ-6PhZ_uX_PRKuX-m5jw5JGvFIWB2zYr3O3qfo0UMl_A4ugVteot7vFuryk6kI7BH6gwnXCKC58ql4fgGRoVUsmBLQ/s320/IMG_8083.JPG&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
수령까지 두달 걸렸다.&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSYOPt_FxPS4tH6YRsvMDAGna-Q4j06ZKiZcp6fEoEVMHiWOa32RXoQQksp8HjJAymfMM_UoQrnplsPY9mwoGX8L0AMVf0RewdG1no6J2TFKDqvFhnbJS3f_HVBFlkpbBiLhnEjafKzi0S/s1600/IMG_8082.JPG&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1600&quot; data-original-width=&quot;1600&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSYOPt_FxPS4tH6YRsvMDAGna-Q4j06ZKiZcp6fEoEVMHiWOa32RXoQQksp8HjJAymfMM_UoQrnplsPY9mwoGX8L0AMVf0RewdG1no6J2TFKDqvFhnbJS3f_HVBFlkpbBiLhnEjafKzi0S/s320/IMG_8082.JPG&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
눈을 뜨고 꿈꿔 보세요. &lt;strike&gt;여기가 눈감으면 코베어 간다는 서울인가?&lt;/strike&gt;&lt;/div&gt;
&lt;br /&gt;
주황색면으로 열면 된다.&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4vdI_eXM48gFmLhyEkGY5r9FqYiNoSp-AQqVTv76DylIeuP4L_1ocdKYXiU4gGHNTy0eRB_5A46JN7LE2klWJ-4PCUke76jE98SjriG6ovtfsjvKbmroFXlDxNg5EXzupJmqxfdc6H7Mj/s1600/IMG_8084.JPG&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1600&quot; data-original-width=&quot;1600&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4vdI_eXM48gFmLhyEkGY5r9FqYiNoSp-AQqVTv76DylIeuP4L_1ocdKYXiU4gGHNTy0eRB_5A46JN7LE2klWJ-4PCUke76jE98SjriG6ovtfsjvKbmroFXlDxNg5EXzupJmqxfdc6H7Mj/s320/IMG_8084.JPG&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkJifL4JqxGW4c8M5CjlZhU3ZW4df6-ZmgXNPSGf2Qgw4uNGQ_J8zCz2FG0jHzuMd6YH0KVmdB2YaisGUNJvWhlhLvuGgr71f619PoG4gqrpGKp3eKRzDYiLFGS-NL9EEUhOyLgspBdvWR/s1600/IMG_8085.JPG&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1600&quot; data-original-width=&quot;1600&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkJifL4JqxGW4c8M5CjlZhU3ZW4df6-ZmgXNPSGf2Qgw4uNGQ_J8zCz2FG0jHzuMd6YH0KVmdB2YaisGUNJvWhlhLvuGgr71f619PoG4gqrpGKp3eKRzDYiLFGS-NL9EEUhOyLgspBdvWR/s320/IMG_8085.JPG&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
컨트롤러의 만듦새는 아주 좋다.&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
기존에 나와있는 사진엔 가로 머리끈만 있어서 똥머리한 언니들도 막 쓰고 플레이하고 그랬는데 실제로 보니 이중 끈이었다.&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://www.cmple.com/Content/Images/uploaded/home-headset.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;800&quot; data-original-width=&quot;764&quot; height=&quot;320&quot; src=&quot;https://www.cmple.com/Content/Images/uploaded/home-headset.jpg&quot; width=&quot;305&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
보통 돌아다니는 착샷&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
Daydream 설치 - 실행 - 폰 장착 - 컨트롤러 페어링 - VR진입까지 과정을 쉽게 하려고한 노력이 보였음.&lt;br /&gt;
NFC로 데이드림뷰어에 폰을 장착하면 자동으로 인식하여 데이드림 앱이 실행되는 경험은 꽤 괜찮았음.&lt;br /&gt;
컨트롤러는 크기, 그립감 면에서 매우 훌륭하고 인식률, 반응속도 모두 만족스러움.&lt;br /&gt;홈버튼으로 시선을 재 조정하는 발상은 좋은데 왼쪽 오른쪽이 앱마다 조금 혼란스럽고 상하방향 캘리브레이션은 안된다.&lt;br /&gt;
&lt;br /&gt;
이 부분에서 좌절했다.&lt;br /&gt;
누워서 넷플릭스를 보는 건 다른 방법을 찾아보아야한다는 소리.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;알고보니 이런 건 Youtube이나 Netflix, Skybox player 등등에서 테스트해본 결과 자체 앱에서 지원하고 있다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Netflix는 void theater라는 것이 있는데&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://i0.wp.com/xn--u9j1gsa8mmgt69o30ec97apm6bpb9b.com/wp-content/uploads/2018/05/netflix-vr-void-theater.png?w=357&amp;amp;ssl=1&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;242&quot; data-original-width=&quot;357&quot; src=&quot;https://i0.wp.com/xn--u9j1gsa8mmgt69o30ec97apm6bpb9b.com/wp-content/uploads/2018/05/netflix-vr-void-theater.png?w=357&amp;amp;ssl=1&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&amp;lt;출처:&amp;nbsp;&lt;a href=&quot;https://元ボクサーの一念発起.com/2018/05/10/oculus-go/&quot;&gt;https://元ボクサーの一念発起.com/2018/05/10/oculus-go/&lt;/a&gt; &amp;gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;Netflix app에서 요렇게 눈송이 같이 생긴 아이콘을 컨트롤러로 찍고 들어가면&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;화면만 덩그러니 나오고 스티어링휠(자동차 핸들) 모양 아이콘을 선택하고 누으면 자연스럽게 조정이 되었다.&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;Youtube나 Skybox앱의 경우 아예 컨트롤러에서 꾹 누르고 화면 이동하는게 가능해서 편리.&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;게임은 이것저것 해봤는데 Rez Infinite이 최고.&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;아니 이건 그냥 전 VR 기기를 통틀어서 Rez Infinite이 최고가 아닐까 싶음;;;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;결론,&amp;nbsp;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;1. 누워서 보기 잘된다.&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;2. Rez Infinite 최고.&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://spectrumdig.blogspot.com/feeds/8085084873601630841/comments/default' title='댓글'/><link rel='replies' type='text/html' href='http://spectrumdig.blogspot.com/2018/10/google-daydream.html#comment-form' title='0개의 덧글'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/8085084873601630841'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/8085084873601630841'/><link rel='alternate' type='text/html' href='http://spectrumdig.blogspot.com/2018/10/google-daydream.html' title='Google Daydream 소감'/><author><name>spectrum</name><uri>http://www.blogger.com/profile/06120635035623961975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEeKTxwngaD2Z5wVGA6LQPsWXfYgU5T1QNXP1iOzNPrulHjlTvUfJ-6PhZ_uX_PRKuX-m5jw5JGvFIWB2zYr3O3qfo0UMl_A4ugVteot7vFuryk6kI7BH6gwnXCKC58ql4fgGRoVUsmBLQ/s72-c/IMG_8083.JPG" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8182501247262747119.post-410262637381442900</id><published>2020-09-20T16:49:00.001+09:00</published><updated>2020-09-20T16:49:22.416+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="async"/><category scheme="http://www.blogger.com/atom/ns#" term="javascript"/><category scheme="http://www.blogger.com/atom/ns#" term="new"/><category scheme="http://www.blogger.com/atom/ns#" term="object"/><title type='text'>async function 에 new operator 를 사용하는 방법</title><content type='html'>&lt;div&gt;&lt;div&gt;&lt;b&gt;tr;dl&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;ol style=&quot;text-align: left;&quot;&gt;&lt;li&gt;async가 필요한 constructor를 만들 때 return (async()=&amp;gt;{ .... })() 로 감싼다.&lt;/li&gt;&lt;li&gt;사용 시 await new ... 와 같이 new 앞에 await 을 붙여 사용한다.&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;/span&gt;&lt;div&gt;javascript 에서는 function 을 사용자 정의 객체 타입으로 사용하여 객체 인스턴스를 사용할 수 있게 해주는 new 라는 operator가 있다.&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/new&quot;&gt;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/new&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;pre class=&quot;brush: js notranslate line-numbers language-js&quot; style=&quot;background: rgb(238, 238, 238); border-bottom: 0px solid rgb(61, 126, 154); border-color: rgb(61, 126, 154); border-image: initial; border-left: 5px solid rgb(61, 126, 154); border-right-color: rgb(61, 126, 154); border-right-style: solid; border-right-width: 0px !important; border-style: solid; border-top: 0px solid rgb(61, 126, 154); border-width: 0px 0px 0px 5px; box-sizing: border-box; color: #333333; counter-reset: linenumber 0; direction: ltr; font-family: consolas, monaco, &amp;quot;Andale Mono&amp;quot;, monospace; font-size: 16px; hyphens: none; letter-spacing: -0.04448px; line-height: 1.5; margin-bottom: 20px; margin-top: 0px; max-width: 100%; overflow-wrap: normal; overflow: auto; padding: 15px 15px 15px 3.8em; position: relative; tab-size: 4; text-shadow: none; width: 1014px; word-break: normal;&quot;&gt;&lt;code class=&quot;language-js&quot; style=&quot;background: 0px 0px transparent; border-radius: 2px; border: 0px; font-family: Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 1em; font-weight: inherit; hyphens: none; line-height: 1.5; margin: 0px; overflow-wrap: normal; padding: 0px; position: relative; tab-size: 4; text-shadow: none; white-space: inherit; word-break: normal; word-spacing: normal;&quot;&gt;&lt;span class=&quot;token keyword&quot; style=&quot;border: 0px; color: #0077aa; margin: 0px; padding: 0px;&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot; style=&quot;border: 0px; color: #dd4a68; margin: 0px; padding: 0px;&quot;&gt;Car&lt;/span&gt;&lt;span class=&quot;token punctuation&quot; style=&quot;border: 0px; color: #999999; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot; style=&quot;border: 0px; margin: 0px; padding: 0px;&quot;&gt;make&lt;span class=&quot;token punctuation&quot; style=&quot;border: 0px; color: #999999; margin: 0px; padding: 0px;&quot;&gt;,&lt;/span&gt; model&lt;span class=&quot;token punctuation&quot; style=&quot;border: 0px; color: #999999; margin: 0px; padding: 0px;&quot;&gt;,&lt;/span&gt; year&lt;/span&gt;&lt;span class=&quot;token punctuation&quot; style=&quot;border: 0px; color: #999999; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot; style=&quot;border: 0px; color: #999999; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot; style=&quot;border: 0px; color: #0077aa; margin: 0px; padding: 0px;&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot; style=&quot;border: 0px; color: #999999; margin: 0px; padding: 0px;&quot;&gt;.&lt;/span&gt;make &lt;span class=&quot;token operator&quot; style=&quot;background: transparent; border: 0px; color: #9a6e3a; margin: 0px; padding: 0px;&quot;&gt;=&lt;/span&gt; make&lt;span class=&quot;token punctuation&quot; style=&quot;border: 0px; color: #999999; margin: 0px; padding: 0px;&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot; style=&quot;border: 0px; color: #0077aa; margin: 0px; padding: 0px;&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot; style=&quot;border: 0px; color: #999999; margin: 0px; padding: 0px;&quot;&gt;.&lt;/span&gt;model &lt;span class=&quot;token operator&quot; style=&quot;background: transparent; border: 0px; color: #9a6e3a; margin: 0px; padding: 0px;&quot;&gt;=&lt;/span&gt; model&lt;span class=&quot;token punctuation&quot; style=&quot;border: 0px; color: #999999; margin: 0px; padding: 0px;&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot; style=&quot;border: 0px; color: #0077aa; margin: 0px; padding: 0px;&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot; style=&quot;border: 0px; color: #999999; margin: 0px; padding: 0px;&quot;&gt;.&lt;/span&gt;year &lt;span class=&quot;token operator&quot; style=&quot;background: transparent; border: 0px; color: #9a6e3a; margin: 0px; padding: 0px;&quot;&gt;=&lt;/span&gt; year&lt;span class=&quot;token punctuation&quot; style=&quot;border: 0px; color: #999999; margin: 0px; padding: 0px;&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot; style=&quot;border: 0px; color: #999999; margin: 0px; padding: 0px;&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;이렇게 정의할 경우&lt;/div&gt;&lt;div&gt;&lt;pre class=&quot;brush: js notranslate line-numbers language-js&quot; style=&quot;background: rgb(238, 238, 238); border-bottom: 0px solid rgb(61, 126, 154); border-color: rgb(61, 126, 154); border-image: initial; border-left: 5px solid rgb(61, 126, 154); border-right-color: rgb(61, 126, 154); border-right-style: solid; border-right-width: 0px !important; border-style: solid; border-top: 0px solid rgb(61, 126, 154); border-width: 0px 0px 0px 5px; box-sizing: border-box; color: #333333; counter-reset: linenumber 0; direction: ltr; font-family: consolas, monaco, &amp;quot;Andale Mono&amp;quot;, monospace; font-size: 16px; hyphens: none; letter-spacing: -0.04448px; line-height: 1.5; margin-bottom: 20px; margin-top: 0px; max-width: 100%; overflow-wrap: normal; overflow: auto; padding: 15px 15px 15px 3.8em; position: relative; tab-size: 4; text-shadow: none; width: 1014px; word-break: normal;&quot;&gt;&lt;code class=&quot;language-js&quot; style=&quot;background: 0px 0px transparent; border-radius: 2px; border: 0px; font-family: Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 1em; font-weight: inherit; hyphens: none; line-height: 1.5; margin: 0px; overflow-wrap: normal; padding: 0px; position: relative; tab-size: 4; text-shadow: none; white-space: inherit; word-break: normal; word-spacing: normal;&quot;&gt;&lt;span class=&quot;token keyword&quot; style=&quot;border: 0px; color: #0077aa; margin: 0px; padding: 0px;&quot;&gt;var&lt;/span&gt; mycar &lt;span class=&quot;token operator&quot; style=&quot;background: transparent; border: 0px; color: #9a6e3a; margin: 0px; padding: 0px;&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot; style=&quot;border: 0px; color: #0077aa; margin: 0px; padding: 0px;&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot; style=&quot;border: 0px; color: #dd4a68; margin: 0px; padding: 0px;&quot;&gt;Car&lt;/span&gt;&lt;span class=&quot;token punctuation&quot; style=&quot;border: 0px; color: #999999; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot; style=&quot;border: 0px; color: #669900; margin: 0px; padding: 0px;&quot;&gt;&quot;Eagle&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot; style=&quot;border: 0px; color: #999999; margin: 0px; padding: 0px;&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot; style=&quot;border: 0px; color: #669900; margin: 0px; padding: 0px;&quot;&gt;&quot;Talon TSi&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot; style=&quot;border: 0px; color: #999999; margin: 0px; padding: 0px;&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot; style=&quot;border: 0px; color: #990055; margin: 0px; padding: 0px;&quot;&gt;1993&lt;/span&gt;&lt;span class=&quot;token punctuation&quot; style=&quot;border: 0px; color: #999999; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot; style=&quot;border: 0px; color: #999999; margin: 0px; padding: 0px;&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;mycar와&lt;/div&gt;&lt;div&gt;&lt;pre class=&quot;brush: js notranslate line-numbers language-js&quot; style=&quot;background: rgb(238, 238, 238); border-bottom: 0px solid rgb(61, 126, 154); border-color: rgb(61, 126, 154); border-image: initial; border-left: 5px solid rgb(61, 126, 154); border-right-color: rgb(61, 126, 154); border-right-style: solid; border-right-width: 0px !important; border-style: solid; border-top: 0px solid rgb(61, 126, 154); border-width: 0px 0px 0px 5px; box-sizing: border-box; color: #333333; counter-reset: linenumber 0; direction: ltr; font-family: consolas, monaco, &amp;quot;Andale Mono&amp;quot;, monospace; font-size: 16px; hyphens: none; letter-spacing: -0.04448px; line-height: 1.5; margin-bottom: 20px; margin-top: 0px; max-width: 100%; overflow-wrap: normal; overflow: auto; padding: 15px 15px 15px 3.8em; position: relative; tab-size: 4; text-shadow: none; width: 1014px; word-break: normal;&quot;&gt;&lt;code class=&quot;language-js&quot; style=&quot;background: 0px 0px transparent; border-radius: 2px; border: 0px; font-family: Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 1em; font-weight: inherit; hyphens: none; line-height: 1.5; margin: 0px; overflow-wrap: normal; padding: 0px; position: relative; tab-size: 4; text-shadow: none; white-space: inherit; word-break: normal; word-spacing: normal;&quot;&gt;&lt;span class=&quot;token keyword&quot; style=&quot;border: 0px; color: #0077aa; margin: 0px; padding: 0px;&quot;&gt;var&lt;/span&gt; kenscar &lt;span class=&quot;token operator&quot; style=&quot;background: transparent; border: 0px; color: #9a6e3a; margin: 0px; padding: 0px;&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot; style=&quot;border: 0px; color: #0077aa; margin: 0px; padding: 0px;&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot; style=&quot;border: 0px; color: #dd4a68; margin: 0px; padding: 0px;&quot;&gt;Car&lt;/span&gt;&lt;span class=&quot;token punctuation&quot; style=&quot;border: 0px; color: #999999; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot; style=&quot;border: 0px; color: #669900; margin: 0px; padding: 0px;&quot;&gt;&quot;Nissan&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot; style=&quot;border: 0px; color: #999999; margin: 0px; padding: 0px;&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot; style=&quot;border: 0px; color: #669900; margin: 0px; padding: 0px;&quot;&gt;&quot;300ZX&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot; style=&quot;border: 0px; color: #999999; margin: 0px; padding: 0px;&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot; style=&quot;border: 0px; color: #990055; margin: 0px; padding: 0px;&quot;&gt;1992&lt;/span&gt;&lt;span class=&quot;token punctuation&quot; style=&quot;border: 0px; color: #999999; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot; style=&quot;border: 0px; color: #999999; margin: 0px; padding: 0px;&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;kenscar는 다른 객체를 만들어서 mycar.year, kenscar.year 가 각각 다른 값을 유지할 수 있다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;개인적으로 function을 좋아하기 때문에 React도 hook이 생기고 나선 쓰기 시작했고 깊게 파고 들수록 함수가 가지고 있는 유연함과 강력함에 더더욱 빠져들고 있는데&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;최근 async/await 비동기 패턴을 자주 쓰면서 async function도 자주 사용하는데&lt;/div&gt;&lt;div&gt;객체 인스턴스를 사용하기 위해 new 를 사용하려고 하면 문제가 있는 것을 발견했다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;가령,&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;div&gt;async function foo() {&lt;/div&gt;&lt;div&gt;&amp;nbsp; let pubInfo = await (await fetch(&quot;https://www.cloudflare.com/cdn-cgi/trace&quot;)).text();&lt;/div&gt;&lt;div&gt;&amp;nbsp; return {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; getTrace: ()=&amp;gt; pubInfo&lt;/div&gt;&lt;div&gt;&amp;nbsp; }&lt;/div&gt;&lt;div&gt;}&lt;/div&gt;&lt;/blockquote&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;이와 같이 async function을 만들고 난 후&lt;/div&gt;&lt;div&gt;bar라는 변수를 new를 가지고 foo객체로 만들려고&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;blockquote&gt;let bar = new foo();&lt;/blockquote&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;하면&lt;/div&gt;&lt;div&gt;Uncaught TypeError: foo is not a constructor 오류가 발생한다.&lt;/div&gt;&lt;div&gt;async 가 붙으면서 function인 foo의 new operator를 사용할 수 없다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;결국 foo 안에서 await 을 사용하려면 어떻게 해야할까?&lt;/div&gt;&lt;div&gt;원하는 것은 결국 constructor 에서 return 으로 반환 받는 { getTrace ... } object를 받으면 되는 것에 집중해보면&lt;/div&gt;&lt;div&gt;function 안의 scope을 전부 (async()=&amp;gt; {.... })() 와 같은 IIFE(Immediately Invoked Function Expression)로 감싸서 반환하면 어떨까?&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;div&gt;function foo() {&lt;/div&gt;&lt;div&gt;&amp;nbsp; return (async ()=&amp;gt;{&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; let pubInfo = await (await fetch(&quot;https://www.cloudflare.com/cdn-cgi/trace&quot;)).text();&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; return {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; getTrace: ()=&amp;gt; pubInfo&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; })();&lt;/div&gt;&lt;div&gt;}&lt;/div&gt;&lt;/blockquote&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;그러면 foo 는 new 로 instantiate 되면서 (async ()=&amp;gt; {})() block을 실행하면서 Promise 타입을 반환할 것이고&lt;/div&gt;&lt;div&gt;이를 다시 await으로 실행하면 되지 않을까?&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;div&gt;new foo();&lt;/div&gt;&lt;div&gt;&amp;gt; Promise {&amp;lt;pending&amp;gt;}&lt;/div&gt;&lt;/blockquote&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;new foo() 했을 때 Promise를 반환한다!&lt;/div&gt;&lt;div&gt;자 그럼 await을 붙여서 풀어보자.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;div&gt;bar = await new foo()&lt;/div&gt;&lt;div&gt;&amp;gt; {getTrace: ƒ}&lt;/div&gt;&lt;/blockquote&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;객체가 달려오는 걸 확인할 수 있다.&lt;/div&gt;&lt;div&gt;따라서 bar.getTrace() 를 실행해보면&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;div&gt;bar.getTrace()&lt;/div&gt;&lt;div&gt;&amp;gt; &quot;fl=34f135&lt;/div&gt;&lt;div&gt;h=www.cloudflare.com&lt;/div&gt;&lt;div&gt;ip=....&lt;/div&gt;&lt;div&gt;ts=...&lt;/div&gt;&lt;div&gt;visit_scheme=https&lt;/div&gt;&lt;div&gt;uag=....&lt;/div&gt;&lt;div&gt;colo=ICN&lt;/div&gt;&lt;div&gt;http=http/2&lt;/div&gt;&lt;div&gt;loc=...&lt;/div&gt;&lt;div&gt;tls=TLSv1.3&lt;/div&gt;&lt;div&gt;sni=plaintext&lt;/div&gt;&lt;div&gt;warp=off&lt;/div&gt;&lt;div&gt;&quot;&lt;/div&gt;&lt;/blockquote&gt;&lt;/div&gt;&lt;div&gt;이와 같이 정상적으로 constructor 를 실행했고 그 내용까지 잘 접근하는 모습을 볼 수 있다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;요약:&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;ol style=&quot;text-align: left;&quot;&gt;&lt;li&gt;async가 필요한 constructor를 만들 때 return (async()=&amp;gt;{ .... })() 로 감싼다.&lt;/li&gt;&lt;li&gt;사용 시 await new ... 와 같이 new 앞에 await 을 붙여 사용한다.&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://spectrumdig.blogspot.com/feeds/410262637381442900/comments/default' title='댓글'/><link rel='replies' type='text/html' href='http://spectrumdig.blogspot.com/2020/09/async-function-new-operator.html#comment-form' title='0개의 덧글'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/410262637381442900'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/410262637381442900'/><link rel='alternate' type='text/html' href='http://spectrumdig.blogspot.com/2020/09/async-function-new-operator.html' title='async function 에 new operator 를 사용하는 방법'/><author><name>spectrum</name><uri>http://www.blogger.com/profile/06120635035623961975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8182501247262747119.post-5178561673230926959</id><published>2019-04-16T03:04:00.005+09:00</published><updated>2019-04-16T03:04:49.325+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="https"/><category scheme="http://www.blogger.com/atom/ns#" term="secured"/><category scheme="http://www.blogger.com/atom/ns#" term="server"/><category scheme="http://www.blogger.com/atom/ns#" term="websocket"/><title type='text'> 세상 간단한 https(+secured websocket): Caddy</title><content type='html'>nginx, apache2 같은 걸로 매번 certbot 연동을 통해 https 하는게 지겨워서 알아보니 Caddy라는게 있더라.&lt;br /&gt;
설치법은 강하게 크기 위해 알아서 해본다.&lt;br /&gt;
&lt;br /&gt;
# cat /etc/caddy/Caddyfile&lt;br /&gt;
your.shitty.site:8123 {&lt;br /&gt;
&amp;nbsp; proxy / localhost:9123 {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; websocket&lt;br /&gt;
&amp;nbsp; &amp;nbsp; transparent&lt;br /&gt;
&amp;nbsp; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
이건 외부에서 8123으로 들어오는 걸 내부적으로 9123로 맞춰서 https 맞춰주는 reverse proxy.&lt;br /&gt;
만일 port를 생략하면&lt;br /&gt;
&lt;br /&gt;
# cat /etc/caddy/Caddyfile&lt;br /&gt;
your.shitty.site {&lt;br /&gt;
&amp;nbsp; proxy / localhost:9123 {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; websocket&lt;br /&gt;
&amp;nbsp; &amp;nbsp; transparent&lt;br /&gt;
&amp;nbsp; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
기본포트인 443으로 되어 https://your.shitty.site 로 접속이 된다.&lt;br /&gt;
&lt;br /&gt;
요새 말썽이던 cloudflare 버리고 netlify domain이랑 caddy를 쓰니까 앓던 이가 빠진 느낌.&lt;br /&gt;
이 조합은 당분간 계속 써야겠다 싶다.&lt;br /&gt;
&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://spectrumdig.blogspot.com/feeds/5178561673230926959/comments/default' title='댓글'/><link rel='replies' type='text/html' href='http://spectrumdig.blogspot.com/2019/04/httpssecured-websocket-caddy.html#comment-form' title='1개의 덧글'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/5178561673230926959'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/5178561673230926959'/><link rel='alternate' type='text/html' href='http://spectrumdig.blogspot.com/2019/04/httpssecured-websocket-caddy.html' title=' 세상 간단한 https(+secured websocket): Caddy'/><author><name>spectrum</name><uri>http://www.blogger.com/profile/06120635035623961975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8182501247262747119.post-4921629227848355105</id><published>2019-03-29T12:13:00.000+09:00</published><updated>2019-03-29T12:13:08.770+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="cloudatcost"/><category scheme="http://www.blogger.com/atom/ns#" term="hosting"/><category scheme="http://www.blogger.com/atom/ns#" term="troubleshoot"/><title type='text'>cloudatcost 에서 외부 인터넷 연결이 안될 때</title><content type='html'>전원 한번 껐다 켰더니 모든 linux 서버들이 다 안된다.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgczSweEasmEEhC5jz24xfBZiHmeTapqNoLInd-nvtmHDUYfTYRybCGktVUhV9RRRLbbtxVZJGLXtT0w1hTvAonWBAJcxlvvH5pmwhIWgX2dqqFRyemVkLKIc3PEETDpXgoNi0JQNempAeU/s1600/Screen+Shot+2019-03-29+at+12.11.38.png&quot; imageanchor=&quot;1&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgczSweEasmEEhC5jz24xfBZiHmeTapqNoLInd-nvtmHDUYfTYRybCGktVUhV9RRRLbbtxVZJGLXtT0w1hTvAonWBAJcxlvvH5pmwhIWgX2dqqFRyemVkLKIc3PEETDpXgoNi0JQNempAeU/s1600/Screen+Shot+2019-03-29+at+12.11.38.png&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://panel.cloudatcost.com/index.php&quot;&gt;https://panel.cloudatcost.com/index.php&lt;/a&gt;&amp;nbsp;에서 해당 서버의 console 버튼 눌러서 보니 ifconfig&amp;nbsp;해본 결과 실제 panel의 IP정보와 다르게 설정이 되있더라.&lt;br /&gt;(덧. 여기서 console 이라고 함은 ssh 터미널 같은 게 아니라 cloudatcost에서 VMWare로 관리하는 웹스크린캐스트 같은 거라고 보면 됨)&lt;br /&gt;
&lt;br /&gt;
근데 windows server는 멀쩡하고 linux 서버만 안되는게 이상해서 ticket 넣었더니&lt;br /&gt;
&amp;nbsp;VMWare console로 진입한 뒤 network configuration 을 맞춰보라고 하더라.&lt;br /&gt;
&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
Fabio P&lt;br /&gt;Hello,&lt;br /&gt;In the case of the VM with IP 64.137.182.39, you have to go inside the configuration file and edit it. Make sure that all the network configuration is the same as the information shown to you on panel.&lt;br /&gt;You will have to do the same for the other two VMs.&lt;br /&gt;Let us know if further assistance is required. Have a great day.&lt;/blockquote&gt;
&lt;br /&gt;
음. network configuration 이라니 VMWare의 configuration이 있나? 이것저것 뒤져보다가 도저히 모르겠어서 GG&lt;br /&gt;
리붓하고 났더니 이번엔 console 자체도 진입이 안됨.&lt;br /&gt;
ticket 또 쓰고 reboot 했더니 다시 돌아와서 어떤 거 수정하면 되냐 하고 또 물어봄.&lt;br /&gt;
내가 어느정도 알고 있을지를 모르니 첨 부터 자세하게 얘기를 안해주는구나. 얘들 스타일인갑다.&lt;br /&gt;
&lt;br /&gt;
결국&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
Fabio P&lt;br /&gt;Hello,&lt;br /&gt;For your Ubuntu machine, you can use this link as a guide&lt;br /&gt;https://help.ubuntu.com/community/NetworkConfigurationCommandLine/Automatic&lt;br /&gt;Make sure that all the network configuration is the same as the information shown to you on panel.&lt;br /&gt;Let us know if further assistance is required. Have a great day.&lt;/blockquote&gt;
원하는 답을 얻었고&lt;br /&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEgUwshrDm5Fr_X2DJcH_zmBKjGinZTGBDcNH5Q4j0dr10ddW5aH8cb3dpZW2qxf6z82Nj4ZctuVj4w_nsu6ccCRYamzMt7C1_oJ09nm9hXDie-Z9p_ivqLp3B6_X5KMtYVyxLOB7_SZWx/s1600/Screen+Shot+2019-03-29+at+12.04.51.png&quot; imageanchor=&quot;1&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEgUwshrDm5Fr_X2DJcH_zmBKjGinZTGBDcNH5Q4j0dr10ddW5aH8cb3dpZW2qxf6z82Nj4ZctuVj4w_nsu6ccCRYamzMt7C1_oJ09nm9hXDie-Z9p_ivqLp3B6_X5KMtYVyxLOB7_SZWx/s1600/Screen+Shot+2019-03-29+at+12.04.51.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
나는 sudo vi /etc/network/interfaces 로&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
auto lo&lt;br /&gt;iface lo inet loopback&lt;br /&gt;auto ens32&lt;br /&gt;iface ens32 inet static&lt;br /&gt;address &amp;lt; 이곳 &amp;gt;&lt;br /&gt;gateway &amp;lt; 이곳 &amp;gt;&lt;br /&gt;netmask 255.255.255.0&lt;br /&gt;sysctl kernel.hostname=c.........&lt;br /&gt;hostname c......&lt;br /&gt;dns-nameservers 8.8.8.8 8.8.4.4&lt;/blockquote&gt;
두 곳을 업데이트 해준뒤.&lt;br /&gt;
재시작.&lt;br /&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlfyFbeStwdwcu0H3PfT9saAey1ehDvt655UraB7iWgJCofn6Qgobo67pqcA92qfTn0Rw5LrtJ_cZx9QFcCi2bNMNXe7bSXB_OwzN5gfdE2kcvBwrA63HI1Ief44rphLW0S2OP_4rGEFdn/s1600/Screen+Shot+2019-03-29+at+12.01.34.png&quot; imageanchor=&quot;1&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;587&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlfyFbeStwdwcu0H3PfT9saAey1ehDvt655UraB7iWgJCofn6Qgobo67pqcA92qfTn0Rw5LrtJ_cZx9QFcCi2bNMNXe7bSXB_OwzN5gfdE2kcvBwrA63HI1Ief44rphLW0S2OP_4rGEFdn/s640/Screen+Shot+2019-03-29+at+12.01.34.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
console은 외부 세상과 다시 연결이 되었으며.&lt;br /&gt;
평화가 찾아왔다.&lt;br /&gt;
&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://spectrumdig.blogspot.com/feeds/4921629227848355105/comments/default' title='댓글'/><link rel='replies' type='text/html' href='http://spectrumdig.blogspot.com/2019/03/cloudatcost.html#comment-form' title='0개의 덧글'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/4921629227848355105'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/4921629227848355105'/><link rel='alternate' type='text/html' href='http://spectrumdig.blogspot.com/2019/03/cloudatcost.html' title='cloudatcost 에서 외부 인터넷 연결이 안될 때'/><author><name>spectrum</name><uri>http://www.blogger.com/profile/06120635035623961975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgczSweEasmEEhC5jz24xfBZiHmeTapqNoLInd-nvtmHDUYfTYRybCGktVUhV9RRRLbbtxVZJGLXtT0w1hTvAonWBAJcxlvvH5pmwhIWgX2dqqFRyemVkLKIc3PEETDpXgoNi0JQNempAeU/s72-c/Screen+Shot+2019-03-29+at+12.11.38.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8182501247262747119.post-3624157778851322423</id><published>2018-01-06T05:24:00.006+09:00</published><updated>2018-01-06T06:12:28.037+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="django"/><category scheme="http://www.blogger.com/atom/ns#" term="python"/><category scheme="http://www.blogger.com/atom/ns#" term="REST"/><title type='text'>django REST API practice like a boss</title><content type='html'>pycharm 안녕.&lt;br /&gt;
django project 생성.&lt;br /&gt;
&lt;br /&gt;
실행환경은 virtualenv로 3.6.x&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmBndCXQt7VetDprES9LlY0X5-ANrnNF_3wogEmlITqtTjn0e3SV4jbPXaCEBQ99kZHBHzKRavoekobOAHSwA6VzP0vawn1s3Zb6Oiwj1Tvd55J7pkyML3Lu7uYDt2HbzPmZSp9NZuntWj/s1600/Screen+Shot+2018-01-05+at+03.57.47.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;631&quot; data-original-width=&quot;889&quot; height=&quot;454&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmBndCXQt7VetDprES9LlY0X5-ANrnNF_3wogEmlITqtTjn0e3SV4jbPXaCEBQ99kZHBHzKRavoekobOAHSwA6VzP0vawn1s3Zb6Oiwj1Tvd55J7pkyML3Lu7uYDt2HbzPmZSp9NZuntWj/s640/Screen+Shot+2018-01-05+at+03.57.47.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
virtualenv 생성에 약간 시간 소모.&lt;br /&gt;
setting.py와 urls.py가 반겨줌&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhV4MdbeO2gCzWj_9v-CfB58mlo1lIFliiU3ioRLv1uSfLbmxlde3ei_8aUDCaAQUxGzh9KBXZGVXFrQYYBvHTM0cL5ZZg8IR45dVyHqXZuGxPIU9FqT4aW-uH5KwLoEpdA2ElIL6kZ-L_9/s1600/Screen+Shot+2018-01-05+at+04.01.32.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;381&quot; data-original-width=&quot;752&quot; height=&quot;324&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhV4MdbeO2gCzWj_9v-CfB58mlo1lIFliiU3ioRLv1uSfLbmxlde3ei_8aUDCaAQUxGzh9KBXZGVXFrQYYBvHTM0cL5ZZg8IR45dVyHqXZuGxPIU9FqT4aW-uH5KwLoEpdA2ElIL6kZ-L_9/s640/Screen+Shot+2018-01-05+at+04.01.32.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
djex1 프로젝트를 만들었음&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
__init__.py&lt;br /&gt;
settings.py&lt;br /&gt;
urls.py&lt;br /&gt;
wsgi.py&lt;/blockquote&gt;
요렇게 생김.&lt;br /&gt;
&lt;br /&gt;
자, 게임을 시작해볼까?&lt;br /&gt;
option+R 누름.&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxeGMFxQqTz4A4GoeeAp5Jp2fRFr9uiAI1JUyro2BdjBP-7oRG9NLgpV17-vblg1wmyVevzqf9BOQe_ygBbK3VPZf75g-3OzGToDacr-n9OvqKP3P-WORhu367uxZE5O3c0qq08o5vasPd/s1600/Screen+Shot+2018-01-05+at+04.02.58.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;269&quot; data-original-width=&quot;429&quot; height=&quot;250&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxeGMFxQqTz4A4GoeeAp5Jp2fRFr9uiAI1JUyro2BdjBP-7oRG9NLgpV17-vblg1wmyVevzqf9BOQe_ygBbK3VPZf75g-3OzGToDacr-n9OvqKP3P-WORhu367uxZE5O3c0qq08o5vasPd/s400/Screen+Shot+2018-01-05+at+04.02.58.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
이런 애가 나옴.&lt;br /&gt;
startapp product 라고 쳐서 product 업무를 만들어보자.&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1wVs4-GHw7hG8GDUg_KNKDJdZMq2AqIG9JeuktHzj0UTgkY5mtj5wPSAOPRuWwdlb4up8gN1QZSPdyOGCBIiSEV0dXVn37sDgWCiIHoL_8o7AVbdpwNFKmJiHETPPWx6enaYt3wX8AVoy/s1600/Screen+Shot+2018-01-05+at+04.03.37.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;181&quot; data-original-width=&quot;702&quot; height=&quot;163&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1wVs4-GHw7hG8GDUg_KNKDJdZMq2AqIG9JeuktHzj0UTgkY5mtj5wPSAOPRuWwdlb4up8gN1QZSPdyOGCBIiSEV0dXVn37sDgWCiIHoL_8o7AVbdpwNFKmJiHETPPWx6enaYt3wX8AVoy/s640/Screen+Shot+2018-01-05+at+04.03.37.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
자동완성이 된다.&lt;/div&gt;
&lt;br /&gt;
product 아래&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
admin.py&lt;br /&gt;
apps.py&lt;br /&gt;
models.py&lt;br /&gt;
tests.py&lt;br /&gt;
views.py&lt;/blockquote&gt;
이런 파일들이 생겼다. rails 생각이 난다.&lt;br /&gt;
내용물은 텅텅 비어서 실망스럽다.&lt;br /&gt;
comment라도 있을 줄 알았는데.&lt;br /&gt;
여튼 텍스트로 hello world 같은 건 안찍어본다. 바로 rest API&lt;br /&gt;
&lt;a href=&quot;http://www.django-rest-framework.org/tutorial/quickstart/&quot;&gt;http://www.django-rest-framework.org/tutorial/quickstart/&lt;/a&gt;&lt;br /&gt;
돌입한다.&lt;br /&gt;
pip install djangorestframework&lt;br /&gt;
뭔가 설치했으니&lt;br /&gt;
requirements.txt로 pip freeze하여 깡통인 곳에서 설치할때 pip install -r requirements.txt로 설치할 수 있게 만들자.&lt;br /&gt;
pip freeze &amp;gt; requirements.txt&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
REST API framework는 설치했고 DB를 만들자.&lt;/div&gt;
&lt;div&gt;
먼저 migrate을 돌려주자.&lt;/div&gt;
&lt;div&gt;
option+R 상태에서 migrate 엔터&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
Running migrations:&lt;br /&gt;
&amp;nbsp; Applying contenttypes.0001_initial... OK&lt;br /&gt;
&amp;nbsp; Applying auth.0001_initial... OK&lt;br /&gt;
&amp;nbsp; Applying admin.0001_initial... OK&lt;br /&gt;
&amp;nbsp; Applying admin.0002_logentry_remove_auto_add... OK&lt;br /&gt;
&amp;nbsp; Applying contenttypes.0002_remove_content_type_name... OK&lt;br /&gt;
&amp;nbsp; Applying auth.0002_alter_permission_name_max_length... OK&lt;br /&gt;
&amp;nbsp; Applying auth.0003_alter_user_email_max_length... OK&lt;br /&gt;
&amp;nbsp; Applying auth.0004_alter_user_username_opts... OK&lt;br /&gt;
&amp;nbsp; Applying auth.0005_alter_user_last_login_null... OK&lt;br /&gt;
&amp;nbsp; Applying auth.0006_require_contenttypes_0002... OK&lt;br /&gt;
&amp;nbsp; Applying auth.0007_alter_validators_add_error_messages... OK&lt;br /&gt;
&amp;nbsp; Applying auth.0008_alter_user_username_max_length... OK&lt;br /&gt;
&amp;nbsp; Applying auth.0009_alter_user_last_name_max_length... OK&lt;br /&gt;
&amp;nbsp; Applying sessions.0001_initial... OK&lt;br /&gt;
Following files were affected&lt;br /&gt;
&amp;nbsp;/Users/spectrum/Documents/python/djex1/db.sqlite3&lt;/blockquote&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
이런 식으로 죽 올라가고 db.sqlite3 이란 파일이 생겼다.&lt;/div&gt;
&lt;div&gt;
로컬디비가 기본이 sqlite다.&lt;/div&gt;
&lt;div&gt;
pycharm의 툴을 이용해서 연결해놓자.&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBofgr1iKuQb8iFacbTEL-7UKyGJX5WDhLAVG_IOzxlviNkHp5gD2G_jwiiEoELRSOD71AWzRzU5XHq6R7dlyDcuEy4CUGXZIDNzPi7hcrHgNLzsFlKoujtc8nBW2IMkAMMQ-GCf2TJ_GO/s1600/Screen+Shot+2018-01-05+at+04.15.14.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;443&quot; data-original-width=&quot;454&quot; height=&quot;312&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBofgr1iKuQb8iFacbTEL-7UKyGJX5WDhLAVG_IOzxlviNkHp5gD2G_jwiiEoELRSOD71AWzRzU5XHq6R7dlyDcuEy4CUGXZIDNzPi7hcrHgNLzsFlKoujtc8nBW2IMkAMMQ-GCf2TJ_GO/s320/Screen+Shot+2018-01-05+at+04.15.14.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
아니 이렇게 좋은 것이?&lt;/div&gt;
만일 처음 sqlite data source를 추가하는 것이면 &lt;u&gt;Download&lt;/u&gt; missing driver files 라고 나올 수도 있는데 Download를 누르면 알아서 드라이버를 받는다.&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEheFVO38Gtuk_s_kqVsf0apvqU3P-51nSBOXI0qmCv8eqNKOzrGJGdCjGc8AgbDz-miOP88yrdXGesw9T52iu12TTkPgDVo1qaNSMBA38-w6MKUJuG0E7E9blLf5NU9R8IeBNakBwZHDFq3/s1600/Screen+Shot+2018-01-05+at+04.18.10.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;726&quot; data-original-width=&quot;847&quot; height=&quot;548&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEheFVO38Gtuk_s_kqVsf0apvqU3P-51nSBOXI0qmCv8eqNKOzrGJGdCjGc8AgbDz-miOP88yrdXGesw9T52iu12TTkPgDVo1qaNSMBA38-w6MKUJuG0E7E9blLf5NU9R8IeBNakBwZHDFq3/s640/Screen+Shot+2018-01-05+at+04.18.10.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
기본은 pycharm 에서 관리하는 경로인데 File: 에서 ... 을 눌러 현재 프로젝트의 db.sqlite3랑 연결하자.&lt;br /&gt;
Project에서 Copy Path 해놓은 걸 붙여넣기 하면 편하다.&lt;br /&gt;
Test Connection 눌러서 Successful 떨어지는 거 구경하고 OK&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4VL9DOAUTfxhy-EbzUG208oUvLAJ9lLSHb_RT44iWpkgROeIVvzBUftfaOig5fC5sxUHx_wDrZLXm1yxKzMPzMttsWAdncIPfgpt1A9UVN-DSiuvJ2mS7Wp2AfrJC9jrtUdF-a0MJyVta/s1600/Screen+Shot+2018-01-05+at+04.22.42.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;433&quot; data-original-width=&quot;458&quot; height=&quot;302&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4VL9DOAUTfxhy-EbzUG208oUvLAJ9lLSHb_RT44iWpkgROeIVvzBUftfaOig5fC5sxUHx_wDrZLXm1yxKzMPzMttsWAdncIPfgpt1A9UVN-DSiuvJ2mS7Wp2AfrJC9jrtUdF-a0MJyVta/s320/Screen+Shot+2018-01-05+at+04.22.42.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
기본 서비스가 훌륭&lt;/div&gt;
DB가 있으니 모델을 만들자.&lt;br /&gt;
product/models.py 에 자동완성을 써가면서 만들어 보았다.&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
from django.db import models&lt;br /&gt;
&lt;br /&gt;
class Product(models.Model):&lt;br /&gt;
&amp;nbsp; &amp;nbsp; id = models.AutoField(primary_key=True)&lt;br /&gt;
&amp;nbsp; &amp;nbsp; name = models.TextField()&lt;br /&gt;
&amp;nbsp; &amp;nbsp; price = models.IntegerField()&lt;br /&gt;
&amp;nbsp; &amp;nbsp; createdAt = models.DateTimeField(auto_now_add=True)&lt;/blockquote&gt;
오케이 makemigrations product 해보자&lt;br /&gt;
App &#39;product&#39; could not be found. Is it in INSTALLED_APPS?&lt;br /&gt;
&lt;div&gt;
하고 묻는다.&lt;/div&gt;
&lt;div&gt;
djex1/settings.py 에 INSTALLED_APPS를 수정하자.&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
INSTALLED_APPS = [&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &#39;django.contrib.admin&#39;,&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &#39;django.contrib.auth&#39;,&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &#39;django.contrib.contenttypes&#39;,&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &#39;django.contrib.sessions&#39;,&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &#39;django.contrib.messages&#39;,&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &#39;django.contrib.staticfiles&#39;,&lt;br /&gt;
&amp;nbsp; &amp;nbsp; # apps&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &#39;product&#39;,&lt;br /&gt;
]&lt;/blockquote&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
시키는 대로 했다.&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
Migrations for &#39;product&#39;:&lt;/div&gt;
&lt;div&gt;
&amp;nbsp; product/migrations/0001_initial.py&lt;/div&gt;
&lt;div&gt;
&amp;nbsp; &amp;nbsp; - Create model Product&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Following files were affected&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&amp;nbsp;/Users/spectrum/Documents/python/djex1/product/migrations/0001_initial.py&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
migrations 파일이 생겼다.&lt;/div&gt;
&lt;div&gt;
Database는 변함이 없는데&lt;/div&gt;
&lt;div&gt;
방금 생성한 migrations 를 가지고 실제 table들을 만들자&lt;/div&gt;
&lt;div&gt;
option+R 상태에서 migrate&lt;/div&gt;
&lt;div&gt;
그리고 python console 에서 확인해보자.&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
PyDev console: starting.&lt;br /&gt;
Python 3.6.2 (v3.6.2:5fd33b5926, Jul 16 2017, 20:11:06)&lt;br /&gt;
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin&lt;br /&gt;
Django 2.0.1&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; from product.models import Product&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; Product.objects.all()&lt;br /&gt;
&amp;lt;QuerySet []&amp;gt;&lt;/blockquote&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
좋아보인다. import를 하는 순간. Product가 watch에 보이는게 좋다.&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigrndpEHkXJDq9sLeb6n5a-Ll504H251v7wILePgKgeADJvDIPOg4AbyR9BjlcHahzlRt-tXpKMy9yPFIYzVTTjRRrQWUNGWN0zI-ZP9VIGMvCvDjPO-QP3gsF5IN7XSMeenOTFKqlrHNO/s1600/Screen+Shot+2018-01-05+at+05.51.26.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;239&quot; data-original-width=&quot;1191&quot; height=&quot;128&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigrndpEHkXJDq9sLeb6n5a-Ll504H251v7wILePgKgeADJvDIPOg4AbyR9BjlcHahzlRt-tXpKMy9yPFIYzVTTjRRrQWUNGWN0zI-ZP9VIGMvCvDjPO-QP3gsF5IN7XSMeenOTFKqlrHNO/s640/Screen+Shot+2018-01-05+at+05.51.26.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
db 를 새로고침하면&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEheUYbjGW2oCQIyqlEC1pvnhS-qvlqW_zFaxLRKd758VDJ6-SXtHDc6upzbkfA-Qi10kUvd9cKl0dd-zxH5KnfrXsK_jjACjbngEbr2BlFjKO715WNZ-RxMRFGkcmzv6Tz29I_a-JiaWOEf/s1600/Screen+Shot+2018-01-05+at+04.31.00.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;121&quot; data-original-width=&quot;259&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEheUYbjGW2oCQIyqlEC1pvnhS-qvlqW_zFaxLRKd758VDJ6-SXtHDc6upzbkfA-Qi10kUvd9cKl0dd-zxH5KnfrXsK_jjACjbngEbr2BlFjKO715WNZ-RxMRFGkcmzv6Tz29I_a-JiaWOEf/s1600/Screen+Shot+2018-01-05+at+04.31.00.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
테이블명이 엄하다.&lt;/div&gt;
&lt;div&gt;
업무명인 product에 entity명도 product 니 product_product로 나올 수 밖에.&lt;/div&gt;
&lt;div&gt;
&lt;a href=&quot;https://gist.github.com/perrygeo/5380196&quot;&gt;https://gist.github.com/perrygeo/5380196&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
graphviz로 ERD도 뽑아보자.&lt;/div&gt;
&lt;div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
brew install graphviz&lt;br /&gt;
pip install django-extensions&lt;br /&gt;
pip install pygraphviz&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div&gt;
쭉 설치하고&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
djex1/settings.py 에 INSTALLED_APPS에&amp;nbsp;&lt;a href=&quot;https://github.com/django-extensions/django-extensions&quot; target=&quot;_blank&quot;&gt;django-extensions&lt;/a&gt; 추가&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
INSTALLED_APPS = [&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &#39;django.contrib.admin&#39;,&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &#39;django.contrib.auth&#39;,&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &#39;django.contrib.contenttypes&#39;,&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &#39;django.contrib.sessions&#39;,&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &#39;django.contrib.messages&#39;,&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &#39;django.contrib.staticfiles&#39;,&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &#39;product&#39;,&lt;br /&gt;
&amp;nbsp; &amp;nbsp; # extension&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &lt;b&gt;&#39;django_extensions&#39;,&lt;/b&gt;]&lt;/blockquote&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
option+R에서&amp;nbsp;graph_models product -o test.png 뽑아본다.&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6bupVLJV2MKLZeYTajeQW75DMz0_1MZnsp7hVeKP7v8-Y5Y5-lmEL9l-n03mUjXO9iraGLjPVzbdKQuKLCnKXT5iSMxKpCfxs2vbaf9wjO92BAN_ISKhz6kU4Y3Qh-B4g9Lfuz_Y_sw6c/s1600/Screen+Shot+2018-01-05+at+05.06.29.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;113&quot; data-original-width=&quot;207&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6bupVLJV2MKLZeYTajeQW75DMz0_1MZnsp7hVeKP7v8-Y5Y5-lmEL9l-n03mUjXO9iraGLjPVzbdKQuKLCnKXT5iSMxKpCfxs2vbaf9wjO92BAN_ISKhz6kU4Y3Qh-B4g9Lfuz_Y_sw6c/s1600/Screen+Shot+2018-01-05+at+05.06.29.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
괜찮네.&lt;/div&gt;
&lt;div&gt;
어드민도 있었지. 한번 볼까.&lt;/div&gt;
&lt;div&gt;
option+R에서&amp;nbsp;createsuperuser&lt;/div&gt;
&lt;div&gt;
유저명/이메일/암호 다 넣고&lt;/div&gt;
&lt;div&gt;
녹색 삼각형 눌러서 django Rrrrrrrrrrrun!&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlna_OvhujU9tYpFYOfjTWZf2xl1bKmQbzvwlpkpyesIJ2Oe_OwU9mk-uY_FQ8SUKdTKg5fmrWDFR2WglL7-EYa8-zXGwyhTDb7uvPFkj_FumBKE5_NMvaQfWg50C6yA2LnUzXHCsw1jCq/s1600/Screen+Shot+2018-01-05+at+05.10.29.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;292&quot; data-original-width=&quot;1324&quot; height=&quot;140&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlna_OvhujU9tYpFYOfjTWZf2xl1bKmQbzvwlpkpyesIJ2Oe_OwU9mk-uY_FQ8SUKdTKg5fmrWDFR2WglL7-EYa8-zXGwyhTDb7uvPFkj_FumBKE5_NMvaQfWg50C6yA2LnUzXHCsw1jCq/s640/Screen+Shot+2018-01-05+at+05.10.29.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
createsuperuser로 진입하면 이렇게 된다.&lt;/div&gt;
&lt;div&gt;
groups/users가 기본이고 users에 admin계정 하나 달랑 들었다.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
view 만들어 볼 차례다.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
product/views.py 를 열고&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
from rest_framework.generics import ListAPIView&lt;br /&gt;
from rest_framework import serializers&lt;br /&gt;
from product.models import Product&lt;/blockquote&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
3개를 import 하자. ListAPIView하고 serializers는 이 view에서 사용할 것이고 대상인 모델은 product.models의 Product이다.&lt;/div&gt;
&lt;div&gt;
실제로 화면에 연결할 View를 만들기 전에 Product model을 가져와보자.&lt;/div&gt;
&lt;div&gt;
그냥은 가져올 수 없고&amp;nbsp;serializers.ModelSerializer 를 사용하는 serializer를 먼저 만들어야한다.&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
class ProductSerializer(serializers.ModelSerializer):&lt;br /&gt;
&amp;nbsp; &amp;nbsp; class Meta:&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; model = Product&lt;/blockquote&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
이정도면 일단 오케이&lt;/div&gt;
&lt;div&gt;
여기에 실제 url 과 연결할 View를 만들자&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
class ProductsView(ListAPIView):&lt;br /&gt;
&amp;nbsp; &amp;nbsp; queryset = Product.objects.all()&lt;br /&gt;
&amp;nbsp; &amp;nbsp; serializer_class = ProductSerializer&lt;/blockquote&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
중요한 건 queryset.&lt;/div&gt;
&lt;div&gt;
product/views.py는 이렇게 완성하자.&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
from rest_framework.generics import ListAPIView&lt;br /&gt;
from rest_framework import serializers&lt;br /&gt;
from product.models import Product&lt;br /&gt;
&lt;br /&gt;
class ProductSerializer(serializers.ModelSerializer):&lt;br /&gt;
&amp;nbsp; &amp;nbsp; class Meta:&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; model = Product&lt;br /&gt;
&lt;br /&gt;
class ProductsView(ListAPIView):&lt;br /&gt;
&amp;nbsp; &amp;nbsp; queryset = Product.objects.all()&lt;br /&gt;
&amp;nbsp; &amp;nbsp; serializer_class = ProductSerializer&lt;/blockquote&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
나머지는 연결하는 것만 남았다.&lt;/div&gt;
&lt;div&gt;
djex1/urls.py 의 최종모습은&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
from django.contrib import admin&lt;br /&gt;
from django.urls import include, path&lt;br /&gt;
from product.views import ProductsView&lt;br /&gt;
&lt;br /&gt;
urlpatterns = [&lt;br /&gt;
&amp;nbsp; &amp;nbsp; path(&#39;admin/&#39;, admin.site.urls),&lt;br /&gt;
&amp;nbsp; &amp;nbsp; path(&#39;product/&#39;, ProductsView.as_view()),&lt;br /&gt;
]&lt;/blockquote&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
이와 같다.&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
Class-based views&lt;/div&gt;
&lt;div&gt;
&amp;nbsp; &amp;nbsp; 1. Add an import:&amp;nbsp; from other_app.views import Home&lt;/div&gt;
&lt;div&gt;
&amp;nbsp; &amp;nbsp; 2. Add a URL to urlpatterns:&amp;nbsp; path(&#39;&#39;, Home.as_view(), name=&#39;home&#39;)&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
커멘트에 있는 내용대로 path와 View 클래스.as_view()로 연결했다.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiOuwJwi9myU4oeR39B4scvWS_DqoUKMtxA2FzQyB_FDjX3NyR-HzSoLi7fCEhA7cCJ65lN_55q6swWDfrY6vtyuLtWr2KNN8YXWrVTcW7AmQ6bnSKK6-tCQtRkBnkU9n1wRuPVUlrPGD51/s1600/Screen+Shot+2018-01-06+at+01.15.22.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;413&quot; data-original-width=&quot;683&quot; height=&quot;386&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiOuwJwi9myU4oeR39B4scvWS_DqoUKMtxA2FzQyB_FDjX3NyR-HzSoLi7fCEhA7cCJ65lN_55q6swWDfrY6vtyuLtWr2KNN8YXWrVTcW7AmQ6bnSKK6-tCQtRkBnkU9n1wRuPVUlrPGD51/s640/Screen+Shot+2018-01-06+at+01.15.22.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
API서버가 완성되었다.&lt;/div&gt;
&lt;div&gt;
http://127.0.0.1:13000/product/?format=json 하면 [] 값만 나오기도 한다.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
django admin을 이용해 model로 만든 테이블에 값을 입력해보자.&lt;/div&gt;
&lt;div&gt;
product/admin.py 에서&amp;nbsp;admin.site.register() 를 사용하여 model을 추가한다.&lt;/div&gt;
&lt;div&gt;
model은&amp;nbsp;from product.models import Product 있으니&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
from django.contrib import admin&lt;br /&gt;
from product.models import Product&lt;br /&gt;
admin.site.register(Product)&lt;/blockquote&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
이정도면 된다.&lt;br /&gt;
/admin 을 보자.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFFBFilSSdOhnXO66BCsx9qDYGSetPMZMgXLWLrOnvFyHmKg05oB4OUiuazTs5WbweeY9YdRtbOr38k7ZbG-gFcqGGup7fKZJtC_Czgd8OZ8JUeXkjOvWgFzvlTmRxEdZFbP0qu44dm150/s1600/Screen+Shot+2018-01-06+at+01.21.32.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;380&quot; data-original-width=&quot;630&quot; height=&quot;241&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFFBFilSSdOhnXO66BCsx9qDYGSetPMZMgXLWLrOnvFyHmKg05oB4OUiuazTs5WbweeY9YdRtbOr38k7ZbG-gFcqGGup7fKZJtC_Czgd8OZ8JUeXkjOvWgFzvlTmRxEdZFbP0qu44dm150/s400/Screen+Shot+2018-01-06+at+01.21.32.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
Product / Products가 나왔다.&lt;br /&gt;
+ Add 를 눌러서 데이터를 입력할 수 있다.&lt;br /&gt;
&lt;br /&gt;
relation을 만들기 위해 Coupon도 한번 만들어보자.&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
class Coupon(models.Model):&lt;br /&gt;
&amp;nbsp; &amp;nbsp; id = models.AutoField(primary_key=True)&lt;br /&gt;
&amp;nbsp; &amp;nbsp; name = models.CharField(max_length=255)&lt;br /&gt;
&amp;nbsp; &amp;nbsp; product_id = models.ForeignKey(&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Product,&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; on_delete=models.CASCADE,&lt;br /&gt;
&amp;nbsp; &amp;nbsp; )&lt;br /&gt;
&amp;nbsp; &amp;nbsp; createdAt = models.DateTimeField(auto_now_add=True)&lt;/blockquote&gt;
&lt;div&gt;
product_id라는 필드를 foreignkey로 product와 완결했다.&lt;/div&gt;
&lt;div&gt;
product삭제시에 같이 삭제되도록 models.CASCADE를 지정했다.&lt;/div&gt;
&lt;div&gt;
product/admin.py를 조금 손봐서&lt;/div&gt;
&lt;div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
from django.contrib import admin&lt;br /&gt;
from product.models import Product, Coupon&lt;br /&gt;
&lt;br /&gt;
class ProductAdmin(admin.ModelAdmin):&lt;br /&gt;
&amp;nbsp; &amp;nbsp; list_display = (&#39;id&#39;, &#39;name&#39;, &#39;price&#39;)&lt;br /&gt;
&lt;br /&gt;
class CouponAdmin(admin.ModelAdmin):&lt;br /&gt;
&amp;nbsp; &amp;nbsp; list_display = (&#39;id&#39;, &#39;name&#39;, &#39;product_id&#39;)&lt;br /&gt;
&lt;br /&gt;
admin.site.register(Product, ProductAdmin)&lt;br /&gt;
admin.site.register(Coupon, CouponAdmin)&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div&gt;
이렇게 만들고 django admin에서 값을 입력해보면&lt;/div&gt;
&lt;div&gt;
이 경우 Coupon -&amp;gt; Product 인 상태에서&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
1. Coupon을 기준으로 Product 접근:&lt;br /&gt;
&amp;nbsp; Coupon.objects.get(id=2).product_id.price # 한개&lt;br /&gt;
&amp;nbsp; Coupon.objects.filter(id=2).select_related() # 일치하는 여러개&lt;/div&gt;
&lt;div&gt;
2. Product를 기준으로 Coupon 접근:&lt;br /&gt;
&amp;nbsp; Product.objects.get(id=2).coupon_set.all() # 한개&lt;br /&gt;
&amp;nbsp;&amp;nbsp;Product.objects.filter(id=2).prefetch_related() # 일치하는 여러개&lt;/div&gt;
&lt;div&gt;
이 가능하다.&lt;/div&gt;
&lt;br /&gt;
이번에 처음 django를 써봤는데 안정적이고 완성도 높은 framework이네.&lt;br /&gt;
admin하나만 해도 꽤 훌륭해보인다.&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://spectrumdig.blogspot.com/feeds/3624157778851322423/comments/default' title='댓글'/><link rel='replies' type='text/html' href='http://spectrumdig.blogspot.com/2018/01/django-rest-api-practice-like-boss.html#comment-form' title='0개의 덧글'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/3624157778851322423'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/3624157778851322423'/><link rel='alternate' type='text/html' href='http://spectrumdig.blogspot.com/2018/01/django-rest-api-practice-like-boss.html' title='django REST API practice like a boss'/><author><name>spectrum</name><uri>http://www.blogger.com/profile/06120635035623961975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmBndCXQt7VetDprES9LlY0X5-ANrnNF_3wogEmlITqtTjn0e3SV4jbPXaCEBQ99kZHBHzKRavoekobOAHSwA6VzP0vawn1s3Zb6Oiwj1Tvd55J7pkyML3Lu7uYDt2HbzPmZSp9NZuntWj/s72-c/Screen+Shot+2018-01-05+at+03.57.47.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8182501247262747119.post-4860116491037345914</id><published>2018-01-02T18:23:00.000+09:00</published><updated>2018-01-06T05:27:45.862+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="firebase"/><category scheme="http://www.blogger.com/atom/ns#" term="functions"/><category scheme="http://www.blogger.com/atom/ns#" term="GraphQL"/><title type='text'>firebase functions 위에 올라간 apollo server 에 apollo engine 끼얹기</title><content type='html'>이전 글에서 계속.&lt;br /&gt;
&lt;a href=&quot;https://www.apollographql.com/docs/engine/setup-node.html&quot;&gt;https://www.apollographql.com/docs/engine/setup-node.html&lt;/a&gt;&amp;nbsp;보고 진행해보자.&lt;br /&gt;
미리 가입하고 키도 받아놓자.&lt;br /&gt;
ENGINE_API_KEY 를 잘 copy 해놓자.&lt;br /&gt;
app.use &#39;/graphql&#39;,&lt;br /&gt;
&amp;nbsp; bodyParser.json()&lt;br /&gt;
&amp;nbsp; graphqlExpress {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; schema&lt;br /&gt;
&amp;nbsp; &amp;nbsp; context: {}&lt;br /&gt;
&amp;nbsp; &amp;nbsp; tracing: true&lt;br /&gt;
&amp;nbsp; &amp;nbsp; cacheControl: true&lt;br /&gt;
&amp;nbsp; }&lt;br /&gt;
먼저 /graphql 쪽&amp;nbsp;graphqlExpress 에 두개의 키(tracing, cacheControl)를 추가하자.&lt;br /&gt;
&lt;br /&gt;
npm install --save&amp;nbsp;compression&amp;nbsp;apollo-engine&lt;br /&gt;
두 패키지를 설치하고&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
compression = require &#39;compression&#39;&lt;br /&gt;
{ Engine } = require &#39;apollo-engine&#39;&lt;/blockquote&gt;
&lt;div&gt;
압축과 엔진을 추가하고&lt;/div&gt;
&lt;div&gt;
engine을 하나 만들자.&lt;/div&gt;
&lt;div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
engine = new Engine&lt;br /&gt;
&amp;nbsp; engineConfig:&lt;br /&gt;
&amp;nbsp; &amp;nbsp; apiKey: &#39;service:acidsound-6459:wKt62uPKS9dmxmBhFh-cZA&#39;&lt;br /&gt;
&amp;nbsp; endpoint: &#39;/api/graphql&#39;&lt;br /&gt;
&amp;nbsp; graphqlPort: process.env.PORT or 80&lt;br /&gt;
engine.start()&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div&gt;
여기서 graphqlPort를 지정하지 않으면&amp;nbsp;&lt;/div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
Error: Neither &#39;graphqlPort&#39; nor process.env.PORT is set. In order for Apollo Engine to act as a proxy for your GraphQL server, it needs to know which port your GraphQL server is listening on (this is the port number that comes before &#39;/graphql&#39;). If you see this error, you should make sure to add e.g. &#39;graphqlPort: 1234&#39; wherever you call new Engine(...).&lt;/blockquote&gt;
&lt;div&gt;
이런 오류를 만나게 될 것이다. 80으로 지정해주자.&lt;/div&gt;
&lt;div&gt;
미들웨어로 engine과 compression을 추가하자.&lt;/div&gt;
&lt;div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
app = express()&lt;br /&gt;
app.use engine.expressMiddleware()&lt;br /&gt;
app.use compression()&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div&gt;
express 바로 아래에 넣어주자.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
마지막으로 graphqlExpress에 key 두개를 추가해주자.&lt;/div&gt;
&lt;div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
app.use &#39;/graphql&#39;,&lt;br /&gt;
&amp;nbsp; bodyParser.json()&lt;br /&gt;
&amp;nbsp; graphqlExpress {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; schema&lt;br /&gt;
&amp;nbsp; &amp;nbsp; context: {}&lt;br /&gt;
&amp;nbsp; &amp;nbsp; tracing: true&lt;br /&gt;
&amp;nbsp; &amp;nbsp; cacheControl: true&lt;br /&gt;
&amp;nbsp; }&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div&gt;
tracing과 cacheControl 까지 true로 해주면 된다.&lt;/div&gt;
&lt;div&gt;
deploy하고 다시 graphiql을 보면&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiS0dxfXfTwTzVfIyTBcNSUqufGsas-wp0s3HMg_8BHGm_CRm6Tdn5ElRmhKn5cghpSsQASA-4KaqbucAxIvF6Sd7n3Eies3XwD2PJdY52VHM8RWGOY7tycb5IbZSuUDiFFacKPquw3Mn0f/s1600/Screen+Shot+2018-01-01+at+19.29.48.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;461&quot; data-original-width=&quot;1366&quot; height=&quot;215&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiS0dxfXfTwTzVfIyTBcNSUqufGsas-wp0s3HMg_8BHGm_CRm6Tdn5ElRmhKn5cghpSsQASA-4KaqbucAxIvF6Sd7n3Eies3XwD2PJdY52VHM8RWGOY7tycb5IbZSuUDiFFacKPquw3Mn0f/s640/Screen+Shot+2018-01-01+at+19.29.48.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
extensions에 tracing과 cacheControl이 생겼다.&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
기분 탓인가 좀 느려진 듯하다.&lt;/div&gt;
&lt;div&gt;
하지만 다시 쿼리하면 빨라진다.&lt;/div&gt;
&lt;div&gt;
아무래도 중간에서 뭔가 하니 처음엔 그루브 타는 듯.&lt;/div&gt;
&lt;div&gt;
https://engine.apollographql.com 이 계속 AWAITING CONNECTION 상태여서 외부 연결이 안되서 그런가 싶어 Blaze(종량제) 요금제로 올렸는데 별 차이는 없는 듯.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
해다보니 firebase functions엔진 deploy가 매우 구리다. 느리고 실패하고 5번 연속실패 후 성공한 적도 있음.&lt;/div&gt;
&lt;div&gt;
이럴 바엔 Lambda&amp;nbsp;+ graphQL Engine&amp;nbsp;+ graphQL Server&amp;nbsp;+ FireStore 구성을 하는게 낫겠다 싶기도하네.&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://spectrumdig.blogspot.com/feeds/4860116491037345914/comments/default' title='댓글'/><link rel='replies' type='text/html' href='http://spectrumdig.blogspot.com/2018/01/firebase-functions-apollo-server-apollo.html#comment-form' title='0개의 덧글'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/4860116491037345914'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/4860116491037345914'/><link rel='alternate' type='text/html' href='http://spectrumdig.blogspot.com/2018/01/firebase-functions-apollo-server-apollo.html' title='firebase functions 위에 올라간 apollo server 에 apollo engine 끼얹기'/><author><name>spectrum</name><uri>http://www.blogger.com/profile/06120635035623961975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiS0dxfXfTwTzVfIyTBcNSUqufGsas-wp0s3HMg_8BHGm_CRm6Tdn5ElRmhKn5cghpSsQASA-4KaqbucAxIvF6Sd7n3Eies3XwD2PJdY52VHM8RWGOY7tycb5IbZSuUDiFFacKPquw3Mn0f/s72-c/Screen+Shot+2018-01-01+at+19.29.48.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8182501247262747119.post-8463910563735122285</id><published>2018-01-01T18:59:00.000+09:00</published><updated>2018-01-01T18:59:16.050+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="firebase"/><category scheme="http://www.blogger.com/atom/ns#" term="functions"/><category scheme="http://www.blogger.com/atom/ns#" term="GraphQL"/><title type='text'>firebase /w functions + graphQL backend 만들기</title><content type='html'>persistence 영역을 graphQL 로 일반화 하고 apollo engine 같은 cache를 사용하고 싶다.&lt;br /&gt;
&lt;br /&gt;
firebase의 functions를 통해 firebase를 불러오는 건 사실상 이중 작업인 것 같지만 apollo engine이 매우 맘에 들어 끌어들이고 싶다.&lt;br /&gt;
&lt;br /&gt;
먼저 해볼 것은 functions에 graphql을 집어넣고 정적데이터를 읽어오는 것 먼저 로컬에서 구현해본다.&lt;br /&gt;
&lt;br /&gt;
프로젝트 폴더를 생성하고&lt;br /&gt;
&lt;br /&gt;
firebase init functions 부터 하자.&lt;br /&gt;
프로젝트를 선택(혹은 생성하고&lt;br /&gt;
? What language would you like to use to write Cloud Functions? JavaScript&lt;br /&gt;
✔&amp;nbsp; Wrote functions/package.json&lt;br /&gt;
✔&amp;nbsp; Wrote functions/index.js&lt;br /&gt;
그냥 firebase init 하고 functions를 선택하는 것과는 달리 뭔가 기본 scaffold를 생성해줘서 좋다.&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
{&lt;br /&gt;&amp;nbsp; &quot;functions&quot;: {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &quot;source&quot;: &quot;functions&quot;&lt;br /&gt;&amp;nbsp; }&lt;br /&gt;}&lt;/blockquote&gt;
디폴트로 이렇게 해주자.&lt;br /&gt;
&lt;br /&gt;
index.coffee 로 get 테스트.&lt;br /&gt;
exports하는 놈 이름이 functions 이름이 되고 경로도 /[exports한 놈]/ 이 되는 점이 특징이다.&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
functions = require &#39;firebase-functions&#39;&lt;br /&gt;admin = require &#39;firebase-admin&#39;&lt;br /&gt;admin.initializeApp functions.config().firebase&lt;br /&gt;exports.addMessage = functions.https.onRequest (req, res)-&amp;gt;&lt;br /&gt;&amp;nbsp; original = req.query.text?&lt;br /&gt;&amp;nbsp; admin.firestore()&lt;br /&gt;&amp;nbsp; &amp;nbsp; .collection &#39;messages&#39;&lt;br /&gt;&amp;nbsp; &amp;nbsp; .add { original }&lt;br /&gt;&amp;nbsp; &amp;nbsp; .then (writeResult)-&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; res.json result: &quot;#{req.query.text} #{writeResult.id} added&quot;&lt;/blockquote&gt;
&lt;div&gt;
왜때문인지 모르겠는데 firebase serve --only functions 했을 떄 잘 작동하지 않는다.&lt;/div&gt;
&lt;div&gt;
firebase deploy --only functions 해주자.&lt;/div&gt;
&lt;div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
$ curl https://us-central1-xxxxx.cloudfunctions.net/addMessage\?text\=mama&lt;br /&gt;{&quot;result&quot;:&quot;mama u3rEkSMGDunsdqBpbX2g added&quot;}%&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div&gt;
잘 되는 것 같다.&lt;/div&gt;
&lt;div&gt;
db는 firebase가 아니라 firestore(beta)로 했다.&lt;/div&gt;
&lt;div&gt;
당연한 이야기지만&lt;/div&gt;
&lt;div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
functions = require &#39;firebase-functions&#39;&lt;br /&gt;exports.addMessage = functions.https.onRequest (req, res)-&amp;gt;&lt;br /&gt;&amp;nbsp; original = req.query.text or &#39;&#39;&lt;br /&gt;&amp;nbsp; res.json result: &quot;#{original} entered&quot;&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div&gt;
이런 것도 잘된다.&lt;/div&gt;
&lt;div&gt;
그럼 일단 apollo server를 끼얹어 보자. functions 디렉토리 아래에서&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
npm install --save apollo-server-express graphql-tools graphql express body-parser&lt;/div&gt;
&lt;div&gt;
패키지를 설치하고&lt;/div&gt;
&lt;div&gt;
express 먼저 테스트 해보자. functions이름은 api. 즉 /api 로 접근하게 하자.&lt;/div&gt;
&lt;div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
functions = require &#39;firebase-functions&#39;&lt;br /&gt;express = require &#39;express&#39;&lt;br /&gt;app = express()&lt;br /&gt;app.get &#39;*&#39;, (req, res)-&amp;gt;&lt;br /&gt;&amp;nbsp; res.send &quot;Hello express!&quot;&lt;br /&gt;exports.api = functions.https.onRequest app&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div&gt;
간단히 될거라 생각했으나. 안된다.&lt;/div&gt;
&lt;div&gt;
curl https://us-central1-xxxxx.cloudfunctions.net/api&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;a href=&quot;https://github.com/firebase/firebase-functions/issues/27&quot;&gt;https://github.com/firebase/firebase-functions/issues/27&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
Cannot GET null&lt;/div&gt;
&lt;div&gt;
오류가 난다. 나만 그런건 아니었구나.&lt;/div&gt;
&lt;div&gt;
curl https://us-central1-xxxxx.cloudfunctions.net/api/&lt;/div&gt;
&lt;div&gt;
이처럼 /를 추가하니까 작동하는데 /api 가 root가 되서 그런 것 같다.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
뭐 실제론 별 문제 아니니&amp;nbsp;&lt;a href=&quot;https://www.apollographql.com/docs/apollo-server/example.html&quot;&gt;https://www.apollographql.com/docs/apollo-server/example.html&lt;/a&gt; 보고 구현해보자.&lt;/div&gt;
&lt;div&gt;
주의할 점은 web frontend인 /graphiql이 바라보는 endpoint는 /graphql 이 아니라 /api/graphql 이 되는 점만 주의하면 된다.&lt;/div&gt;
&lt;div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
functions = require &#39;firebase-functions&#39;&lt;br /&gt;express = require &#39;express&#39;&lt;br /&gt;bodyParser = require &#39;body-parser&#39;&lt;br /&gt;{ graphqlExpress, graphiqlExpress } = require &#39;apollo-server-express&#39;&lt;br /&gt;{ makeExecutableSchema } = require &#39;graphql-tools&#39;&lt;br /&gt;# fixture&lt;br /&gt;books = [&lt;br /&gt;&amp;nbsp; &amp;nbsp; title: &quot;Harry Potter and the Sorcerer&#39;s stone&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; author: &#39;J.K. Rowling&#39;&lt;br /&gt;&amp;nbsp; ,&lt;br /&gt;&amp;nbsp; &amp;nbsp; title: &#39;Jurassic Park&#39;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; author: &#39;Michael Crichton&#39;&lt;br /&gt;]&lt;br /&gt;typeDefs = &quot;&quot;&quot;&lt;br /&gt;&amp;nbsp; type Query { books: [Book] }&lt;br /&gt;&amp;nbsp; type Book { title: String, author: String }&lt;br /&gt;&quot;&quot;&quot;&lt;br /&gt;resolvers =&lt;br /&gt;&amp;nbsp; Query:&lt;br /&gt;&amp;nbsp; &amp;nbsp; books: =&amp;gt; books&lt;br /&gt;schema = makeExecutableSchema { typeDefs, resolvers }&lt;br /&gt;app = express()&lt;br /&gt;# /api/graphql&lt;br /&gt;app.use &#39;/graphql&#39;,&lt;br /&gt;&amp;nbsp; bodyParser.json()&lt;br /&gt;&amp;nbsp; graphqlExpress { schema, context: {} }&lt;br /&gt;# /api/graphiql&lt;br /&gt;app.use &#39;/graphiql&#39;,&lt;br /&gt;&amp;nbsp; graphiqlExpress endpointURL: &#39;/api/graphql&#39;&lt;br /&gt;exports.api = functions.https.onRequest app&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div&gt;
deploy 하고 graphiql을 열어보자.&lt;/div&gt;
&lt;div&gt;
브라우저에서&amp;nbsp;https://us-central1-xxxxx.cloudfunctions.net/api/graphiql 로 접근하면&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgyqvDvz4b_BIRcfaansmyYNiPjKfAX4r9dKYS2o-sy7e4aV_7Fdbi_lX-O7Csu-SAe_f67u_XyFOov_tssw5gvsPYBZZZ-RIZ5fKkZLYuXrKcNllDLfWaOZdTP5KTOFvM0JTg-VCBm5N4F/s1600/Screen+Shot+2018-01-01+at+18.55.16.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;319&quot; data-original-width=&quot;1366&quot; height=&quot;148&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgyqvDvz4b_BIRcfaansmyYNiPjKfAX4r9dKYS2o-sy7e4aV_7Fdbi_lX-O7Csu-SAe_f67u_XyFOov_tssw5gvsPYBZZZ-RIZ5fKkZLYuXrKcNllDLfWaOZdTP5KTOFvM0JTg-VCBm5N4F/s640/Screen+Shot+2018-01-01+at+18.55.16.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
짜자자장!&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
아하하. 멋져멋져. 박수짝짝.&lt;/div&gt;
&lt;div&gt;
조금 쉬었다가 apollo engine이나 firebase 연결 같은 걸 시도해봐야겠다.&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://spectrumdig.blogspot.com/feeds/8463910563735122285/comments/default' title='댓글'/><link rel='replies' type='text/html' href='http://spectrumdig.blogspot.com/2018/01/firebase-w-functions-graphql-backend.html#comment-form' title='0개의 덧글'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/8463910563735122285'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/8463910563735122285'/><link rel='alternate' type='text/html' href='http://spectrumdig.blogspot.com/2018/01/firebase-w-functions-graphql-backend.html' title='firebase /w functions + graphQL backend 만들기'/><author><name>spectrum</name><uri>http://www.blogger.com/profile/06120635035623961975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgyqvDvz4b_BIRcfaansmyYNiPjKfAX4r9dKYS2o-sy7e4aV_7Fdbi_lX-O7Csu-SAe_f67u_XyFOov_tssw5gvsPYBZZZ-RIZ5fKkZLYuXrKcNllDLfWaOZdTP5KTOFvM0JTg-VCBm5N4F/s72-c/Screen+Shot+2018-01-01+at+18.55.16.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8182501247262747119.post-7917521857400058868</id><published>2017-12-21T20:10:00.000+09:00</published><updated>2017-12-22T01:56:09.630+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="cycle.js"/><category scheme="http://www.blogger.com/atom/ns#" term="cyclejs"/><category scheme="http://www.blogger.com/atom/ns#" term="driver"/><category scheme="http://www.blogger.com/atom/ns#" term="xs"/><title type='text'>cycle.js driver에 대한 정리.</title><content type='html'>cyclejs는 observable을 logic, subscribe를 effect로 각각 분리하고 서로 순환하는 구조인 full reactive framework 이다.&lt;br /&gt;
&lt;br /&gt;
예를 들면 1초(1000ms)마다 스트림을 발산하는 text$는 매번 fold(reduce)하여 1씩 증가하고 &quot;Second elapsed xx&quot;로 map하는&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
text$=xs.periodic(1000)&lt;br /&gt;
&amp;nbsp; .fold(prev=&amp;gt;prev+1,0)&lt;br /&gt;
&amp;nbsp; .map(i=&amp;gt;`Second elapsed ${i}`)&lt;/blockquote&gt;
를 logic.&lt;br /&gt;
이를 subscribe 하여 #app element의 텍스트로 넣는 것을&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
text$.subscrbie({&lt;br /&gt;
&amp;nbsp; next: str =&amp;gt; document.querySelector(&#39;#app&#39;).textContent = str&lt;br /&gt;
})&lt;/blockquote&gt;
를 effect로 볼 수 있다.&lt;br /&gt;
&lt;br /&gt;
이를 각각 함수로 구분하여&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
const sink = ()=&amp;gt; xs.periodic(1000)&lt;br /&gt;
&amp;nbsp; &amp;nbsp; .fold(prev=&amp;gt;prev+1,0)&lt;br /&gt;
&amp;nbsp; &amp;nbsp; .map(i=&amp;gt;`Second elapsed ${i}`);&lt;br /&gt;
const domDriver = text$ =&amp;gt; text$.subscribe({&lt;br /&gt;
&amp;nbsp; next: str =&amp;gt; document.querySelector(&#39;#app&#39;).textContent = str&lt;br /&gt;
});&lt;br /&gt;
domDriver(sink);&lt;/blockquote&gt;
이와 같이 재정의 할 수 있다.&lt;br /&gt;
같은&amp;nbsp; logic에 대해 DOM 렌더링과 log를 분리하려면 logDriver를 아래와 같이 추가하여&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
const logDriver = msg =&amp;gt; console.log(msg);&lt;br /&gt;
logDriver(sink);&lt;/blockquote&gt;
하여도 마찬가지.&lt;br /&gt;
subscribe한 객체의 observable만 있으면 DOM이건 console이건 canvas건 websocket이건 어느쪽이든 effect를 만들어 낼 수 있다.&lt;br /&gt;
&lt;br /&gt;
만일 위의 예에서 logDriver와 domDriver가 다른 logic을 수행한다면 어떨까?&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
const sinks = ()=&amp;gt; ({&lt;br /&gt;
&amp;nbsp; DOM: xs.periodic(1000)&lt;br /&gt;
&amp;nbsp; &amp;nbsp; .fold(prev=&amp;gt;prev+1,0)&lt;br /&gt;
&amp;nbsp; &amp;nbsp; .map(i=&amp;gt;`Second elapsed ${i}`),&lt;br /&gt;
&amp;nbsp; log: xs.periodic(1000)&lt;br /&gt;
&amp;nbsp; &amp;nbsp; .fold(prev=&amp;gt;prev+1,0)&lt;br /&gt;
})&lt;br /&gt;
domDriver(sinks.DOM);&lt;br /&gt;
logDriver(sinks.log);&lt;/blockquote&gt;
이와 같이 sinks로 만들고 각각의 driver들이 sinks의 키를 가져오면 될 것이다.&lt;br /&gt;
&lt;br /&gt;
이걸 일반화해보자. n개의 driver들이 있고 우리는 이걸 한번에 묶어서 실행하는 runner를 만들고 싶다.&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
const runner = (sinks, drivers)=&amp;gt; {&lt;br /&gt;
&amp;nbsp; for (idx in drivers) {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; drivers[idx] &amp;amp;&amp;amp; drivers[idx](sinks[idx]);&lt;br /&gt;
&amp;nbsp; }&lt;br /&gt;
}&lt;br /&gt;
runner(sinks, {&lt;br /&gt;
&amp;nbsp; DOM: domDriver,&lt;br /&gt;
&amp;nbsp; log: logDriver&lt;br /&gt;
});&lt;/blockquote&gt;
이와 같이 정리할 수 있다.&lt;br /&gt;
&lt;br /&gt;
여기서 좀 더 runner를 유연하게 해보자.&lt;br /&gt;
만일 domDriver 에서 하드코드로 &quot;#app&quot;인 element를 지정하는게 아니라 인자로 받게 하면 어떨까?&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
runner(sinks, {&lt;br /&gt;
&amp;nbsp; DOM: domDriver(&quot;#app&quot;),&lt;br /&gt;
&amp;nbsp; log: logDriver&lt;br /&gt;
});&lt;/blockquote&gt;
가령 이런 식으로 말이다.&lt;br /&gt;
그렇다면 function을 반환하는 함수를 &quot;실행&quot;해야한다.&lt;br /&gt;
그런 driver를 makeDomDriver라고 하고 구현해보자.&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
const makeDomDriver = target =&amp;gt;&lt;br /&gt;&amp;nbsp; text$ =&amp;gt; text$.subscribe({&lt;br /&gt;&amp;nbsp; &amp;nbsp; next: str =&amp;gt; document.querySelector(target).textContent = str&lt;br /&gt;&amp;nbsp; });&lt;/blockquote&gt;
그러면, 인자를 받아도 여전히 text$를 인자로 하는 무기명 함수를 지정한다.&lt;br /&gt;
logDriver도 같은 방식으로 정리하고 모아보면&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
const makeLogDriver = ()=&amp;gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;msg =&amp;gt; console.log(msg);&lt;br /&gt;
runner(sinks, {&lt;br /&gt;
&amp;nbsp; DOM: makeDomDriver(&quot;#app&quot;),&lt;br /&gt;
&amp;nbsp; log: makeLogDriver()&lt;br /&gt;
});&lt;/blockquote&gt;
이와 같은 형태로 만들 수 있다.&lt;br /&gt;
&lt;br /&gt;
이것이 cycle.js의 근간을 이루고 있는 아이디어이며.&lt;br /&gt;
요컨데 logic으로 effect를 만드는 함수를 driver라고 한다.</content><link rel='replies' type='application/atom+xml' href='http://spectrumdig.blogspot.com/feeds/7917521857400058868/comments/default' title='댓글'/><link rel='replies' type='text/html' href='http://spectrumdig.blogspot.com/2017/12/cyclejs-driver.html#comment-form' title='0개의 덧글'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/7917521857400058868'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/7917521857400058868'/><link rel='alternate' type='text/html' href='http://spectrumdig.blogspot.com/2017/12/cyclejs-driver.html' title='cycle.js driver에 대한 정리.'/><author><name>spectrum</name><uri>http://www.blogger.com/profile/06120635035623961975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8182501247262747119.post-5896895738488621311</id><published>2017-12-10T01:51:00.003+09:00</published><updated>2017-12-10T01:51:43.235+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="docker"/><category scheme="http://www.blogger.com/atom/ns#" term="linux"/><category scheme="http://www.blogger.com/atom/ns#" term="raspberryPi"/><category scheme="http://www.blogger.com/atom/ns#" term="raspbian"/><category scheme="http://www.blogger.com/atom/ns#" term="WiFi"/><category scheme="http://www.blogger.com/atom/ns#" term="zero"/><title type='text'>Rasbperry Pi Zero W 에 Docker 설치함. (/w raspbian)</title><content type='html'>w3c 컨퍼 갔다왔더니 docker를 다시 잘 써보고 싶어졌다.&lt;br /&gt;
&lt;br /&gt;
귀염둥이 Raspberry Pi Zero W를 꺼내서 ssh 접속하고 docker-ce를 설치해본다.&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
$ echo &quot;deb [arch=armhf] https://download.docker.com/linux/debian $(lsb_release -cs) stable&quot; | sudo tee /etc/apt/sources.list.d/docker.list&lt;br /&gt;deb [arch=armhf] https://download.docker.com/linux/debian jessie stable&lt;br /&gt;&lt;br /&gt;$ sudo apt update &amp;amp;&amp;amp; sudo apt install docker-ce -y --force-yes&lt;br /&gt;Get:1 https://download.docker.com jessie InRelease [20.2 kB]&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;br /&gt;Hit http://mirrordirector.raspbian.org jessie InRelease&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;br /&gt;Ign https://download.docker.com jessie InRelease&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;br /&gt;Hit https://download.docker.com jessie/stable armhf Packages&lt;br /&gt;Hit http://archive.raspberrypi.org jessie InRelease&lt;br /&gt;Hit http://mirrordirector.raspbian.org jessie/main armhf Packages&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;br /&gt;Get:2 https://download.docker.com jessie/stable Translation-en [584 B]&lt;br /&gt;Hit http://archive.raspberrypi.org jessie/main armhf Packages&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;br /&gt;Get:3 https://download.docker.com jessie/stable Translation-en [584 B]&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;br /&gt;Hit http://mirrordirector.raspbian.org jessie/contrib armhf Packages&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;br /&gt;Hit http://archive.raspberrypi.org jessie/ui armhf Packages&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;br /&gt;Get:4 https://download.docker.com jessie/stable Translation-en [584 B]&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;br /&gt;Hit http://mirrordirector.raspbian.org jessie/non-free armhf Packages&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;br /&gt;Hit http://mirrordirector.raspbian.org jessie/rpi armhf Packages&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;br /&gt;Get:5 https://download.docker.com jessie/stable Translation-en [584 B]&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;br /&gt;Get:6 https://download.docker.com jessie/stable Translation-en [584 B]&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;br /&gt;Ign https://download.docker.com jessie/stable Translation-en&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;br /&gt;Ign http://archive.raspberrypi.org jessie/main Translation-en&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;br /&gt;Ign http://archive.raspberrypi.org jessie/ui Translation-en&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;br /&gt;Ign http://mirrordirector.raspbian.org jessie/contrib Translation-en&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;br /&gt;Ign http://mirrordirector.raspbian.org jessie/main Translation-en&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;br /&gt;Ign http://mirrordirector.raspbian.org jessie/non-free Translation-en&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;br /&gt;Ign http://mirrordirector.raspbian.org jessie/rpi Translation-en&amp;nbsp; &amp;nbsp; &lt;br /&gt;Fetched 20.2 kB in 20s (968 B/s)&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;br /&gt;Reading package lists... Done&lt;br /&gt;Building dependency tree&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;br /&gt;Reading state information... Done&lt;br /&gt;85 packages can be upgraded. Run &#39;apt list --upgradable&#39; to see them.&lt;br /&gt;W: GPG error: https://download.docker.com jessie InRelease: The following signatures couldn&#39;t be verified because the public key is not available: NO_PUBKEY 7EA0A9C3F273FCD8&lt;br /&gt;Reading package lists... Done&lt;br /&gt;Building dependency tree&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;br /&gt;Reading state information... Done&lt;br /&gt;Selecting previously unselected package libltdl7:armhf.&lt;br /&gt;(Reading database ... 31449 files and directories currently installed.)tdl7 rsync&lt;br /&gt;Preparing to unpack .../libltdl7_2.4.2-1.11_armhf.deb ...&lt;br /&gt;&amp;nbsp; git-daemon-run git-daemon-sysvinit git-doc git-el git-email git-gui gitk gitweb git-arch&lt;br /&gt;&amp;nbsp; git-cvs git-mediawiki git-svn&lt;br /&gt;The following NEW packages will be installed:&lt;br /&gt;&amp;nbsp; aufs-tools cgroupfs-mount docker-ce git git-man libapparmor1 liberror-perl libltdl7 rsync&lt;br /&gt;0 upgraded, 9 newly installed, 0 to remove and 85 not upgraded.&lt;br /&gt;Need to get 22.4 MB of archives.&lt;br /&gt;After this operation, 104 MB of additional disk space will be used.&lt;br /&gt;WARNING: The following packages cannot be authenticated!&lt;br /&gt;&amp;nbsp; docker-ce&lt;br /&gt;Get:1 https://download.docker.com/linux/debian/ jessie/stable docker-ce armhf 17.09.1~ce-0~debian [17.8 MB]&lt;br /&gt;Get:2 http://mirrordirector.raspbian.org/raspbian/ jessie/main libltdl7 armhf 2.4.2-1.11 [42.5 kB]&lt;br /&gt;Get:3 http://mirrordirector.raspbian.org/raspbian/ jessie/main aufs-tools armhf 1:3.2+20130722-1.1 [91.0 kB]&lt;br /&gt;Get:4 http://mirrordirector.raspbian.org/raspbian/ jessie/main cgroupfs-mount all 1.1 [4572 B]&lt;br /&gt;Get:5 http://mirrordirector.raspbian.org/raspbian/ jessie/main libapparmor1 armhf 2.9.0-3 [58.1 kB]&lt;br /&gt;Get:6 http://mirrordirector.raspbian.org/raspbian/ jessie/main liberror-perl all 0.17-1.1 [22.4 kB]&lt;br /&gt;Get:7 http://mirrordirector.raspbian.org/raspbian/ jessie/main git-man all 1:2.1.4-2.1+deb8u5 [1268 kB]&lt;br /&gt;Get:8 http://mirrordirector.raspbian.org/raspbian/ jessie/main git armhf 1:2.1.4-2.1+deb8u5 [2689 kB]&lt;br /&gt;Get:9 http://mirrordirector.raspbian.org/raspbian/ jessie/main rsync armhf 3.1.1-3 [373 kB]&amp;nbsp; &amp;nbsp;&lt;br /&gt;Fetched 22.4 MB in 26s (855 kB/s)&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;br /&gt;Can&#39;t set locale; make sure $LC_* and $LANG are correct!&lt;br /&gt;perl: warning: Setting locale failed.&lt;br /&gt;perl: warning: Please check that your locale settings:&lt;br /&gt;&lt;span style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt;LANGUAGE = (unset),&lt;br /&gt;&lt;span style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt;LC_ALL = (unset),&lt;br /&gt;&lt;span style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt;LC_CTYPE = &quot;UTF-8&quot;,&lt;br /&gt;&lt;span style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt;LANG = &quot;en_GB.UTF-8&quot;&lt;br /&gt;&amp;nbsp; &amp;nbsp; are supported and installed on your system.&lt;br /&gt;perl: warning: Falling back to a fallback locale (&quot;en_GB.UTF-8&quot;).&lt;br /&gt;locale: Cannot set LC_CTYPE to default locale: No such file or directory&lt;br /&gt;locale: Cannot set LC_ALL to default locale: No such file or directory&lt;br /&gt;Processing triggers for man-db (2.7.0.2-5) ...##################..............................]&amp;nbsp;&lt;/blockquote&gt;
여기서 한참 걸린다.&lt;br /&gt;
sudo dpkg-reconfigure locales 하는 거 깜빡했네;;&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
W: GPG error: https://download.docker.com jessie InRelease: The following signatures couldn&#39;t be verified because the public key is not available: NO_PUBKEY 7EA0A9C3F273FCD8&lt;br /&gt;Reading package lists... Done&lt;br /&gt;Building dependency tree&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;br /&gt;Reading state information... Done&lt;br /&gt;E: Sub-process /usr/bin/dpkg returned an error code (1)&lt;/blockquote&gt;
&lt;div&gt;
이렇게 끝나버렸 ㅜㅜ&lt;/div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
$ sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 7EA0A9C3F273FCD8&lt;br /&gt;Executing: gpg --ignore-time-conflict --no-options --no-default-keyring --homedir /tmp/tmp.JOozHjzEhw --no-auto-check-trustdb --trust-model always --keyring /etc/apt/trusted.gpg --primary-keyring /etc/apt/trusted.gpg --keyserver keyserver.ubuntu.com --recv-keys 7EA0A9C3F273FCD8&lt;br /&gt;gpg: requesting key F273FCD8 from hkp server keyserver.ubuntu.com&lt;br /&gt;gpg: key 0EBFCD88: public key &quot;Docker Release (CE deb) &amp;lt;docker@docker.com&amp;gt;&quot; imported&lt;br /&gt;gpg: Total number processed: 1&lt;br /&gt;gpg:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;imported: 1&amp;nbsp; (RSA: 1)&lt;/blockquote&gt;
&lt;div&gt;
키 등록하고 재시도 해볼까나&lt;/div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
E: Sub-process /usr/bin/dpkg returned an error code (1)&lt;/blockquote&gt;
&lt;div&gt;
뜨지만 docker-ce가 이미 설치되있다고 해서.&lt;/div&gt;
&lt;div&gt;
docker 쳐봤더니&lt;/div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
Segmentaion fault&lt;/blockquote&gt;
&lt;div&gt;
어질;;;;&lt;/div&gt;
&lt;div&gt;
아 이건 안되겠네. oTL&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;a href=&quot;https://github.com/umiddelb/armhf/wiki/Get-Docker-up-and-running-on-the-RaspberryPi-(ARMv6)-in-four-steps-(Wheezy)&quot;&gt;https://github.com/umiddelb/armhf/wiki/Get-Docker-up-and-running-on-the-RaspberryPi-(ARMv6)-in-four-steps-(Wheezy)&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
이걸로 해봐야겠다. ARMv6 만만하지 않군;&lt;/div&gt;
&lt;div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
sudo apt-get -y remove `sudo dpkg --get-selections | grep -v &quot;deinstall&quot; | grep sound | sed s/install//`&lt;br /&gt;sudo apt-get -y remove `sudo dpkg --get-selections | grep -v &quot;deinstall&quot; | grep gnome | sed s/install//`&lt;br /&gt;sudo apt-get -y remove `sudo dpkg --get-selections | grep -v &quot;deinstall&quot; | grep lxde | sed s/install//`&lt;br /&gt;sudo apt-get -y remove `sudo dpkg --get-selections | grep -v &quot;deinstall&quot; | grep gtk | sed s/install//`&lt;br /&gt;sudo apt-get -y remove `sudo dpkg --get-selections | grep -v &quot;deinstall&quot; | grep desktop | sed s/install//`&lt;br /&gt;sudo apt-get -y remove `sudo dpkg --get-selections | grep -v &quot;deinstall&quot; | grep gstreamer | sed s/install//`&lt;br /&gt;sudo apt-get -y remove `sudo dpkg --get-selections | grep -v &quot;deinstall&quot; | grep avahi | sed s/install//`&lt;br /&gt;sudo apt-get -y remove `sudo dpkg --get-selections | grep -v &quot;deinstall&quot; | grep dbus | sed s/install//`&lt;br /&gt;sudo apt-get -y remove `sudo dpkg --get-selections | grep -v &quot;deinstall&quot; | grep freetype | sed s/install//`&lt;br /&gt;sudo apt-get -y autoremove&lt;br /&gt;sudo apt-get clean&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div&gt;
샤악 날리고.&lt;/div&gt;
&lt;div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
sudo sed -i &#39;s/wheezy/jessie/&#39; /etc/apt/sources.list&lt;br /&gt;sudo sed -i &#39;s/wheezy/jessie/&#39; /etc/apt/sources.list.d/raspi.list&lt;br /&gt;sudo apt-get update &amp;amp;&amp;amp; sudo apt-get -y upgrade # answer &#39;y&#39; to upcoming questions&lt;br /&gt;sudo apt-get -y dist-upgrade&lt;br /&gt;sudo init 6&lt;/blockquote&gt;
&lt;div&gt;
sudo apt-get -y dist-upgrade 요기서 쫌 오래걸림&lt;/div&gt;
&lt;div&gt;
여튼 재부팅 뒤 다시 접속&lt;/div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
sudo apt-get -y autoremove&lt;br /&gt;sudo apt-get -y purge $(dpkg -l | awk &#39;/^rc/ { print $2 }&#39;)&lt;br /&gt;sudo init 6&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div&gt;
또 재접속.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Hypriot team 님들걸로 설치 시도.&lt;/div&gt;
&lt;div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
curl -ks https://packagecloud.io/install/repositories/Hypriot/Schatzkiste/script.deb.sh | sudo bash&lt;br /&gt;sudo apt-get install docker-hypriot=1.10.3-1&lt;br /&gt;sudo sh -c &#39;usermod -aG docker $SUDO_USER&#39;&lt;br /&gt;sudo systemctl enable docker.service&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div&gt;
데몬이 안뜨니까 리붓하고 확인해보자. 결과는?&lt;/div&gt;
&lt;div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
$ sudo docker info&lt;br /&gt;Containers: 0&lt;br /&gt;&amp;nbsp;Running: 0&lt;br /&gt;&amp;nbsp;Paused: 0&lt;br /&gt;&amp;nbsp;Stopped: 0&lt;br /&gt;Images: 0&lt;br /&gt;Server Version: 1.10.3&lt;br /&gt;Storage Driver: overlay&lt;br /&gt;&amp;nbsp;Backing Filesystem: extfs&lt;br /&gt;Execution Driver: native-0.2&lt;br /&gt;Logging Driver: json-file&lt;br /&gt;Plugins:&lt;br /&gt;&amp;nbsp;Volume: local&lt;br /&gt;&amp;nbsp;Network: null host bridge&lt;br /&gt;Kernel Version: 4.9.35+&lt;br /&gt;Operating System: Raspbian GNU/Linux 8 (jessie)&lt;br /&gt;OSType: linux&lt;br /&gt;Architecture: armv6l&lt;br /&gt;CPUs: 1&lt;br /&gt;Total Memory: 434.1 MiB&lt;br /&gt;Name: sprpi0&lt;br /&gt;ID: KI4Y:B5FO:6MMS:J4PE:7EGH:TJDK:FTBC:RXJN:QTNZ:7XVS:YXYW:UJNR&lt;br /&gt;Debug mode (server): true&lt;br /&gt;&amp;nbsp;File Descriptors: 11&lt;br /&gt;&amp;nbsp;Goroutines: 20&lt;br /&gt;&amp;nbsp;System Time: 2017-12-09T16:39:46.019045602Z&lt;br /&gt;&amp;nbsp;EventsListeners: 0&lt;br /&gt;&amp;nbsp;Init SHA1: 0db326fc09273474242804e87e11e1d9930fb95b&lt;br /&gt;&amp;nbsp;Init Path: /usr/lib/docker/dockerinit&lt;br /&gt;&amp;nbsp;Docker Root Dir: /var/lib/docker&lt;br /&gt;WARNING: No swap limit support&lt;br /&gt;WARNING: No cpu cfs quota support&lt;br /&gt;WARNING: No cpu cfs period support&lt;br /&gt;WARNING: No cpuset support&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div&gt;
꺄하하하하하!&lt;/div&gt;
&lt;div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
$ docker version&lt;br /&gt;Client:&lt;br /&gt;&amp;nbsp;Version:&amp;nbsp; &amp;nbsp; &amp;nbsp; 1.10.3&lt;br /&gt;&amp;nbsp;API version:&amp;nbsp; 1.22&lt;br /&gt;&amp;nbsp;Go version:&amp;nbsp; &amp;nbsp;go1.4.3&lt;br /&gt;&amp;nbsp;Git commit:&amp;nbsp; &amp;nbsp;20f81dd&lt;br /&gt;&amp;nbsp;Built:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Thu Mar 10 22:23:48 2016&lt;br /&gt;&amp;nbsp;OS/Arch:&amp;nbsp; &amp;nbsp; &amp;nbsp; linux/arm&lt;br /&gt;Server:&lt;br /&gt;&amp;nbsp;Version:&amp;nbsp; &amp;nbsp; &amp;nbsp; 1.10.3&lt;br /&gt;&amp;nbsp;API version:&amp;nbsp; 1.22&lt;br /&gt;&amp;nbsp;Go version:&amp;nbsp; &amp;nbsp;go1.4.3&lt;br /&gt;&amp;nbsp;Git commit:&amp;nbsp; &amp;nbsp;20f81dd&lt;br /&gt;&amp;nbsp;Built:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Thu Mar 10 22:23:48 2016&lt;br /&gt;&amp;nbsp;OS/Arch:&amp;nbsp; &amp;nbsp; &amp;nbsp; linux/arm&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div&gt;
씐난다!&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDT3xBmpZq9-o0nzMmCOCnBKRTJAB_DiqLqDgtfQK0jDZxVSYi3OSvSY0pA56rT2Idesw3JtqcSqDrLxYxznKnBv_LQx8ypcfrTP59-CWF7Yc9VaKJVCd6NcY2Ye7NcmdFhpRQm0HZ95TN/s1600/Screen+Shot+2017-12-10+at+01.50.28.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;586&quot; data-original-width=&quot;575&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDT3xBmpZq9-o0nzMmCOCnBKRTJAB_DiqLqDgtfQK0jDZxVSYi3OSvSY0pA56rT2Idesw3JtqcSqDrLxYxznKnBv_LQx8ypcfrTP59-CWF7Yc9VaKJVCd6NcY2Ye7NcmdFhpRQm0HZ95TN/s1600/Screen+Shot+2017-12-10+at+01.50.28.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
오우!&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
hypriot님들 잘하시네. $SUDO_USER라는 게 없어서 그냥 pi 기본 유저 적어줌.&lt;/div&gt;
&lt;div&gt;
sudo sh -c &#39;usermod -aG docker pi&#39;&lt;/div&gt;
&lt;div&gt;
&lt;a href=&quot;http://blog.hypriot.com/post/build-smallest-possible-docker-image/&quot;&gt;http://blog.hypriot.com/post/build-smallest-possible-docker-image/&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
블로그도 읽어보고 좀 배워야겠다.&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://spectrumdig.blogspot.com/feeds/5896895738488621311/comments/default' title='댓글'/><link rel='replies' type='text/html' href='http://spectrumdig.blogspot.com/2017/12/rasbperry-pi-zero-w-docker-w-raspbian.html#comment-form' title='0개의 덧글'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/5896895738488621311'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/5896895738488621311'/><link rel='alternate' type='text/html' href='http://spectrumdig.blogspot.com/2017/12/rasbperry-pi-zero-w-docker-w-raspbian.html' title='Rasbperry Pi Zero W 에 Docker 설치함. (/w raspbian)'/><author><name>spectrum</name><uri>http://www.blogger.com/profile/06120635035623961975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDT3xBmpZq9-o0nzMmCOCnBKRTJAB_DiqLqDgtfQK0jDZxVSYi3OSvSY0pA56rT2Idesw3JtqcSqDrLxYxznKnBv_LQx8ypcfrTP59-CWF7Yc9VaKJVCd6NcY2Ye7NcmdFhpRQm0HZ95TN/s72-c/Screen+Shot+2017-12-10+at+01.50.28.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8182501247262747119.post-6847794899318288883</id><published>2017-12-03T23:01:00.002+09:00</published><updated>2017-12-04T02:19:43.523+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="android"/><category scheme="http://www.blogger.com/atom/ns#" term="daydream"/><category scheme="http://www.blogger.com/atom/ns#" term="lg"/><category scheme="http://www.blogger.com/atom/ns#" term="nougat"/><category scheme="http://www.blogger.com/atom/ns#" term="vr"/><title type='text'>LG G Pro 2 Custom Rom +Rooting 기록</title><content type='html'>선 루팅&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://forum.xda-developers.com/lg-g3/general/guide-root-lg-firmwares-kitkat-lollipop-t3056951&quot;&gt;https://forum.xda-developers.com/lg-g3/general/guide-root-lg-firmwares-kitkat-lollipop-t3056951&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
1.2버전 스크립트 모드로.&lt;br /&gt;
&lt;br /&gt;
echo Looking for LG serial port..&lt;br /&gt;
echo.&lt;br /&gt;
adb.exe wait-for-device&lt;br /&gt;
&lt;br /&gt;
여기에서 정체 상태여서 강제로 adb kill-server 했더니 알아서 재부팅하심.&lt;br /&gt;
폰에선 &quot;펌웨어 업데이트&quot;화면이 나오고&lt;br /&gt;
&lt;br /&gt;
Phone found at COM5!&lt;br /&gt;
&lt;br /&gt;
Rooting phone..&lt;br /&gt;
&lt;br /&gt;
If you don&#39;t see the SuperSu installer script runs within about a minute,&lt;br /&gt;
then the root failed.&lt;br /&gt;
&lt;br /&gt;
Author : blog.lvu.kr&lt;br /&gt;
SPECIAL COMMAND : ENTER, LEAVE&lt;br /&gt;
&lt;br /&gt;
##&lt;br /&gt;
&lt;br /&gt;
이런 상황이 됨. #이 느린 속도로 하나씩 증가하고 있음.&lt;br /&gt;
한 시간 기다려도 성과없음.&lt;br /&gt;
&lt;br /&gt;
뭐 몇번해봐도 안되고 결국 불안하긴 하지만 &lt;a href=&quot;http://kingroot.net/&quot;&gt;kingroot.net&lt;/a&gt;&amp;nbsp;로 설치&lt;br /&gt;
&lt;br /&gt;
롤리팝이니까&amp;nbsp;&lt;a href=&quot;https://drive.google.com/file/d/0B4kfGU1z_PClQ3AyM2lKOGR1S3M/view&quot;&gt;https://drive.google.com/file/d/0B4kfGU1z_PClQ3AyM2lKOGR1S3M/view&lt;/a&gt;&amp;nbsp;이걸 받아서 압축 풀기.&lt;br /&gt;
d838recovery.bat 만 있어서 당황스럽긴한데 실행하고 1번 하니까 리커버리 모드 진입까진 잘 되는 듯.&lt;br /&gt;
adb sideload 라는게 있어서 해봄.&lt;br /&gt;
Advance에서 adb sideload 선택하고 USB연결한 PC에서 adb sideload RR-N-v5.8.5-20171010-d838-Final.zip 하니까 착착 잘 진행됨.&lt;br /&gt;
&lt;br /&gt;
이제 흉측한 U+LTE 가 없어짐. ㅇㅋ&lt;br /&gt;
&lt;br /&gt;
군더더기 없고 깔끔한데 남은 건 gapps 를 설치해야함.&lt;br /&gt;
&lt;a href=&quot;http://opengapps.org/&quot;&gt;http://opengapps.org/&lt;/a&gt;&amp;nbsp;를 권해줘서 갔는데 지옥 느림.&lt;br /&gt;
ARM&amp;nbsp;+ 7.1&amp;nbsp;+ stock 이 기본이어서 그걸로 함.&lt;br /&gt;
한시간 정도 걸렸나 제길. Remix OS 할 때 처럼 쉽게 할순 없네.&lt;br /&gt;
sideload가 zip파일 압축 안풀어도 되고 맘에 들어서 똑같이 한번 더 시도.&lt;br /&gt;
&lt;br /&gt;
adb sideload open_gapps-arm-7.1-stock-20171202.zip (아마 날짜마다 조금씩 뒷부분은 다를 듯)&lt;br /&gt;
우째&lt;br /&gt;
&lt;br /&gt;
원래 목표는 여기까진데 하다보니 7.1 누가가 되면 DayDream 도 되겠네? 싶은 욕심이 발동.&lt;br /&gt;
&lt;br /&gt;
google VR Services 설치 시도.&lt;br /&gt;
&lt;a href=&quot;https://www.apkmirror.com/apk/google-inc/google-vr-services/google-vr-services-1-10-172754103-release/google-vr-services-1-10-172754103-android-apk-download/download/&quot;&gt;https://www.apkmirror.com/apk/google-inc/google-vr-services/google-vr-services-1-10-172754103-release/google-vr-services-1-10-172754103-android-apk-download/download/&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
$ adb install com.google.vr.vrcore_1.10.172754103-160729829_minAPI19\(armeabi-v7a\)\(nodpi\)_apkmirror.com.apk&lt;br /&gt;
Failed to install com.google.vr.vrcore_1.10.172754103-160729829_minAPI19(armeabi-v7a)(nodpi)_apkmirror.com.apk: Failure [INSTALL_FAILED_ALREADY_EXISTS: Attempt to re-install com.google.vr.vrcore without first uninstalling.]&lt;br /&gt;
&lt;br /&gt;
어맛? 이미 있다고?&lt;br /&gt;
&lt;br /&gt;
마찬가지로 apkmirror 에서 daydream 으로 검색해서 설치&lt;br /&gt;
&lt;a href=&quot;https://www.apkmirror.com/apk/google-inc/daydream-daydream/daydream-daydream-1-10-170912063-release/daydream-daydream-1-10-170912063-android-apk-download/download/&quot;&gt;https://www.apkmirror.com/apk/google-inc/daydream-daydream/daydream-daydream-1-10-170912063-release/daydream-daydream-1-10-170912063-android-apk-download/download/&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
이상태로는 검은 화면만 나온다.&lt;br /&gt;
/system/etc/permissions/handheld_core_hardware.xml 내용 중&lt;br /&gt;
&amp;lt;feature name=”android.software.vr.mode” /&amp;gt;&lt;br /&gt;
&amp;lt;feature name=”android.hardware.vr.high_performance” /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
두 줄 추가. 잘 보면 비슷한 내용이 comment 있는데 수정해주면 된다.&lt;br /&gt;
근데 이게 readonly 라서 미리 손을 좀 봐야하는데&lt;br /&gt;
&lt;br /&gt;
adb root 하고&lt;br /&gt;
adb remount &amp;lt;- 중요! 까지 해서 수정가능한 상태로 해놓고&lt;br /&gt;
adb shell 에서 nano로 수정하든 adb pull 로 받고 수정 후 push 하든 잘 됨.&lt;br /&gt;
remount를 안했더니 readonly 오류가 남.&lt;br /&gt;
&lt;br /&gt;
리붓하고 다시 daydream 했더니 daydream keyboard 깔라고 함.&lt;br /&gt;
아오 카드 인증까지 했는데 또?&lt;br /&gt;
&lt;a href=&quot;https://www.apkmirror.com/apk/google-inc/daydream-keyboard-daydream/daydream-keyboard-daydream-1-10-170912023-release/daydream-keyboard-daydream-1-10-170912023-android-apk-download/download/&quot;&gt;https://www.apkmirror.com/apk/google-inc/daydream-keyboard-daydream/daydream-keyboard-daydream-1-10-170912023-release/daydream-keyboard-daydream-1-10-170912023-android-apk-download/download/&lt;/a&gt;&lt;br /&gt;
또 하지 뭐.&lt;br /&gt;
결과는?&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhzUJn_hEgJ1Y1vRIVw_2SmuXjoHMOCbYOhSBjnTPb7irP8dGKwjGopt49N4K91tv1V-wgdx5Kc4aZTvkaX3o3o48dQK1rMVMcXlBSZb95FniIqEZ4rfD2ZsYq3xuMnPG0-Srz3ueGMV0TL/s1600/IMG_8159.JPG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1600&quot; data-original-width=&quot;1200&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhzUJn_hEgJ1Y1vRIVw_2SmuXjoHMOCbYOhSBjnTPb7irP8dGKwjGopt49N4K91tv1V-wgdx5Kc4aZTvkaX3o3o48dQK1rMVMcXlBSZb95FniIqEZ4rfD2ZsYq3xuMnPG0-Srz3ueGMV0TL/s320/IMG_8159.JPG&quot; width=&quot;240&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
아흑흑 ㅜㅜ&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgL3frm5yukgEwgI3WAmRihFd5xs30ISFn_qVVtMPIOwg3lKyGVxKuInkTLMWMJq2v9NIj_X1xB3nIICioWGYj9UfrrepjXgeoV_uL9cqrqAuspOhxHR7obuowOGEtftvEozmGe17rrzvX2/s1600/IMG_8160.JPG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1600&quot; data-original-width=&quot;1200&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgL3frm5yukgEwgI3WAmRihFd5xs30ISFn_qVVtMPIOwg3lKyGVxKuInkTLMWMJq2v9NIj_X1xB3nIICioWGYj9UfrrepjXgeoV_uL9cqrqAuspOhxHR7obuowOGEtftvEozmGe17rrzvX2/s320/IMG_8160.JPG&quot; width=&quot;240&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
ㅋㅋㅋㅋㅋ&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
DayDream Viewer는 안되고 컨트롤러는 잘된다.ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ 아 진짜 황당하네.&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
커스텀 롬 루팅도 해야겠지만 오늘은 여기까지 하자.&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
주말 이틀이 순삭이네.&lt;/div&gt;
&lt;br /&gt;
build.props를 joan(V30)으로 했는데 Daydream을 순정으로 설치할 수 있는 것 말곤 잇점이 없네.</content><link rel='replies' type='application/atom+xml' href='http://spectrumdig.blogspot.com/feeds/6847794899318288883/comments/default' title='댓글'/><link rel='replies' type='text/html' href='http://spectrumdig.blogspot.com/2017/12/lg-g-pro-2-custom-rom-rooting.html#comment-form' title='0개의 덧글'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/6847794899318288883'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/6847794899318288883'/><link rel='alternate' type='text/html' href='http://spectrumdig.blogspot.com/2017/12/lg-g-pro-2-custom-rom-rooting.html' title='LG G Pro 2 Custom Rom +Rooting 기록'/><author><name>spectrum</name><uri>http://www.blogger.com/profile/06120635035623961975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhzUJn_hEgJ1Y1vRIVw_2SmuXjoHMOCbYOhSBjnTPb7irP8dGKwjGopt49N4K91tv1V-wgdx5Kc4aZTvkaX3o3o48dQK1rMVMcXlBSZb95FniIqEZ4rfD2ZsYq3xuMnPG0-Srz3ueGMV0TL/s72-c/IMG_8159.JPG" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8182501247262747119.post-3342152818706104644</id><published>2017-11-28T03:52:00.002+09:00</published><updated>2017-11-30T08:41:22.215+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="git"/><category scheme="http://www.blogger.com/atom/ns#" term="HFS"/><category scheme="http://www.blogger.com/atom/ns#" term="osx"/><title type='text'>Mac 주의 - 약간 빡치는 git 대소문자 변환 파일명 변환(renaming)하는 법</title><content type='html'>Mac에서 종종 화나는 일 중 하나가 파일 시스템이 대소문자를 구분하지 않는 점이다.&lt;br /&gt;
가령, A.png를 a.png로 바꿔야할 경우 HFS 기준으론 전혀 바뀐 점이 없는 걸로 인식하는데 다소 화가난다.&lt;br /&gt;
하지만, 방법이 없는 것은 아닌데&lt;br /&gt;
&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;A.png를 임의의 다른 파일로 바꾼다. (ex. A_.png)&lt;/li&gt;
&lt;li&gt;1에서 바꾼 상태에서 staging 한다. (git add A_.png)&lt;/li&gt;
&lt;li&gt;1의 파일을 다시 원래 바꾸려고 했던 파일로 바꾼다. (mv A_.png a.png)&lt;/li&gt;
&lt;li&gt;3을 다시 staging 한다. (git add a.png)&lt;/li&gt;
&lt;li&gt;commit 한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;
&lt;a href=&quot;https://stackoverflow.com/a/40307511/880991&quot;&gt;https://stackoverflow.com/a/40307511/880991&lt;/a&gt;&amp;nbsp;에서 답변해준&amp;nbsp;Chris Barr 님에게 감사를.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;
* 추가&lt;br /&gt;
&lt;br /&gt;
실제로 해보니 git mv --force A.png a.png 가 더 간단하다. 이는 바로 staging 영역에 add까지 해준다.&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://spectrumdig.blogspot.com/feeds/3342152818706104644/comments/default' title='댓글'/><link rel='replies' type='text/html' href='http://spectrumdig.blogspot.com/2017/11/mac-git-renaming.html#comment-form' title='0개의 덧글'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/3342152818706104644'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/3342152818706104644'/><link rel='alternate' type='text/html' href='http://spectrumdig.blogspot.com/2017/11/mac-git-renaming.html' title='Mac 주의 - 약간 빡치는 git 대소문자 변환 파일명 변환(renaming)하는 법'/><author><name>spectrum</name><uri>http://www.blogger.com/profile/06120635035623961975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8182501247262747119.post-1682886356336925133</id><published>2017-11-11T07:38:00.002+09:00</published><updated>2017-11-11T07:38:16.453+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="graphiQL"/><title type='text'>graphql 연습 /w launchpad</title><content type='html'>&lt;a href=&quot;https://launchpad.graphql.com/mw9wkzv99&quot;&gt;https://launchpad.graphql.com/mw9wkzv99&lt;/a&gt;&lt;br /&gt;
단순 전체쿼리+조건쿼리+추가&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://graphql.org/graphql-js/passing-arguments/&quot;&gt;http://graphql.org/graphql-js/passing-arguments/&lt;/a&gt;&lt;br /&gt;
참고. random ID는 crypto 1.0.1 사용&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&amp;nbsp; type Query {&lt;br /&gt;&amp;nbsp; &amp;nbsp; Members: [member]&lt;br /&gt;&amp;nbsp; &amp;nbsp; getMember(id: ID!): member&lt;br /&gt;&amp;nbsp; }&lt;br /&gt;&amp;nbsp; type member {&lt;br /&gt;&amp;nbsp; &amp;nbsp; id: ID!&lt;br /&gt;&amp;nbsp; &amp;nbsp; text: String&lt;br /&gt;&amp;nbsp; }&lt;br /&gt;&amp;nbsp; input memberInput {&lt;br /&gt;&amp;nbsp; &amp;nbsp; text: String&lt;br /&gt;&amp;nbsp; }&lt;br /&gt;&amp;nbsp; type Mutation {&lt;br /&gt;&amp;nbsp; &amp;nbsp; addMember(member: memberInput): member&lt;br /&gt;&amp;nbsp; }&lt;/blockquote&gt;
&lt;div&gt;
SQL 정의. facebook 쪽은 스트링에 지지는 거 진짜 좋아하네. *.gql 파일이 있다고 하니 이해해주자.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
resolver는&lt;/div&gt;
&lt;div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
var buffer = [];&lt;br /&gt;const resolvers = {&lt;br /&gt;&amp;nbsp; Query: {&lt;br /&gt;&amp;nbsp; &amp;nbsp; Members: (root, args, context) =&amp;gt; {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; return buffer;&lt;br /&gt;&amp;nbsp; &amp;nbsp; },&lt;br /&gt;&amp;nbsp; &amp;nbsp; getMember: (id)=&amp;gt; {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; return buffer.find(o=&amp;gt;o.id)&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; },&lt;br /&gt;&amp;nbsp; Mutation: {&lt;br /&gt;&amp;nbsp; &amp;nbsp; addMember(_, {member}) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; const mm = { ...member, id:randomBytes(10).toString(&#39;hex&#39;) };&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; buffer.push(mm);&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; return mm;&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; }&lt;br /&gt;};&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div&gt;
평범 평범.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;a href=&quot;https://dev-blog.apollodata.com/tutorial-graphql-subscriptions-server-side-e51c32dc2951&quot;&gt;https://dev-blog.apollodata.com/tutorial-graphql-subscriptions-server-side-e51c32dc2951&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
다음으로 pub/sub 연습.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;a href=&quot;https://launchpad.graphql.com/xvn94n3ql&quot;&gt;https://launchpad.graphql.com/xvn94n3ql&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&amp;nbsp; type Subscription {&lt;br /&gt;&amp;nbsp; &amp;nbsp; memberAdded: member&lt;br /&gt;&amp;nbsp; }&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div&gt;
member가 added되는 순간을 감시.&lt;/div&gt;
&lt;div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
import { PubSub, withFilter } from &#39;graphql-subscriptions&#39;;&lt;br /&gt;const pubsub = new PubSub();&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div&gt;
pub/sub 관련 package 추가. filter는 조건 검색용으로 보임.&lt;/div&gt;
&lt;div&gt;
pub/sub은&amp;nbsp;&lt;a href=&quot;https://github.com/apollographql/graphql-subscriptions&quot;&gt;https://github.com/apollographql/graphql-subscriptions&lt;/a&gt;&amp;nbsp;를 사용한다.&lt;/div&gt;
&lt;div&gt;
추가 구현은 두군데.&lt;/div&gt;
&lt;div&gt;
먼제 Mutation addMember에서 이벤트를 발생하도록 함&lt;/div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; pubsub.publish(&#39;memberAdded&#39;, {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; memberAdded: mm&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; });&lt;/blockquote&gt;
&lt;div&gt;
이제 추가할때마다 memberAdded를 publish함.&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&amp;nbsp; Subscription: {&lt;/div&gt;
&lt;div&gt;
&amp;nbsp; &amp;nbsp; memberAdded: {&lt;/div&gt;
&lt;div&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; subscribe: () =&amp;gt; pubsub.asyncIterator(&#39;memberAdded&#39;),&lt;/div&gt;
&lt;div&gt;
&amp;nbsp; &amp;nbsp; }&lt;/div&gt;
&lt;div&gt;
&amp;nbsp; }&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
subscriptions을 만들어본다.&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
잘 안된다. 무엇이 문제일까.&lt;/div&gt;
&lt;div&gt;
&lt;a href=&quot;https://github.com/apollographql/launchpad&quot;&gt;https://github.com/apollographql/launchpad&lt;/a&gt;&amp;nbsp;예제를 좀 보자.&lt;/div&gt;
&lt;div&gt;
subscriptions 예제가 없다.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
혹시 issue엔 없을까&lt;/div&gt;
&lt;div&gt;
&lt;a href=&quot;https://github.com/apollographql/launchpad/issues/15&quot;&gt;https://github.com/apollographql/launchpad/issues/15&lt;/a&gt;&amp;nbsp;websocket을 지원안한다고 한다. 망했네.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://spectrumdig.blogspot.com/feeds/1682886356336925133/comments/default' title='댓글'/><link rel='replies' type='text/html' href='http://spectrumdig.blogspot.com/2017/11/graphql-w-launchpad.html#comment-form' title='0개의 덧글'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/1682886356336925133'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/1682886356336925133'/><link rel='alternate' type='text/html' href='http://spectrumdig.blogspot.com/2017/11/graphql-w-launchpad.html' title='graphql 연습 /w launchpad'/><author><name>spectrum</name><uri>http://www.blogger.com/profile/06120635035623961975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8182501247262747119.post-2089473573427951010</id><published>2017-11-05T05:56:00.001+09:00</published><updated>2017-11-05T20:49:34.445+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="prototyping"/><category scheme="http://www.blogger.com/atom/ns#" term="repl"/><title type='text'>즐거운 Online Prototyping Tool 들</title><content type='html'>jsbin, codepen, jsfiddle 이런 것들은 일단 생략. 너무 유명한 것들이라.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://launchpad.graphql.com/&quot;&gt;https://launchpad.graphql.com&lt;/a&gt;&amp;nbsp;- node.js 기반 graphQL 연습장. 이것만으로도 충분히 훌륭한 백엔드&lt;br /&gt;
&lt;a href=&quot;https://codesandbox.io/&quot;&gt;https://codesandbox.io/&lt;/a&gt;&amp;nbsp;npm 사용이 가능한 클라이언트 사이드 연습장. webpackbin이 너무 문제가 많아서 찾아본 것.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://scrimba.com/&quot;&gt;https://scrimba.com&lt;/a&gt;&amp;nbsp;이건 codesandbox+ asciinema(&lt;a href=&quot;https://asciinema.org/&quot;&gt;https://asciinema.org/&lt;/a&gt;) 같은 느낌인데 키 녹화와 음성 녹화 기능이 추가되었다. 다 좋은데 화살표 키로 빨리감기 뒤로감기 기능이 안되고 익스포트(youtube등)으로 지원이 없는게 아쉽다.&lt;br /&gt;
&lt;br /&gt;
이 둘이 만나면?&amp;nbsp;&lt;a href=&quot;https://codesandbox.io/s/jvlrl98xw3?from-embed&quot;&gt;https://codesandbox.io/s/jvlrl98xw3?from-embed&lt;/a&gt;&lt;br /&gt;
뭐야 이거 무서워 하지마 ㄷㄷ;&amp;nbsp; graphql+react-native-web(부왘ㅋㅋ)&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://repl.it/languages&quot;&gt;https://repl.it/languages&lt;/a&gt;&amp;nbsp;전통을 자랑하는 REPL 도구.&amp;nbsp;지원 언어 종류가 -_-;;;;;&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://tio.run/#&quot;&gt;https://tio.run/#&lt;/a&gt;&amp;nbsp;repl.it? 장난함? 얘는 지원 언어가 무려 386종류. J랑 아희도 있다.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://play.golang.org/&quot;&gt;https://play.golang.org/&lt;/a&gt;&amp;nbsp;즐거운 go playground. 소스 포멧팅 넘 좋아.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://decaffeinate-project.org/repl/&quot;&gt;http://decaffeinate-project.org/repl/&lt;/a&gt;&amp;nbsp;최고의 coffeescript REPL. 원래 용도는 coffee를 ecma6코드로 바꾸는 것이지만...&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://scaphold.io/&quot;&gt;https://scaphold.io&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.graph.cool/&quot;&gt;https://www.graph.cool/&lt;/a&gt;&amp;nbsp;graphql backend service. scaphold.io는 설치도 필요없는 클라우드. graphcool은 호스팅+클라우드 다있음. 둘 다 막상막하. 푸쉬서버도 되고 뭐 미친득.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://glitch.com/&quot;&gt;https://glitch.com/&lt;/a&gt;&amp;nbsp;gomix에서 결국 glitch로 안착.&amp;nbsp; node.js&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://www.shadertoy.com/&quot;&gt;https://www.shadertoy.com&lt;/a&gt;&amp;nbsp;잘하고 싶다! 쉐이다! 오디오도 된다!&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://glslsandbox.com/&quot;&gt;http://glslsandbox.com/&lt;/a&gt;&amp;nbsp;심플하고 가벼운 쉐이더 샌드박스.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://snack.expo.io/&quot;&gt;https://snack.expo.io/&lt;/a&gt;&amp;nbsp;expo+react-native 대안은 없다.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://appetize.io/&quot;&gt;https://appetize.io/&lt;/a&gt;&amp;nbsp;네이티브 (iOS/Android) 테스트 머신 빌려드립니다. 온라인으로요. snack.expo.io나 repl.it에서도 사용 중.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://clara.io/&quot;&gt;https://clara.io/&lt;/a&gt;&amp;nbsp;온라인 3d 에디터.</content><link rel='replies' type='application/atom+xml' href='http://spectrumdig.blogspot.com/feeds/2089473573427951010/comments/default' title='댓글'/><link rel='replies' type='text/html' href='http://spectrumdig.blogspot.com/2017/11/online-prototyping-tool.html#comment-form' title='0개의 덧글'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/2089473573427951010'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/2089473573427951010'/><link rel='alternate' type='text/html' href='http://spectrumdig.blogspot.com/2017/11/online-prototyping-tool.html' title='즐거운 Online Prototyping Tool 들'/><author><name>spectrum</name><uri>http://www.blogger.com/profile/06120635035623961975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8182501247262747119.post-9009635480946440801</id><published>2017-11-01T04:02:00.000+09:00</published><updated>2017-11-01T04:04:41.476+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="expo"/><category scheme="http://www.blogger.com/atom/ns#" term="firebase"/><title type='text'>firebase 일기(+expo)</title><content type='html'>firebase instance를 만들자.&lt;br /&gt;
https://console.firebase.google.com/project/&amp;lt;YOURAPP&amp;gt;-90431/overview 로 들어가보면&lt;br /&gt;
Welcome to Firebase! Get started here. 라고 반긴다.&lt;br /&gt;
필요한 설정을 복사하자.&amp;nbsp;Add Firebase to your web app 를 눌러&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&amp;lt;script src=&quot;https://www.gstatic.com/firebasejs/4.6.0/firebase.js&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
&amp;nbsp; // Initialize Firebase&lt;br /&gt;
&amp;nbsp; var config = {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; apiKey: &quot;ㅌㅌㅌㅌㅌㅌㅌㅌ&quot;,&lt;br /&gt;
&amp;nbsp; &amp;nbsp; authDomain: &quot;ㅅㅅㅅㅅ-90431.firebaseapp.com&quot;,&lt;br /&gt;
&amp;nbsp; &amp;nbsp; databaseURL: &quot;https://ㅅㅅㅅㅅ-90431.firebaseio.com&quot;,&lt;br /&gt;
&amp;nbsp; &amp;nbsp; projectId: &quot;ㅅㅅㅅㅅ-90431&quot;,&lt;br /&gt;
&amp;nbsp; &amp;nbsp; storageBucket: &quot;ㅅㅅㅅㅅ-90431.appspot.com&quot;,&lt;br /&gt;
&amp;nbsp; &amp;nbsp; messagingSenderId: &quot;11111111111&quot;&lt;br /&gt;
&amp;nbsp; };&lt;br /&gt;
&amp;nbsp; firebase.initializeApp(config);&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;/blockquote&gt;
이 부분 중 config만 복사.&lt;br /&gt;
Authentication &amp;gt; sign-in method 에 몇개 연결하고 Template을 한번 슥 본다.&lt;br /&gt;
음 다국어 처리가 되있군. 굿.&lt;br /&gt;
Database, Storage, Hosting, Functions 죄다 켜고.&lt;br /&gt;
&lt;br /&gt;
Grow 에 Remote Config라는게 있어서 Software Version 같은 걸 한번 넣어봤다. 프로그래마틱하게도 설정 가능한 거 같다. 잘 모르겠다.&lt;br /&gt;
&lt;a href=&quot;https://firebase.google.com/docs/remote-config/?authuser=0&quot;&gt;https://firebase.google.com/docs/remote-config/?authuser=0&lt;/a&gt;&amp;nbsp;일단 킵&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://github.com/jsappme/react-native-firebase-starter&quot;&gt;https://github.com/jsappme/react-native-firebase-starter&lt;/a&gt;&lt;br /&gt;
설정 볼만하다. code-push도 있고 무제한 push notification 서비스인&amp;nbsp;&lt;a href=&quot;https://onesignal.com/&quot;&gt;https://onesignal.com&lt;/a&gt;&amp;nbsp;도 눈에 띤다. 근데 firebase notification 도 무료에 무제한 아닌가?&lt;br /&gt;
Analytics, App Indexing, Authentication, Dynamic Links, Invites, Notifications, Crash Reporting &amp;amp; Remote Config 모두 무제한 무료 맞네.&lt;br /&gt;
&lt;br /&gt;
react-native(+expo)용 드라이버로는 고심끝에 그냥 npm install firebase --save 로 결정. 다른 것들은 문제가 있거나 deprecated 되거나 뭐 그렇네. 순정으로 가자.&lt;br /&gt;
&lt;br /&gt;
web에서 간단하게 연습.&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrwtMEudDddKNBUXkYST1IPytRmfY1fzDLYv6wugTRk8fWYzxud-j6Se3gRnzCq5ZtaARkUF0r39OF69_jyhL0M9r1sF81YFPSx1XQ-dMissxjEob6rO5dzM9WT5PJExDilVrFeKhp0dSn/s1600/Screen+Shot+2017-10-26+at+18.39.49.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;276&quot; data-original-width=&quot;445&quot; height=&quot;198&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrwtMEudDddKNBUXkYST1IPytRmfY1fzDLYv6wugTRk8fWYzxud-j6Se3gRnzCq5ZtaARkUF0r39OF69_jyhL0M9r1sF81YFPSx1XQ-dMissxjEob6rO5dzM9WT5PJExDilVrFeKhp0dSn/s320/Screen+Shot+2017-10-26+at+18.39.49.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
테스트 할 데이터는 이와 같다.&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
firebase.database()&lt;br /&gt;
&amp;nbsp; .ref(&#39;rooms/muka/messages&#39;)&lt;br /&gt;
&amp;nbsp; .on &#39;value&#39;, (o) -&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; # [{&quot;text&quot;:&quot;블라블라&quot;},{&quot;text&quot;:&quot;요로호초&quot;}]&lt;br /&gt;
&amp;nbsp; &amp;nbsp; console.log JSON.stringify(o.val())&lt;br /&gt;
# added 될 때 마다&lt;br /&gt;
firebase.database()&lt;br /&gt;
&amp;nbsp; .ref(&#39;rooms/muka/messages&#39;)&lt;br /&gt;
&amp;nbsp; .on &#39;child_added&#39;, (o) -&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; # key:0. val();&quot;text&quot;:&quot;블라블라&quot;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; # key:1. val();&quot;text&quot;:&quot;블라블라&quot;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; console.log JSON.stringify( Object.assign key: o.key, o.val() )&lt;/blockquote&gt;
.on에서 콜백으로 event를 처리하는데 value랑 child_added의 작동이 좀 다르다.&lt;br /&gt;
value는 전체 콜렉션을 한번에 가져오는 반면(근데 value라고 해도 react-native에선 그렇게 배열로 작동 안한다.&lt;br /&gt;
child_added는 Meteo의 observe처럼 건별로 발생한다.&lt;br /&gt;
&lt;br /&gt;
다음은 notification 차례&lt;br /&gt;
&lt;br /&gt;
Firebase console 에서 grow &amp;gt; Notification 가서 iOS든 Android든 하나 추가.&lt;br /&gt;
GoogleService-Info.plist 를 쓸일이 있을지 모르겠는데 일단 받아는 두었다.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://docs.expo.io/versions/latest/guides/push-notifications.html#content&quot;&gt;https://docs.expo.io/versions/latest/guides/push-notifications.html#content&lt;/a&gt;&lt;br /&gt;
Spoiler alert: it’s almost too easy. 쉬움 주의란닼ㅋㅋㅋ&lt;br /&gt;
일단 push 이니만큼 실기기를 준비하고 먼저 안드로이드 기준으로 테스트.&lt;br /&gt;
&lt;br /&gt;
1. 사용자 기기를 백엔드에 등록.&lt;br /&gt;
&lt;br /&gt;
expo를 navigation 포함 버전으로 생성했을 때 api/registerForPushNotificationsAsync.js 가 있는데&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
const PUSH_ENDPOINT = &#39;https://exponent-push-server.herokuapp.com/tokens&#39;;&lt;/blockquote&gt;
이 부분을 수정해준다.&lt;br /&gt;
즉, functions 를 만들어야할 곳은 여기.&lt;br /&gt;
functions를 만들고 보자 firebase cli가 있으니 firebase init 해본다.&lt;br /&gt;
&lt;br /&gt;
firebase database:get &#39;/rooms/muka/messages&#39; 오옹 이런걸로도 JSON 뽑아올 수 있네&lt;br /&gt;
&lt;br /&gt;
보통은 인증서도 등록하고 할게 많은데 expo란 앱이 이미 등록이 되있을 것이니 절차가 간소해지긴 하겠다.&lt;br /&gt;
&lt;br /&gt;
해야할 것은 일단 저 PUSH_ENDPOINT를&amp;nbsp;&lt;a href=&quot;https://github.com/exponent/exponent-server-sdk-node&quot;&gt;https://github.com/exponent/exponent-server-sdk-node&lt;/a&gt;&amp;nbsp;설치로 확인해보고 잘 되면 functions로 옮겨봐야하겠다.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://github.com/expo/exponent-server-sdk-node/blob/master/src/ExpoClient.js&quot;&gt;https://github.com/expo/exponent-server-sdk-node/blob/master/src/ExpoClient.js&lt;/a&gt;&lt;br /&gt;
요약하면 아래의 요청을 POST로 보내면 된다는 소리다.&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
https://exp.host/--/api/v2/push/send&lt;br /&gt;
accept: application/json&lt;br /&gt;
accept-encoding: gzip, deflate&lt;br /&gt;
content-type: application/json&lt;br /&gt;
curl -H &quot;Content-Type: application/json&quot; -X POST https://exp.host/--/api/v2/push/send -d &#39;{&lt;br /&gt;
&amp;nbsp; &quot;to&quot;: &quot;ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]&quot;,&lt;br /&gt;
&amp;nbsp; &quot;title&quot;:&quot;hello&quot;,&lt;br /&gt;
&amp;nbsp; &quot;body&quot;: &quot;world&quot;&lt;br /&gt;
}&#39;&lt;/blockquote&gt;
백엔드 서버는 바로 위의 curl의 역할을 수행하면 되고.&lt;br /&gt;
기기의 푸쉬토큰을 잘 기억하고 있으면 그만이다.&lt;br /&gt;
registerForPushNotificationsAsync.js 를 보면 이렇다. token 이외엔 보내주는게 없는데 일단 이대로라면 누가 누구인지 알 수 없으므로 전체 푸쉬를 하는 걸 구현한다고 생각하고 해보자.&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&amp;nbsp; // POST the token to our backend so we can use it to send pushes from there&lt;br /&gt;
&amp;nbsp; return fetch(PUSH_ENDPOINT, {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; method: &#39;POST&#39;,&lt;br /&gt;
&amp;nbsp; &amp;nbsp; headers: {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; Accept: &#39;application/json&#39;,&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &#39;Content-Type&#39;: &#39;application/json&#39;,&lt;br /&gt;
&amp;nbsp; &amp;nbsp; },&lt;br /&gt;
&amp;nbsp; &amp;nbsp; body: JSON.stringify({&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; token: {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; value: token,&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; },&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; /* 여기에 사용자 정보가 더 필요하다. */&lt;br /&gt;
&amp;nbsp; &amp;nbsp; }),&lt;br /&gt;
&amp;nbsp; });&lt;/blockquote&gt;
&lt;div&gt;
이걸 받아들일 수 있는지 해보자.&lt;/div&gt;
&lt;div&gt;
위의 fetch 부분을 curl로 쓰면 아마도&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
curl -H &quot;Content-Type: application/json&quot; -X POST https://localhost:3000 &#39;{&lt;/div&gt;
&lt;div&gt;
&amp;nbsp; &quot;token&quot;: {&lt;/div&gt;
&lt;div&gt;
&amp;nbsp; &amp;nbsp; value: &quot;xxxxxxxxxxxxx&quot;&lt;/div&gt;
&lt;div&gt;
&amp;nbsp; }&lt;/div&gt;
&lt;div&gt;
&#39;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
이정도만 받아들일 수 있으면 될 것이다.&lt;br /&gt;
&lt;br /&gt;
----------- ---------------&lt;br /&gt;
&lt;br /&gt;
열심히 쓴 글이 날아갔다;;;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;
안되겠다. 요약하자.&lt;br /&gt;
firebase functions를 사용하려면 node v6.11.1을 사용하자.&lt;br /&gt;
node v6.11.1은 Object.entries가 없다. polyfill(&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/entries#Polyfill&quot;&gt;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/entries#Polyfill&lt;/a&gt;) 을 이용하자.&lt;br /&gt;
appspot.com 에 올려서 외부 REST call을 쓰려고 했으나 실패. 무료에서 과금으로 전환.&lt;br /&gt;
orderBy를 썼다면 rules에 json파일을 추가해야함. orderBy를 쓰는 ref와 같은 depth로&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &quot;messages&quot;: {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &quot;.indexOn&quot;: [&quot;createdAt&quot;]&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div&gt;
와 같이 .indexOn을 넣어줘야함&lt;/div&gt;
&lt;div&gt;
functions에서 추가 데이터를 감지할 땐&amp;nbsp;functions.database.ref(&quot;rooms/{roomId}/messages/{messageId}&quot;).onCreate(event=&amp;gt;...)를 사용하고 event.data.val()과 event.data.key로 변경분을 체크.&lt;/div&gt;
&lt;div&gt;
functions안에서 데이터베이스를 다룰 땐 admin.database()객체를 사용. 쿼리도 once를 사용하여 한번만 사용한다. on을 사용하면 핸들러를 달아서 계속 감시하는 것이므로 적절치 않음. (예: admin.database().ref(&quot;deviceToken&quot;).once(&#39;value&#39;).then(tokenInfo=&amp;gt;...) )&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://spectrumdig.blogspot.com/feeds/9009635480946440801/comments/default' title='댓글'/><link rel='replies' type='text/html' href='http://spectrumdig.blogspot.com/2017/11/firebase-expo.html#comment-form' title='0개의 덧글'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/9009635480946440801'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/9009635480946440801'/><link rel='alternate' type='text/html' href='http://spectrumdig.blogspot.com/2017/11/firebase-expo.html' title='firebase 일기(+expo)'/><author><name>spectrum</name><uri>http://www.blogger.com/profile/06120635035623961975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrwtMEudDddKNBUXkYST1IPytRmfY1fzDLYv6wugTRk8fWYzxud-j6Se3gRnzCq5ZtaARkUF0r39OF69_jyhL0M9r1sF81YFPSx1XQ-dMissxjEob6rO5dzM9WT5PJExDilVrFeKhp0dSn/s72-c/Screen+Shot+2017-10-26+at+18.39.49.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8182501247262747119.post-6042760296363897634</id><published>2017-10-31T16:32:00.001+09:00</published><updated>2017-10-31T16:32:57.795+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="expo"/><category scheme="http://www.blogger.com/atom/ns#" term="react-native"/><title type='text'>React-Native App 개발 시 Expo 선택 전에 고려해야할 것.</title><content type='html'>Expo는 지옥같은 React-Native 개발 환경 아래 섬광처럼 빛나는 훌륭한 도구지만 빛이 있으면 어둠이 있는 법.&lt;br /&gt;
&lt;a href=&quot;https://docs.expo.io/versions/latest/introduction/why-not-expo.html&quot;&gt;https://docs.expo.io/versions/latest/introduction/why-not-expo.html&lt;/a&gt;&amp;nbsp;에선 이런 경우에 Expo를 사용을 고려하라고 전하고 있다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;독립형 앱에 이미지 같은 정적 자원(Assets)들을 함께 묶어 배포할 수 없다.&lt;/li&gt;
&lt;li&gt;background 혹은 장치가 잠자기 모드일때 코드 실행을 지원하지 않는다.&lt;/li&gt;
&lt;li&gt;일부 API를 미지원. Bluetooth, WebRTC 같은 건 아직.&lt;/li&gt;
&lt;li&gt;오버헤드가 꽤 크다. iOS 25MB, Android 20MB&lt;/li&gt;
&lt;li&gt;Push Notification의 경우 OneSignal 같은 외부서비스를 활용하려면 ExpoKit을 사용하거나 Expo가 아닌 React-Native를 사용해야한다.&lt;/li&gt;
&lt;li&gt;Staging/Production같은 다양한 디플로이 환경. 하지만 이 경우&amp;nbsp;&lt;a href=&quot;https://github.com/oliverbenns/expo-deploy&quot;&gt;https://github.com/oliverbenns/expo-deploy&lt;/a&gt;&amp;nbsp;같은 선택지도 있으니 참조할 것&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://spectrumdig.blogspot.com/feeds/6042760296363897634/comments/default' title='댓글'/><link rel='replies' type='text/html' href='http://spectrumdig.blogspot.com/2017/10/react-native-app-expo.html#comment-form' title='0개의 덧글'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/6042760296363897634'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/6042760296363897634'/><link rel='alternate' type='text/html' href='http://spectrumdig.blogspot.com/2017/10/react-native-app-expo.html' title='React-Native App 개발 시 Expo 선택 전에 고려해야할 것.'/><author><name>spectrum</name><uri>http://www.blogger.com/profile/06120635035623961975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8182501247262747119.post-242102334835635085</id><published>2017-10-27T01:26:00.002+09:00</published><updated>2017-10-27T01:26:54.399+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="coffeescript"/><category scheme="http://www.blogger.com/atom/ns#" term="firebase"/><title type='text'>Firebase coffeescript snippet</title><content type='html'>firebase 단순 예제 좋은게 없어서 찾다가 문서보고 하나 짰다.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWDhKqkk91vEhVACsCIB0yWNNPa8Z2QaTe5EcrLsohuNBx5rx9kUMBTCvsjOt8jKbwyx1zJcxd7pKh2uiTItOXbzdDIMgVT9zqN3test6Eh2ROV9bVRr-yLjgkC0IP77XxYme-aMeoHEGU/s1600/Screen+Shot+2017-10-27+at+01.06.36.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;311&quot; data-original-width=&quot;461&quot; height=&quot;215&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWDhKqkk91vEhVACsCIB0yWNNPa8Z2QaTe5EcrLsohuNBx5rx9kUMBTCvsjOt8jKbwyx1zJcxd7pKh2uiTItOXbzdDIMgVT9zqN3test6Eh2ROV9bVRr-yLjgkC0IP77XxYme-aMeoHEGU/s320/Screen+Shot+2017-10-27+at+01.06.36.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
흔한 Firebase 자료구조.png&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;a href=&quot;https://console.firebase.google.com/&quot;&gt;https://console.firebase.google.com&lt;/a&gt; 에서 본 Database 구조가 위와 같을 때&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
config =&lt;br /&gt;&amp;nbsp; apiKey: &#39;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&#39;&lt;br /&gt;&amp;nbsp; authDomain: &#39;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&#39;&lt;br /&gt;&amp;nbsp; databaseURL: &#39;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&#39;&lt;br /&gt;&amp;nbsp; projectId: &#39;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&#39;&lt;br /&gt;&amp;nbsp; storageBucket: &#39;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&#39;&lt;br /&gt;&amp;nbsp; messagingSenderId: &#39;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&#39;&lt;br /&gt;firebase.initializeApp config&lt;br /&gt;ref = firebase.database().ref(&#39;rooms/muka/messages&#39;) # database 경로&lt;br /&gt;ref.on &#39;child_added&#39;, (o) -&amp;gt;&lt;br /&gt;&amp;nbsp; # key:0. val();&quot;text&quot;:&quot;블라블라&quot;&lt;br /&gt;&amp;nbsp; # key:1. val();&quot;text&quot;:&quot;블라블라&quot;&lt;br /&gt;&amp;nbsp; console.log JSON.stringify Object.assign key: o.key, o.val()&lt;br /&gt;sendMessage = (message) -&amp;gt;&lt;br /&gt;&amp;nbsp; ref.push&lt;br /&gt;&amp;nbsp; &amp;nbsp; text: message.text&lt;br /&gt;&amp;nbsp; &amp;nbsp; date: (new Date()).toISOString()&lt;/blockquote&gt;
요정도면 읽고 쓰고가 가능.&lt;br /&gt;
firebase 패키지는 하위에 database() 를 통해 데이터베이스 접근이 가능하고 실제 데이터가 있는 노드는 ref(&#39;parent/child/child/child....&#39;) 와 같이 XPath 형식으로 찾아갈 수 있다.&lt;br /&gt;
&lt;br /&gt;
데이터를 읽기 위해선 .on 으로 이벤트를 붙여서 해당 ref의 자식들을 추가(child_added), 삭제(child_deleted), 수정(child_changed),이동(child_moved)를 감지하여 callback으로 해당 객체를 반환한다.&lt;br /&gt;
별다른 조건이 없다면 최초에 child_added를 감시하면 해당 node의 모든 값을 가져온다.&lt;br /&gt;
이는 mongodb+meteor 조합인 경우의 observe(&lt;a href=&quot;https://docs.meteor.com/api/collections.html#Mongo-Cursor-observe&quot;&gt;https://docs.meteor.com/api/collections.html#Mongo-Cursor-observe&lt;/a&gt;)랑 비슷하다.&lt;br /&gt;
&lt;br /&gt;
반면 쓰기의 경우는 간단한데 해당 ref에서 set/update하여 오브젝트를 변경하거나 push로 하위에 유니크한 키를 갖는 복수개의 오보젝트를 추가할 수 있다.&lt;br /&gt;
push에서 생성한 key는 on 에서 인자값의 .key 프로퍼티를 통해 접근할 수 있다.&lt;br /&gt;
&lt;br /&gt;
update는 변경된 부분만 덮어쓰지만 set은 아예 해당 하위트리를 재정의 하므로 주의깊게 사용하자.</content><link rel='replies' type='application/atom+xml' href='http://spectrumdig.blogspot.com/feeds/242102334835635085/comments/default' title='댓글'/><link rel='replies' type='text/html' href='http://spectrumdig.blogspot.com/2017/10/firebase-coffeescript-snippet.html#comment-form' title='0개의 덧글'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/242102334835635085'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/242102334835635085'/><link rel='alternate' type='text/html' href='http://spectrumdig.blogspot.com/2017/10/firebase-coffeescript-snippet.html' title='Firebase coffeescript snippet'/><author><name>spectrum</name><uri>http://www.blogger.com/profile/06120635035623961975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWDhKqkk91vEhVACsCIB0yWNNPa8Z2QaTe5EcrLsohuNBx5rx9kUMBTCvsjOt8jKbwyx1zJcxd7pKh2uiTItOXbzdDIMgVT9zqN3test6Eh2ROV9bVRr-yLjgkC0IP77XxYme-aMeoHEGU/s72-c/Screen+Shot+2017-10-27+at+01.06.36.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8182501247262747119.post-8580355322281411763</id><published>2017-10-13T20:08:00.001+09:00</published><updated>2017-10-13T20:15:15.609+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="meteor"/><category scheme="http://www.blogger.com/atom/ns#" term="nw.js"/><category scheme="http://www.blogger.com/atom/ns#" term="nwjs"/><title type='text'>meteor로 nw.js 개발하기.</title><content type='html'>실제로 nw.js 어플리케이션을 개발하다보면 UI구현하기 막막하고 수동으로 리프레쉬 하는 것도 귀찮아서 Meteor 연동을 하려고 했더니 생각보다 간단했다.&lt;br /&gt;
&lt;br /&gt;
디렉토리 구조는 먼저 이렇게 잡았다.&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
`- app&lt;br /&gt;
&amp;nbsp; `-client&lt;br /&gt;
&amp;nbsp; `-public &lt;br /&gt;
`- dist&lt;br /&gt;
&amp;nbsp; `- 배포용 html,css,js&lt;br /&gt;
&amp;nbsp; `- package.json&lt;br /&gt;
`- package.json&lt;/blockquote&gt;
아이디어는 이렇다. nw.js의 시작페이지를 http://localhost:3000으로 두고 배포시엔 meteor client 배포툴인 meteor-build-client를 사용하여 html,css,js 로 분리하는 계획이다.&lt;br /&gt;
&lt;br /&gt;
가장 중요한 nw.js 용 package.json 파일은 아래와 같이 구성한다.&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
{&lt;br /&gt;
&amp;nbsp; &quot;main&quot;: &quot;http://localhost:3000&quot;,&lt;br /&gt;
&amp;nbsp; &quot;node-remote&quot;: &quot;http://localhost:3000&quot;,&lt;br /&gt;
&amp;nbsp; &quot;name&quot;: &quot;&amp;lt;앱이름&amp;gt;&quot;&lt;br /&gt;
}&lt;/blockquote&gt;
이게 전부. 어떻게 보면 Web과 nw.js를 동시에 개발할 수도 있는 환경이기도 한 것이다.&lt;br /&gt;
meteor create app 을 해서 meteor용 앱을 만들고 meteor 를 시작한다.&lt;br /&gt;
그리고, 위의 package.json이 있는 경로로 돌아가서 nw . 으로 nwjs를 실행한다.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKsXiFPHRuOH-7cFC47DSmeg9uBbl2eCDWhk0sYgGsUXGRdFfj_e3IBPRYuceFiB8Xdx_e1ZCZ-XrmZq3k14_oMM_owWhQHopkdQx8_OneIWQJouNbBr4Q7JFamWxgkWP3R8IQj-jyj2pp/s1600/Screenshot+from+2017-10-13+19-38-00.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;410&quot; data-original-width=&quot;514&quot; height=&quot;255&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKsXiFPHRuOH-7cFC47DSmeg9uBbl2eCDWhk0sYgGsUXGRdFfj_e3IBPRYuceFiB8Xdx_e1ZCZ-XrmZq3k14_oMM_owWhQHopkdQx8_OneIWQJouNbBr4Q7JFamWxgkWP3R8IQj-jyj2pp/s320/Screenshot+from+2017-10-13+19-38-00.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
한번 번쩍하더니 잘 된다.&lt;br /&gt;
cordova 등에서 index.html 대신 http://localhost:3000을 하는 것도 비슷한 느낌이다.&lt;br /&gt;
&lt;br /&gt;
즐겁게 개발을 일단 마구 하고 실제로 배포하기 위해서는 개발환경이 아니라 html,css,js로 구성된 배포본을 만들어야한다.&lt;br /&gt;
npm install -g 해도 되지만 어짜피 Meteor에서만 쓸거&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
meteor npm install -g meteor-build-client&lt;/blockquote&gt;
해버릴거다.&lt;br /&gt;
&lt;br /&gt;
개발은 문제 없어 보이고 배포판을 한번 만들어보자. meteor app 이 있는 경로(meteor run으로 실행했던)로 가서&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
meteor meteor-build-client ../dist --path &quot;&quot;&lt;/blockquote&gt;
하고 ./dist 경로로 가서 확인해보자.&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
701c3455d4f16d4fef2c2396a32edf2b975cac2c.js&lt;br /&gt;
701c3455d4f16d4fef2c2396a32edf2b975cac2c.stats.json&lt;br /&gt;
index.html&lt;/blockquote&gt;
이런 식으로 생겼을거다. 아마 CSS추가하면 .css도 같이 생성될 것이다.&lt;br /&gt;
마찬가지로 ./dist/package.json 도 만들어주자. meteor-build-client 를 생성할 때 마다 dist 디렉토리가 새로 생성되므로 package-dist.json 같은 식으로 상위 디렉토리에 만들어두었다가 복사(cp ../package-dist.json package.json)해서 쓰자.&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
{&lt;br /&gt;
&amp;nbsp; &quot;name&quot;: &quot;firstOne&quot;,&lt;br /&gt;
&amp;nbsp; &quot;main&quot;: &quot;index.html&quot;&lt;br /&gt;
}&lt;/blockquote&gt;
간단하게 이렇게 구성했다. &lt;br /&gt;
자세한 내용은 이쪽(&lt;a href=&quot;https://github.com/nwjs/nw.js/wiki/manifest-format&quot;&gt;https://github.com/nwjs/nw.js/wiki/manifest-format&lt;/a&gt;)을 참조하자.&lt;br /&gt;
nw . 해서 실행해보자.&lt;br /&gt;
거짓말처럼 잘 실행이 된다.&lt;br /&gt;
&lt;br /&gt;
데스크톱 어플리케이션을 쓸일은 실무를 하다 보면 종종 있다.&lt;br /&gt;
배포가 간편하고 사용자가 URL을 입력할 필요가 없어 편리한데다가 오프라인에서도 잘 작동한다.&lt;br /&gt;
아마도 electron도 같은 원리로 작동할 것이라고 본다. nwjs는 XP같은 레거시 지원과 보안면에서 장점이 있어 쓰고 있다. 취향/상황에 맞게 사용해보자.</content><link rel='replies' type='application/atom+xml' href='http://spectrumdig.blogspot.com/feeds/8580355322281411763/comments/default' title='댓글'/><link rel='replies' type='text/html' href='http://spectrumdig.blogspot.com/2017/10/meteor-nwjs.html#comment-form' title='0개의 덧글'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/8580355322281411763'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/8580355322281411763'/><link rel='alternate' type='text/html' href='http://spectrumdig.blogspot.com/2017/10/meteor-nwjs.html' title='meteor로 nw.js 개발하기.'/><author><name>spectrum</name><uri>http://www.blogger.com/profile/06120635035623961975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKsXiFPHRuOH-7cFC47DSmeg9uBbl2eCDWhk0sYgGsUXGRdFfj_e3IBPRYuceFiB8Xdx_e1ZCZ-XrmZq3k14_oMM_owWhQHopkdQx8_OneIWQJouNbBr4Q7JFamWxgkWP3R8IQj-jyj2pp/s72-c/Screenshot+from+2017-10-13+19-38-00.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8182501247262747119.post-9170772731946328813</id><published>2017-10-10T04:12:00.000+09:00</published><updated>2017-10-10T04:12:39.929+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="bitcoin"/><category scheme="http://www.blogger.com/atom/ns#" term="blockchain"/><category scheme="http://www.blogger.com/atom/ns#" term="exchange"/><category scheme="http://www.blogger.com/atom/ns#" term="peatio"/><category scheme="http://www.blogger.com/atom/ns#" term="코인거래소"/><title type='text'>Peatio 분투기</title><content type='html'>흠 요즘은 ruby 할때 puma(https://github.com/puma/puma)를? 패신저는 안쓰남.&lt;br /&gt;
https://github.com/peatio/peatio/blob/master/doc/setup-local-osx.md&lt;br /&gt;
로컬 환경 먼저 잡자.&lt;br /&gt;
brew, rbenv 설치하고 ruby를 2.2.1로 설정하자.&lt;br /&gt;
gem install bundler을 설치&lt;br /&gt;
Fetching: bundler-1.15.4.gem (100%)&lt;br /&gt;
ERROR:  While executing gem ... (Gem::FilePermissionError)&lt;br /&gt;
You don&#39;t have write permissions for the /Library/Ruby/Gems/2.0.0 directory.&lt;br /&gt;
&lt;br /&gt;
sudo chown -R $USER /Library/Ruby/Gems/ 하고 나면&lt;br /&gt;
gem install bundler                    &lt;br /&gt;
Fetching: bundler-1.15.4.gem (100%)&lt;br /&gt;
ERROR:  While executing gem ... (Gem::FilePermissionError)&lt;br /&gt;
You don&#39;t have write permissions for the /usr/bin directory.&lt;br /&gt;
&lt;br /&gt;
또 오류. 에잇 귀찮아 sudo install bundler 해버림.&lt;br /&gt;
&lt;br /&gt;
mysql 설치&lt;br /&gt;
brew install mysql 설치하고&lt;br /&gt;
mysql.server start 기동.&lt;br /&gt;
&lt;br /&gt;
redis 설치&lt;br /&gt;
brew install redis&lt;br /&gt;
redis-server /usr/local/etc/redis.conf&lt;br /&gt;
&lt;br /&gt;
rabbitMQ 설치 얘는 /usr/local/sbin 여기에 설치되니&lt;br /&gt;
brew install rabbitmq&lt;br /&gt;
rabbitmq-server&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
bitcore 도 다운 받고 https://bitcoin.org/en/download&lt;br /&gt;
&lt;br /&gt;
mkdir -p ~/Library/Application\ Support/Bitcoin&lt;br /&gt;
touch ~/Library/Application\ Support/Bitcoin/bitcoin.conf&lt;br /&gt;
vim ~/Library/Application\ Support/Bitcoin/bitcoin.conf&lt;br /&gt;
다행히도 testnet 은 153GB씩 안하는 듯.&lt;br /&gt;
server=1&lt;br /&gt;
daemon=1&lt;br /&gt;
&lt;br /&gt;
# If run on the test network instead of the real bitcoin network&lt;br /&gt;
testnet=1&lt;br /&gt;
&lt;br /&gt;
# You must set rpcuser and rpcpassword to secure the JSON-RPC api&lt;br /&gt;
# Please make rpcpassword to something secure, `5gKAgrJv8CQr2CGUhjVbBFLSj29HnE6YGXvfykHJzS3k` for example.&lt;br /&gt;
# Listen for JSON-RPC connections on &lt;port&gt; (default: 8332 or testnet: 18332)&lt;br /&gt;
rpcuser=INVENT_A_UNIQUE_USERNAME&lt;br /&gt;
rpcpassword=INVENT_A_UNIQUE_PASSWORD&lt;br /&gt;
rpcport=18332&lt;br /&gt;
&lt;br /&gt;
# Notify when receiving coins&lt;br /&gt;
walletnotify=/usr/local/sbin/rabbitmqadmin publish routing_key=peatio.deposit.coin payload=&#39;{&quot;txid&quot;:&quot;%s&quot;, &quot;channel_key&quot;:&quot;satoshi&quot;}&#39;&lt;br /&gt;
&lt;br /&gt;
하고나서 데몬 띄우고 open /Applications/Bitcoin-Qt.app&lt;br /&gt;
&lt;br /&gt;
테스트넷이라 방심했더니 disk full.&lt;br /&gt;
일단 내려놓고 용량 확보 후 &lt;br /&gt;
brew install phantomjs&lt;br /&gt;
brew install imagemagick&lt;br /&gt;
&lt;br /&gt;
하면서 Peatio clone 함&lt;br /&gt;
git clone git://github.com/peatio/peatio.git&lt;br /&gt;
cd peatio&lt;br /&gt;
bundle install&lt;br /&gt;
bundle install 이 진짜 오래걸림.&lt;br /&gt;
그리고 root 암호도 종종 물어봄. 싹 귀찮네.&lt;br /&gt;
자다깨다를 반복.&lt;br /&gt;
Fetching ffi 1.9.3......... 오래걸린다...&lt;br /&gt;
&lt;br /&gt;
그러다가 갑자기.&lt;br /&gt;
Retrying download gem from https://rubygems.org/ due to error (2/4): Gem::RemoteFetcher::FetchError too many connection resets (https://rubygems.org/gems/ffi-1.9.3.gem)&lt;br /&gt;
엥 뭐지?&lt;br /&gt;
아몰랑 첨부터 다시 하니&lt;br /&gt;
&lt;br /&gt;
To see why this extension failed to compile, please check the mkmf.log which can be found here:&lt;br /&gt;
&lt;br /&gt;
  /Library/Ruby/Gems/2.0.0/extensions/universal-darwin-16/2.0.0/eventmachine-1.0.4/mkmf.log&lt;br /&gt;
&lt;br /&gt;
current directory:&lt;br /&gt;
/private/var/folders/9_/4p3ry0ks44g0jn2zl25cr8bw0000gn/T/bundler20171009-5586-48ljfbeventmachine-1.0.4/gems/eventmachine-1.0.4/ext&lt;br /&gt;
make &quot;DESTDIR=&quot; clean&lt;br /&gt;
&lt;br /&gt;
current directory:&lt;br /&gt;
/private/var/folders/9_/4p3ry0ks44g0jn2zl25cr8bw0000gn/T/bundler20171009-5586-48ljfbeventmachine-1.0.4/gems/eventmachine-1.0.4/ext&lt;br /&gt;
make &quot;DESTDIR=&quot;&lt;br /&gt;
compiling binder.cpp&lt;br /&gt;
In file included from binder.cpp:20:&lt;br /&gt;
./project.h:112:10: fatal error: &#39;openssl/ssl.h&#39; file not found&lt;br /&gt;
#include &lt;openssl ssl.h=&quot;&quot;&gt;&lt;br /&gt;
         ^~~~~~~~~~~~~~~&lt;br /&gt;
1 error generated.&lt;br /&gt;
make: *** [binder.o] Error 1&lt;br /&gt;
&lt;br /&gt;
make failed, exit code 2&lt;br /&gt;
&lt;br /&gt;
Gem files will remain installed in&lt;br /&gt;
/var/folders/9_/4p3ry0ks44g0jn2zl25cr8bw0000gn/T/bundler20171009-5586-48ljfbeventmachine-1.0.4/gems/eventmachine-1.0.4&lt;br /&gt;
for inspection.&lt;br /&gt;
Results logged to&lt;br /&gt;
/Library/Ruby/Gems/2.0.0/extensions/universal-darwin-16/2.0.0/eventmachine-1.0.4/gem_make.out&lt;br /&gt;
&lt;br /&gt;
An error occurred while installing eventmachine (1.0.4), and Bundler cannot continue.&lt;br /&gt;
Make sure that `gem install eventmachine -v &#39;1.0.4&#39;` succeeds before bundling.&lt;br /&gt;
&lt;br /&gt;
In Gemfile:&lt;br /&gt;
  amqp was resolved to 1.3.0, which depends on&lt;br /&gt;
    eventmachine&lt;br /&gt;
&lt;br /&gt;
아으 eventmachine이 오류. openssl 설치했던 것 같은데 아닌가?&lt;br /&gt;
export LD_LIBRARY_PATH=$(brew --prefix openssl)/lib&lt;br /&gt;
export CPATH=$(brew --prefix openssl)/include&lt;br /&gt;
export PKG_CONFIG_PATH=$(brew --prefix openssl)/lib/pkgconfig&lt;br /&gt;
하고 다시 하니 잘된다. (https://github.com/libimobiledevice/libimobiledevice/issues/389#issuecomment-289284190) 참조함.&lt;br /&gt;
&lt;br /&gt;
Depending on your version of ruby, you may need to install ruby rdoc/ri data:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;= 1.8.6 : unsupported
 = 1.8.7 : gem install rdoc-data; rdoc-data --install
 = 1.9.1 : gem install rdoc-data; rdoc-data --install
&amp;gt;= 1.9.2 : nothing to do! Yay!&lt;br /&gt;
&lt;br /&gt;
다 되었다. 진짜 오래 걸렸네.&lt;br /&gt;
그럼 db setup을 해볼까&lt;br /&gt;
$ bundle exec rake db:setup&lt;br /&gt;
Could not find aasm-3.4.0 in any of the sources&lt;br /&gt;
Run `bundle install` to install missing gems.&lt;br /&gt;
&lt;br /&gt;
bundle version 탓인가&lt;br /&gt;
$ gem install bundler --pre&lt;br /&gt;
ERROR:  While executing gem ... (Gem::FilePermissionError)&lt;br /&gt;
권한에서부터 뭔가 문제 있는거 아닌가.&lt;br /&gt;
&lt;br /&gt;
$ sudo gem install bundler --pre&lt;br /&gt;
Password:&lt;br /&gt;
Successfully installed bundler-1.16.0.pre.3&lt;br /&gt;
되긴 되었다.&lt;br /&gt;
&lt;br /&gt;
bundle exec rake db:setup 하니&lt;br /&gt;
$ bundle exec rake db:setup     &lt;br /&gt;
rake aborted!&lt;br /&gt;
Bundler::GemRequireError: There was an error while trying to load the gem &#39;mysql2&#39;.&lt;br /&gt;
Gem Load Error is: uninitialized constant Mysql2::Client::SECURE_CONNECTION&lt;br /&gt;
Backtrace for gem load error is:&lt;br /&gt;
&lt;br /&gt;
음. 또 버전 문제인가.&lt;br /&gt;
https://github.com/brianmario/mysql2/issues/754#issuecomment-291564933&lt;br /&gt;
이거 보고 재시도. DB 버전을 바꿨다.&lt;br /&gt;
&lt;br /&gt;
rake aborted!&lt;br /&gt;
ActiveRecord::StatementInvalid: Mysql2::Error: All parts of a PRIMARY KEY must be NOT NULL; if you need NULL in a key, use UNIQUE instead: CREATE TABLE `account_versions` (`id` int(11) DEFAULT NULL auto_increment PRIMARY KEY, `member_id` int(11), `account_id` int(11), `reason` int(11), `balance` decimal(32,16), `locked` decimal(32,16), `fee` decimal(32,16), `amount` decimal(32,16), `modifiable_id` int(11), `modifiable_type` varchar(255), `created_at` datetime, `updated_at` datetime, `currency` int(11), `fun` int(11)) ENGINE=InnoDB&lt;br /&gt;
&lt;br /&gt;
뭐냐 왜 Primary key가 null이라고;&lt;br /&gt;
https://github.com/peatio/peatio/issues/590&lt;br /&gt;
이 문제로구만.&lt;br /&gt;
https://stackoverflow.com/a/40758542 이걸보고 &lt;br /&gt;
config/initializers/abstract_mysql2_adapter.rb 에 아래를 추가함.&lt;br /&gt;
&lt;br /&gt;
require &#39;active_record/connection_adapters/mysql2_adapter&#39;&lt;br /&gt;
&lt;br /&gt;
class ActiveRecord::ConnectionAdapters::Mysql2Adapter&lt;br /&gt;
  NATIVE_DATABASE_TYPES[:primary_key] = &quot;int(11) auto_increment PRIMARY KEY&quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
우여곡절끝에 뜨긴 떴구만.&lt;br /&gt;
&lt;/openssl&gt;&lt;/port&gt;&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgiBNC5Ah29TjDcAm-QV0jKOHhqYC766MRtkp0Aoil8M0pSk8_xaWAyA1cEXGbn7PqOCeeVhsBF2B3cEWcB3ONAknbyKsrIql9bnnVGaMPeNbUTJLQ7BPkqX4JLgvL_gFiyWu00a3M1nZ_p/s1600/Screen+Shot+2017-10-10+at+04.08.38.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;671&quot; data-original-width=&quot;1364&quot; height=&quot;315&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgiBNC5Ah29TjDcAm-QV0jKOHhqYC766MRtkp0Aoil8M0pSk8_xaWAyA1cEXGbn7PqOCeeVhsBF2B3cEWcB3ONAknbyKsrIql9bnnVGaMPeNbUTJLQ7BPkqX4JLgvL_gFiyWu00a3M1nZ_p/s640/Screen+Shot+2017-10-10+at+04.08.38.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
눈물의 화면&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
개발환경은 이정도인데 OS X에서 하는 거 고통스럽다.&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
그냥 바로 linux로 가야할듯.&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://spectrumdig.blogspot.com/feeds/9170772731946328813/comments/default' title='댓글'/><link rel='replies' type='text/html' href='http://spectrumdig.blogspot.com/2017/10/peatio.html#comment-form' title='0개의 덧글'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/9170772731946328813'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/9170772731946328813'/><link rel='alternate' type='text/html' href='http://spectrumdig.blogspot.com/2017/10/peatio.html' title='Peatio 분투기'/><author><name>spectrum</name><uri>http://www.blogger.com/profile/06120635035623961975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgiBNC5Ah29TjDcAm-QV0jKOHhqYC766MRtkp0Aoil8M0pSk8_xaWAyA1cEXGbn7PqOCeeVhsBF2B3cEWcB3ONAknbyKsrIql9bnnVGaMPeNbUTJLQ7BPkqX4JLgvL_gFiyWu00a3M1nZ_p/s72-c/Screen+Shot+2017-10-10+at+04.08.38.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8182501247262747119.post-5320983585020109885</id><published>2017-09-22T16:25:00.003+09:00</published><updated>2017-09-27T00:02:10.567+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="coffeescript"/><category scheme="http://www.blogger.com/atom/ns#" term="meteor"/><category scheme="http://www.blogger.com/atom/ns#" term="vulcan"/><category scheme="http://www.blogger.com/atom/ns#" term="vulcanjs"/><title type='text'>VulcanJS를 coffeescript로 써본다면? Meteor 스타일로?</title><content type='html'>요즘은 다들 javascript 환경을 React 하나만을 위해 구성하는 경우가 꽤 많은데 사실 Meteor는 상당히 유연한 javascript platform이며 Meteor를 사용하는 Vulcan 역시도 그렇다.&lt;br /&gt;
&lt;br /&gt;
이번엔 따로 package를 만들지 않고 그냥 vulcan 프로젝트 위에서 해보자.&lt;br /&gt;
&lt;br /&gt;
import를 써서 빈틈없이 의존 관계를 명시적으로 정의하는 방법 외에도 어플리케이션 구조에 따른 load-order(&lt;a href=&quot;https://guide.meteor.com/structure.html#load-order&quot;&gt;https://guide.meteor.com/structure.html#load-order&lt;/a&gt;)를 가지고 자동으로 소스코드를 병합하는 방식도 지원한다. (원래는 이게 기본이었음)&lt;br /&gt;
&lt;br /&gt;
작동하는 방식은 간단한데 /client 로 시작하면 client에서 실행. /server 로 시작하면 server 에서 실행하는 것을 기본으로&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;HTML 템플릿 파일이 항상 먼저&lt;/li&gt;
&lt;li&gt;lib/ 디렉토리를 그 다음으로&lt;/li&gt;
&lt;li&gt;path가 깊은 곳을 그 다음으로&lt;/li&gt;
&lt;li&gt;파일은 전체 경로명에서 알파벳 순으로&lt;/li&gt;
&lt;li&gt;main*.* 인 파일은 마지막에&lt;/li&gt;
&lt;/ol&gt;불러오는 규칙을 가지고 있다. 만일 import를 꼭 쓰고 싶으면 import/ 디렉토리 안에 두면 된다.&lt;br /&gt;
&lt;br /&gt;
이 규칙에 따라 이전에 다루었던 요소들을 정리하여 디렉토리를 구성을 다음과 같이 해보았다.&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;\client&lt;br /&gt;
\lib&lt;br /&gt;
&amp;nbsp; \components&lt;br /&gt;
&amp;nbsp; &amp;nbsp; \Posts&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; PostsListComponent.coffee&lt;br /&gt;
&amp;nbsp; \lib&lt;br /&gt;
&amp;nbsp; &amp;nbsp; \lib&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; global.coffee&lt;br /&gt;
&amp;nbsp; &amp;nbsp; \models&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; \Posts&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; \imports&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; schema.coffee&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ..resolver.coffee, mutation.coffee...&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; collection.coffee&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; permissions.coffee&lt;br /&gt;
&amp;nbsp; routes.coffee&lt;br /&gt;
\server&lt;/blockquote&gt;의도하고자 하는 바는 이렇다.&lt;br /&gt;
&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;component, models 과 같은 기본 요소들은 client/server 양쪽에서 로드할 수 있도록 최상위 lib 디렉토리 아래에 두었고&lt;/li&gt;
&lt;li&gt;최초로 /lib/lib/global.coffee를&lt;/li&gt;
&lt;li&gt;그 다음으로 /lib/models 에서 collection 정의를&lt;/li&gt;
&lt;li&gt;그 다음으로 /components 에서 화면 구성 컴포넌트를&lt;/li&gt;
&lt;li&gt;그리고 마지막으로 routes.coffee 를&lt;/li&gt;
&lt;/ol&gt;&lt;div&gt;불러올 수 있도록 구성했다.&lt;br /&gt;
schema.coffee나 resolver.coffee, mutation.coffee 등등은 각각의 Collection에 의존적이어서 imports로 분리했다.&lt;br /&gt;
공통요소 &amp;gt; 모델(collection) &amp;gt; 컴포넌트(component) &amp;gt; 라우트(route) 순이라고 볼 수 있다.&lt;br /&gt;
&lt;br /&gt;
global.coffee 먼저 살펴보면 매번 자주 import 하는 React 객체와 Vulcan 객체 및 하위 객체들을 @를 사용하여&amp;nbsp;전역으로 분리하였다. 만일 하위 객체만 사용하고자하면&amp;nbsp;@React와 @Vulcan 대신 React, Vulcan으로 정의해도 상관은 없겠다.&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;@React = require &#39;react&#39;&lt;br /&gt;
{&lt;br /&gt;
&amp;nbsp;&amp;nbsp;@Component&lt;br /&gt;
&amp;nbsp;&amp;nbsp;@PropTypes&lt;br /&gt;
}=@React&lt;br /&gt;
@Vulcan = require &#39;meteor/vulcan:core&#39;&lt;br /&gt;
{&lt;br /&gt;
&amp;nbsp;&amp;nbsp;@addRoute&lt;br /&gt;
&amp;nbsp;&amp;nbsp;@Components&lt;br /&gt;
&amp;nbsp;&amp;nbsp;@createCollection&lt;br /&gt;
&amp;nbsp;&amp;nbsp;@getDefaultResolvers&lt;br /&gt;
&amp;nbsp;&amp;nbsp;@getDefaultMutations&lt;br /&gt;
&amp;nbsp;&amp;nbsp;@registerComponent&lt;br /&gt;
&amp;nbsp;&amp;nbsp;@replaceComponent&lt;br /&gt;
&amp;nbsp;&amp;nbsp;@withCurrentUser&lt;br /&gt;
&amp;nbsp;&amp;nbsp;@withDocument&lt;br /&gt;
&amp;nbsp;&amp;nbsp;@withEdit&lt;br /&gt;
&amp;nbsp;&amp;nbsp;@withList&lt;br /&gt;
&amp;nbsp;&amp;nbsp;@withMessages&lt;br /&gt;
&amp;nbsp;&amp;nbsp;@withMutation&lt;br /&gt;
&amp;nbsp;&amp;nbsp;@withNew&lt;br /&gt;
&amp;nbsp;&amp;nbsp;@withRemove&lt;br /&gt;
&amp;nbsp;&amp;nbsp;@Users&lt;br /&gt;
}=@Vulcan&lt;/blockquote&gt;생각나는 것만 일단 넣어봤다.&lt;br /&gt;
하위 객체를 coffee에서 그때그때 만들어도 좋을 것 같다는 생각이 든다.&lt;br /&gt;
&lt;br /&gt;
먼저 할 일은 component를 만드는 것이다.&lt;br /&gt;
그런데 coffeescript에서 JSX 지원을 하던가?&lt;br /&gt;
현재 coffeescript 2버전에선 한다. 하지만, meteor에선 아직 1.12.x 를 지원하고 있는데다가 coffeescript랑 JSX랑 별로 잘 어울리지 않는다고 생각한다.&lt;br /&gt;
괄호를 안쓰는 것이 coffeescript를 쓰는 사람의 즐거움이다. 그래서 html대신 jade, css대신 stylus를 쓰기도 하는데 JSX없이 render를 할 수 있나?&lt;br /&gt;
있다.&lt;br /&gt;
createElement(&lt;a href=&quot;https://facebook.github.io/react/docs/react-without-jsx.html&quot;&gt;https://facebook.github.io/react/docs/react-without-jsx.html&lt;/a&gt;)를 사용하자.&lt;br /&gt;
근데 좀 불편하다. attribute가 없는데도 매번 두번째 인자에 null을 써야하나?&lt;br /&gt;
불합리하다. 그래서 korat(&lt;a href=&quot;https://www.npmjs.com/package/korat&quot;&gt;https://www.npmjs.com/package/korat&lt;/a&gt;)이란 걸 만들었다.&lt;br /&gt;
아직 atmosphere에서 사용할 수는 없지만 업데이트 예정이다.&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;@k = -&amp;gt;&lt;br /&gt;
&amp;nbsp; a = Array.from arguments&lt;br /&gt;
&amp;nbsp; args = a&lt;br /&gt;
&amp;nbsp; args.splice 1,0,null if (a[1]?.$$typeof or&lt;br /&gt;
&amp;nbsp; &amp;nbsp; Array.isArray a[1]) or&lt;br /&gt;
&amp;nbsp; &amp;nbsp; (typeof a[1] isnt &#39;object&#39;)&lt;br /&gt;
&amp;nbsp; React.createElement.apply React, args&lt;/blockquote&gt;&lt;br /&gt;
/lib/lib/korat.coffee에 이렇게 붙여넣자.&lt;br /&gt;
Hyperscript(&lt;a href=&quot;https://github.com/hyperhype/hyperscript&quot;&gt;https://github.com/hyperhype/hyperscript&lt;/a&gt;)에 영향을 받았다.&lt;br /&gt;
k라고 전역에 선언하고 DOM을 만들자.&lt;br /&gt;
그리고 이 기회에 i,j,k,l 같은 한 글자짜리 for 문용 index 변수를 쓰는 습관도 버리자 :)&lt;br /&gt;
&lt;br /&gt;
/lib/components/Posts 디렉토리를 만들고 PostsListComponent.coffee를 korat을 사용하여 이렇게 해보자.&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;class PostsListComponent extrends Component&lt;br /&gt;
&amp;nbsp; render: -&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; k &quot;div&quot;,&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; k &quot;h1&quot;, &quot;Hello, VulcanJS&quot;&lt;br /&gt;
registerComponent &#39;PostsListComponent&#39;, PostsListComponent&lt;/blockquote&gt;엥? 이게 전부?&lt;br /&gt;
그렇다. 괜찮다고 생각이 들면&amp;nbsp;&lt;a href=&quot;https://github.com/acidsound/korat&quot;&gt;https://github.com/acidsound/korat&lt;/a&gt;&amp;nbsp;으로 가서 star를 찍어주자.&lt;br /&gt;
Component를 만들었으면 그 다음은 뭐?&lt;br /&gt;
그렇다 Route를 연결하자.&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;addRoute&lt;br /&gt;
&amp;nbsp; name: &quot;posts&quot;&lt;br /&gt;
&amp;nbsp; path: &quot;/&quot;&lt;br /&gt;
&amp;nbsp; componentName: &quot;PostsListComponent&quot;&lt;/blockquote&gt;/lib/routes.coffee에 이렇게 지정하면 끝이다.&lt;br /&gt;
주목할 점은 component: PostsListComponent를 사용하는 대신 componentName을 사용했는 것인데 PostsListComponent를 전역으로 정의하지 않아도 registerComponent에서 등록한 이름을 기준으로 컴포넌트를 연결할 수 있어서 좋다.&lt;br /&gt;
&lt;br /&gt;
Collection, Schema, Permission, Mutation, Resolver 등등은 그냥 쓰면 된다...지만 한 박자 쉬고 데이터관련 글을 이어가보자.&lt;br /&gt;
&lt;br /&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://spectrumdig.blogspot.com/feeds/5320983585020109885/comments/default' title='댓글'/><link rel='replies' type='text/html' href='http://spectrumdig.blogspot.com/2017/09/vulcanjs-coffeescript-meteor.html#comment-form' title='0개의 덧글'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/5320983585020109885'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/5320983585020109885'/><link rel='alternate' type='text/html' href='http://spectrumdig.blogspot.com/2017/09/vulcanjs-coffeescript-meteor.html' title='VulcanJS를 coffeescript로 써본다면? Meteor 스타일로?'/><author><name>spectrum</name><uri>http://www.blogger.com/profile/06120635035623961975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8182501247262747119.post-223921404996045259</id><published>2017-09-15T18:09:00.000+09:00</published><updated>2017-09-15T18:09:48.797+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="bug"/><category scheme="http://www.blogger.com/atom/ns#" term="meteor"/><category scheme="http://www.blogger.com/atom/ns#" term="package"/><category scheme="http://www.blogger.com/atom/ns#" term="tip"/><title type='text'>Troubleshooting - Meteor package가 적용이 되지 않을 때</title><content type='html'>버전 1.5 기준 package.js에서 Package.onUse 에 새 패키지를 추가했는데 인식하지 못하는 경우가 있다.&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
Package.onUse((api) =&amp;gt; {&lt;br /&gt;
&amp;nbsp; api.use([&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &#39;vulcan:core&#39;,&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &#39;vulcan:forms&#39;,&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &#39;vulcan:accounts&#39; /* &amp;lt;-- 추가함! */&lt;br /&gt;
&amp;nbsp; ]);&lt;br /&gt;
...&lt;br /&gt;
}&lt;/blockquote&gt;
&lt;div&gt;
내부패키지건 원격패키지건 안되는 안된다.&lt;/div&gt;
&lt;div&gt;
이럴 때 meteor add 후 meteor remove 해도 되지만 더 간단한 방법이 있다.&lt;/div&gt;
&lt;div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
meteor update vulcan:accounts&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div&gt;
이렇게 update 해주는 방법이 있다.&lt;/div&gt;
&lt;div&gt;
.meteor/package 파일을 건들지 않아서 좋다.&lt;/div&gt;
&lt;div&gt;
그래도 역시 좋지 않다. Meteor 스럽지 않다.&lt;/div&gt;
&lt;div&gt;
&lt;a href=&quot;https://github.com/meteor/meteor/issues/7721&quot;&gt;https://github.com/meteor/meteor/issues/7721&lt;/a&gt;&amp;nbsp;현재 1.5.2에서도 해결이 안되었군요.&lt;/div&gt;
&lt;div&gt;
해결되어 적용되면 다시 글 올리겠습니다.&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://spectrumdig.blogspot.com/feeds/223921404996045259/comments/default' title='댓글'/><link rel='replies' type='text/html' href='http://spectrumdig.blogspot.com/2017/09/troubleshooting-meteor-package.html#comment-form' title='0개의 덧글'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/223921404996045259'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/223921404996045259'/><link rel='alternate' type='text/html' href='http://spectrumdig.blogspot.com/2017/09/troubleshooting-meteor-package.html' title='Troubleshooting - Meteor package가 적용이 되지 않을 때'/><author><name>spectrum</name><uri>http://www.blogger.com/profile/06120635035623961975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8182501247262747119.post-3818391652595414997</id><published>2017-09-04T16:29:00.004+09:00</published><updated>2017-09-04T16:32:36.229+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="meteor"/><category scheme="http://www.blogger.com/atom/ns#" term="mutation"/><category scheme="http://www.blogger.com/atom/ns#" term="vulcan"/><category scheme="http://www.blogger.com/atom/ns#" term="vulcanjs"/><title type='text'>vulcanJS - 부록 Appendix A. Mutation (delete) 보강</title><content type='html'>Posts를 만들면서 약간 아쉬웠던 부분은 Delete 부분에 대해서 너무 SmartForm에 의지하여 간단하게 넘어가긴 했다.&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
showRemove={true}&lt;/blockquote&gt;
옵션을 통해 한줄 찍 긋고 Delete라고 나오는 부분을 클릭하는 것도 좋지만&lt;br /&gt;
목록에서 지우거나 상세 보기에서 지울 일도 분명 있을 것이다.&lt;br /&gt;
&lt;br /&gt;
역시나 이것도 목록(ex. withList)을 가져올 때 처럼 HoC(Higher-Order Component)를 사용한다.&lt;br /&gt;
&lt;a href=&quot;http://docs.vulcanjs.org/mutations.html#Remove-Mutation&quot;&gt;http://docs.vulcanjs.org/mutations.html#Remove-Mutation&lt;/a&gt;&amp;nbsp;부분을 참조하자.&lt;br /&gt;
&lt;br /&gt;
이번에 구현할 것은 목록마다 remove 버튼을 달고 버튼을 누르면 이벤트를 받아 삭제하도록 구현해보자.&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
import { registerComponent, withList, withRemove } from &#39;meteor/vulcan:core&#39;;&lt;/blockquote&gt;
먼저 해야할 것은 withRemove를 import에 추가하는 것이다.&lt;br /&gt;
그러면 withRemove를 registerComponent시 사용할 수 있다.&lt;br /&gt;
withList와 함께 withRemove도 추가하자.&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
registerComponent(&#39;PostsListComponent&#39;, PostsListComponent, [&lt;br /&gt;
&amp;nbsp; withList, {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; collection: Posts&lt;br /&gt;
&amp;nbsp; }], [&lt;br /&gt;
&amp;nbsp; withRemove, {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; collection: Posts&lt;br /&gt;
&amp;nbsp; }&lt;br /&gt;
]);&lt;/blockquote&gt;
&lt;div&gt;
withRemove도 withList와 동일하게 collection만 지정하면 된다.&lt;/div&gt;
&lt;div&gt;
준비는 다 되었다. withRemove를 붙여서 this.props는&amp;nbsp;removeMutation을 갖게 되었다.&lt;/div&gt;
&lt;div&gt;
removeMutation의 사용법은 다음과 같다.&lt;/div&gt;
&lt;div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
this.props.removeMutation({&lt;br /&gt;
&amp;nbsp; documentId: documentId&lt;br /&gt;
}).then(/* success */).catch(/* error */);&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div&gt;
promise를 지원한다. 오류처리는 catch에 정상적 완료 후에 무언가를 하려면 then에 function을 구현하자.&lt;/div&gt;
&lt;div&gt;
지금은 별 생각이 없으므로 버튼을 만들고 단순하게 붙여보자.&lt;/div&gt;
&lt;div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;button onClick={()=&amp;gt;this.props.removeMutation({&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; documentId: o._id&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; })&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .then(console.log)&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .catch(console.log)&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&amp;gt;Remove&amp;lt;/button&amp;gt;&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div&gt;
이런 버튼을 붙이면 되겠다.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
전체 소스를 다시 살펴보자. PostsListComponent는 이렇게 되었다.&lt;/div&gt;
&lt;div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
import React, { Component } from &#39;react&#39;;&lt;br /&gt;
import { Link } from &#39;react-router&#39;;&lt;br /&gt;
import { registerComponent, withList, withRemove } from &#39;meteor/vulcan:core&#39;;&lt;br /&gt;
import Posts from &quot;../modules/posts/collection.js&quot;;&lt;br /&gt;
class PostsListComponent extends Component {&lt;br /&gt;
&amp;nbsp; render () {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; return (&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;div&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; this.props.results&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;amp;&amp;amp; this.props.results.map(o=&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;div key={o._id}&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;Link to={`/posts/${o._id}`}&amp;gt;{o.title}&amp;lt;/Link&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;button onClick={()=&amp;gt;this.props.removeMutation({&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; documentId: o._id&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; })&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .then(console.log)&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .catch(console.log)&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&amp;gt;Remove&amp;lt;/button&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; )&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; );&lt;br /&gt;
&amp;nbsp; }&lt;br /&gt;
}&lt;br /&gt;
registerComponent(&#39;PostsListComponent&#39;, PostsListComponent, [&lt;br /&gt;
&amp;nbsp; withList, {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; collection: Posts&lt;br /&gt;
&amp;nbsp; }], [&lt;br /&gt;
&amp;nbsp; withRemove, {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; collection: Posts&lt;br /&gt;
&amp;nbsp; }&lt;br /&gt;
]);&lt;br /&gt;
export default PostsListComponent;&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div&gt;
목록 한건한건 오른쪽 끝에 Remove버튼이 붙었고 누르자마 해당 Post가 지워진다.&lt;br /&gt;
HoC는 처음엔 다소 어색하지만 한번 맛들이면 빠져나오기 힘들다. 일관적인 구현을 위해 적극적으로 사용하자.&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://spectrumdig.blogspot.com/feeds/3818391652595414997/comments/default' title='댓글'/><link rel='replies' type='text/html' href='http://spectrumdig.blogspot.com/2017/09/vulcanjs-appendix-mutation-delete.html#comment-form' title='0개의 덧글'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/3818391652595414997'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/3818391652595414997'/><link rel='alternate' type='text/html' href='http://spectrumdig.blogspot.com/2017/09/vulcanjs-appendix-mutation-delete.html' title='vulcanJS - 부록 Appendix A. Mutation (delete) 보강'/><author><name>spectrum</name><uri>http://www.blogger.com/profile/06120635035623961975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8182501247262747119.post-7647410912430324847</id><published>2017-09-02T17:47:00.000+09:00</published><updated>2017-09-02T17:47:15.294+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="delete"/><category scheme="http://www.blogger.com/atom/ns#" term="meteor"/><category scheme="http://www.blogger.com/atom/ns#" term="smartForm"/><category scheme="http://www.blogger.com/atom/ns#" term="update"/><category scheme="http://www.blogger.com/atom/ns#" term="vulcan"/><category scheme="http://www.blogger.com/atom/ns#" term="vulcanjs"/><title type='text'>vulcanJS - 10. Posts Update/Delete</title><content type='html'>마지막으로 수정과 삭제를 구현해보면 목록 조회(List), 상세 조회, 쓰기, 수정, 삭제까지 모든 필요한 요소를 아우를 수 있을 것이다.&lt;br /&gt;
감이 좋은 분들은 눈치 챘을지도 모르겠지만 사실 수정이란 건 UI면에서 볼때 이미 양식이 채워져있는 신규 쓰기와 별반 다르지 않다.&lt;br /&gt;
&lt;br /&gt;
먼저 해야할 것은 역시나 Component를 만드는 일이다.&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
$ vulcan g component&lt;br /&gt;? Package name spectrum-simplebb&lt;br /&gt;? Component name PostsEditComponent&lt;br /&gt;? Component type Class Component&lt;br /&gt;? Register component Yes&lt;br /&gt;&amp;nbsp; &amp;nbsp;create packages/spectrum-simplebb/lib/components/PostsEditComponent.jsx&lt;br /&gt;&amp;nbsp;conflict packages/spectrum-simplebb/lib/components/index.js&lt;br /&gt;? Overwrite packages/spectrum-simplebb/lib/components/index.js? overwrite&lt;br /&gt;&amp;nbsp; &amp;nbsp; force packages/spectrum-simplebb/lib/components/index.js&lt;/blockquote&gt;
PostsEditComponent를 만들었다.&lt;br /&gt;
route도 만들자. /posts/edit/:id 이렇게 경로를 만들면 좋겠다. 그러고보니 이전 글에서 만든 상세보기도 /posts/view/:id 형식으로 만들껄 그랬다.&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
$ vulcan g route&lt;br /&gt;? Package name spectrum-simplebb&lt;br /&gt;? Route name postsEdit&lt;br /&gt;? Route path /posts/edit/:_id&lt;br /&gt;? Component name PostsEditComponent&lt;br /&gt;? Layout name&lt;br /&gt;&amp;nbsp;conflict packages/spectrum-simplebb/lib/modules/routes.js&lt;br /&gt;? Overwrite packages/spectrum-simplebb/lib/modules/routes.js? overwrite&lt;br /&gt;&amp;nbsp; &amp;nbsp; force packages/spectrum-simplebb/lib/modules/routes.js&lt;/blockquote&gt;
Route도 만들었다.&lt;br /&gt;
&lt;br /&gt;
그렇다면 다음은 /posts/edit/:_id로 들어올 진입점을 만들자. PostsSingleComponent가 괜찮아보인다.&lt;br /&gt;
&amp;lt;Link to={`/posts/edit/${this.props.documentId}`}&amp;gt;Edit&amp;lt;/Link&amp;gt;&lt;br /&gt;
이 부분을 삽입하여주자.&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
import React, { Component } from &#39;react&#39;;&lt;br /&gt;import { Link } from &#39;react-router&#39;;&lt;br /&gt;import { registerComponent, withDocument } from &#39;meteor/vulcan:core&#39;;&lt;br /&gt;import Posts from &quot;../modules/posts/collection.js&quot;;&lt;br /&gt;class PostsSingleComponent extends Component {&lt;br /&gt;&amp;nbsp; render () {&lt;br /&gt;&amp;nbsp; &amp;nbsp; return (&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; this.props.loading &amp;amp;&amp;amp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;div /&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; ||&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;div&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;h1&amp;gt;{this.props.document.title}&amp;lt;/h1&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;pre&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {this.props.document.body}&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/pre&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;div&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;Link to={`/posts/edit/${this.props.documentId}`}&amp;gt;Edit&amp;lt;/Link&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/div&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;div&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;Link to=&#39;/&#39;&amp;gt;to Home&amp;lt;/Link&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/div&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/div&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; );&lt;br /&gt;&amp;nbsp; }&lt;br /&gt;}&lt;br /&gt;registerComponent(&#39;PostsSingleComponent&#39;, PostsSingleComponent, [withDocument, {&lt;br /&gt;&amp;nbsp; collection: Posts&lt;br /&gt;}]);&lt;br /&gt;export default PostsSingleComponent;&lt;/blockquote&gt;
&lt;div&gt;
상위 Component인 PostsViewComponent가 _id를 documentId로 전달하므로 같은 걸 사용했다.&lt;/div&gt;
&lt;div&gt;
목록&amp;gt;상세&amp;gt;수정까지 진입하는 길은 다 만들어졌다.&lt;/div&gt;
&lt;div&gt;
수정 서식을 보여주도록 PostsEditComponent를 고쳐보자.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
사실 PostsEditComponent는 PostsNewComponent와 거의 같다.&lt;/div&gt;
&lt;div&gt;
단 하나 차이점이 있다면 SmartForm에 documentId를 지정하는 것만이 다르다.&lt;/div&gt;
&lt;div&gt;
즉, SmartForm Component는 documentId를 지정할 경우 수정을 하고 없을 경우 새로운 Document를 생성한다고 보면된다.&lt;/div&gt;
&lt;div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
import React, { Component } from &#39;react&#39;;&lt;br /&gt;import { registerComponent, Components } from &#39;meteor/vulcan:core&#39;;&lt;br /&gt;import Posts from &amp;nbsp;&quot;../modules/posts/collection.js&quot;;&lt;br /&gt;class PostsEditComponent extends Component {&lt;br /&gt;&amp;nbsp; render () {&lt;br /&gt;&amp;nbsp; &amp;nbsp; return (&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;div&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;Components.SmartForm&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; collection={Posts}&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; documentId={this.props.params._id}&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; /&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/div&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; );&lt;br /&gt;&amp;nbsp; }&lt;br /&gt;}&lt;br /&gt;registerComponent(&#39;PostsEditComponent&#39;, PostsEditComponent);&lt;br /&gt;export default PostsEditComponent;&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div&gt;
날로 먹는다고 봐야 한다.&lt;/div&gt;
&lt;div&gt;
아직 날로 먹는 건 끝이 아니다.&lt;/div&gt;
&lt;div&gt;
SmartForm은 collection과 documentId 말고도 몇 가지가 더 있기 때문이다.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
지금 현재 구현은 수정이라는 기능엔 충실하지만 실제로 사용자는 수정을 완료한 뒤에 멀뚱하게 그자리에 있는 것을 원하지 않을 것이다.&lt;/div&gt;
&lt;div&gt;
successCallback을 추가하여 수정 완료 시 상세 조회 화면으로 돌아가도록 해 보자.&lt;/div&gt;
&lt;div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
import React, { Component } from &#39;react&#39;;&lt;br /&gt;import { registerComponent, Components } from &#39;meteor/vulcan:core&#39;;&lt;br /&gt;import Posts from &amp;nbsp;&quot;../modules/posts/collection.js&quot;;&lt;br /&gt;class PostsEditComponent extends Component {&lt;br /&gt;&amp;nbsp; render () {&lt;br /&gt;&amp;nbsp; &amp;nbsp; return (&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;div&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;Components.SmartForm&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; collection={Posts}&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; documentId={this.props.params._id}&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; successCallback={&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ()=&amp;gt; this.props.router.push(`/posts/${this.props.params._id}`)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; /&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/div&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; );&lt;br /&gt;&amp;nbsp; }&lt;br /&gt;}&lt;br /&gt;registerComponent(&#39;PostsEditComponent&#39;, PostsEditComponent);&lt;br /&gt;export default PostsEditComponent;&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div&gt;
router객체 안에 있는 push를 사용하여 해당 글로 다시 돌아가보았다.&lt;/div&gt;
&lt;div&gt;
신규 작성에 비해 수정을 위해서 우리가 해준 건 딱 두개 밖에 없다.&lt;/div&gt;
&lt;div&gt;
&lt;ol&gt;
&lt;li&gt;documentID 를 추가했고&lt;/li&gt;
&lt;li&gt;수정 후 callback을 구현했다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div&gt;
그러면 이젠 마지막 관문인 삭제 구현을 해야하는데 이게 의외로 싱겁다.&lt;/div&gt;
&lt;div&gt;
SmartForm에 속성 두개만 추가하면 된다.&lt;/div&gt;
&lt;div&gt;
&lt;ol&gt;
&lt;li&gt;showRemove={true} 를 추가하여 삭제 기능을 화면에 보여주고&lt;/li&gt;
&lt;li&gt;removeSuccessCallback을 구현하여 삭제 후 callback을 구현하면 된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div&gt;
한번 해보자.&lt;/div&gt;
&lt;div&gt;
먼저 removeSuccessCallback&amp;nbsp;후엔 이미 자신이 사라진 이후니까 successCallback처럼 자신의 글로 되돌아 갈 수 없다.&lt;/div&gt;
&lt;div&gt;
목록으로 가도록 해주자.&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; removeSuccessCallback ={&lt;/div&gt;
&lt;div&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ()=&amp;gt; this.props.router.push(`/`)&lt;/div&gt;
&lt;div&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
이 정도가 적당할 것 같다.&lt;/div&gt;
&lt;div&gt;
전부 모아보자.&lt;/div&gt;
&lt;div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
import React, { Component } from &#39;react&#39;;&lt;br /&gt;import { registerComponent, Components } from &#39;meteor/vulcan:core&#39;;&lt;br /&gt;import Posts from &amp;nbsp;&quot;../modules/posts/collection.js&quot;;&lt;br /&gt;class PostsEditComponent extends Component {&lt;br /&gt;&amp;nbsp; render () {&lt;br /&gt;&amp;nbsp; &amp;nbsp; return (&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;div&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;Components.SmartForm&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; collection={Posts}&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; documentId={this.props.params._id}&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; successCallback={&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ()=&amp;gt; this.props.router.push(`/posts/${this.props.params._id}`)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; showRemove={true}&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; removeSuccessCallback ={&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ()=&amp;gt; this.props.router.push(`/`)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; /&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/div&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; );&lt;br /&gt;&amp;nbsp; }&lt;br /&gt;}&lt;br /&gt;registerComponent(&#39;PostsEditComponent&#39;, PostsEditComponent);&lt;br /&gt;export default PostsEditComponent;&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div&gt;
showRemove와 removeSuccessCallback을 구현하였다.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEju04bK4w_E4sOPEU6gE0az2tckTeZR7Hua5UhHV5cj7_ZUCHhKXBWpPj9QleZ4VQajaAiQVYqR4VxPspriy4BU3TMLqVoVjIUGQZn79ks7E1C6P6E7ju3qwceAPU5y9-DvKG4xf8cfXsRv/s1600/Screen+Shot+2017-09-02+at+17.30.21.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;302&quot; data-original-width=&quot;534&quot; height=&quot;360&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEju04bK4w_E4sOPEU6gE0az2tckTeZR7Hua5UhHV5cj7_ZUCHhKXBWpPj9QleZ4VQajaAiQVYqR4VxPspriy4BU3TMLqVoVjIUGQZn79ks7E1C6P6E7ju3qwceAPU5y9-DvKG4xf8cfXsRv/s640/Screen+Shot+2017-09-02+at+17.30.21.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Delete 기능이 아래 달라붙었다.&lt;/div&gt;
&lt;div&gt;
버튼 같지 않지만 그거야 스타일링 하면 되고 클릭해보자.&lt;/div&gt;
&lt;div&gt;
확인 alert이 뜨고.&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgfn9z1eUqqc0wgxUsF9DodEk-FZwZk8S8eUvABY6-yVjnUmi4573qzv4ylxfqfWcNlaCcTZ2LONMsqUNokIiFB8pSFO8V4hL0sM3UyFFiPz-XRFXWoPcxF2G32jHaAJn-IQCI5Tbc9dyvM/s1600/Screen+Shot+2017-09-02+at+17.37.19.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;327&quot; data-original-width=&quot;546&quot; height=&quot;382&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgfn9z1eUqqc0wgxUsF9DodEk-FZwZk8S8eUvABY6-yVjnUmi4573qzv4ylxfqfWcNlaCcTZ2LONMsqUNokIiFB8pSFO8V4hL0sM3UyFFiPz-XRFXWoPcxF2G32jHaAJn-IQCI5Tbc9dyvM/s640/Screen+Shot+2017-09-02+at+17.37.19.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
홈으로 이동 후 삭제가 잘 이루어졌다.&lt;/div&gt;
&lt;div&gt;
갑자기 수정/삭제가 너무 허망하게 구현이 된 것 같은 느낌은 있지만 여기까지 필요한 최소한의 기능은 모두 구현해보았다고 할 수 있다.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
이제 나머지는 디테일을 위한 공부!&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEicczihWq62RAG0WmJzmMdnJROb17gv5qXKCakIf9vsuF11Z5WFSzotUCGblDs_e7Tb_FXX_uZszHVpombRCdjHjsAaJhnSzywY22dznKUVjYsM9mwGm8IIEE5Roei1COoyofkgkSSRmsml/s1600/a3d452220f84728012f042692a014e8f.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;367&quot; data-original-width=&quot;380&quot; height=&quot;309&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEicczihWq62RAG0WmJzmMdnJROb17gv5qXKCakIf9vsuF11Z5WFSzotUCGblDs_e7Tb_FXX_uZszHVpombRCdjHjsAaJhnSzywY22dznKUVjYsM9mwGm8IIEE5Roei1COoyofkgkSSRmsml/s320/a3d452220f84728012f042692a014e8f.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Vulcan 메뉴얼은 당연히 읽고&lt;/div&gt;
&lt;div&gt;
&lt;a href=&quot;http://docs.vulcanjs.org/&quot;&gt;http://docs.vulcanjs.org/&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Meteor 도 봐두고&lt;/div&gt;
&lt;div&gt;
&lt;a href=&quot;https://docs.meteor.com/&quot;&gt;https://docs.meteor.com/&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
GraphQL도 익히고&lt;/div&gt;
&lt;div&gt;
&lt;a href=&quot;http://graphql.org/&quot;&gt;http://graphql.org/&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
React도 보고&lt;/div&gt;
&lt;div&gt;
&lt;a href=&quot;https://facebook.github.io/react/&quot;&gt;https://facebook.github.io/react/&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
즐거운 공부 뿐이다. 즐기자.&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://spectrumdig.blogspot.com/feeds/7647410912430324847/comments/default' title='댓글'/><link rel='replies' type='text/html' href='http://spectrumdig.blogspot.com/2017/09/vulcanjs-10-posts-updatedelete.html#comment-form' title='0개의 덧글'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/7647410912430324847'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/7647410912430324847'/><link rel='alternate' type='text/html' href='http://spectrumdig.blogspot.com/2017/09/vulcanjs-10-posts-updatedelete.html' title='vulcanJS - 10. Posts Update/Delete'/><author><name>spectrum</name><uri>http://www.blogger.com/profile/06120635035623961975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEju04bK4w_E4sOPEU6gE0az2tckTeZR7Hua5UhHV5cj7_ZUCHhKXBWpPj9QleZ4VQajaAiQVYqR4VxPspriy4BU3TMLqVoVjIUGQZn79ks7E1C6P6E7ju3qwceAPU5y9-DvKG4xf8cfXsRv/s72-c/Screen+Shot+2017-09-02+at+17.30.21.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8182501247262747119.post-3763697002384608257</id><published>2017-09-02T14:40:00.002+09:00</published><updated>2017-09-02T14:45:53.397+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="loading"/><category scheme="http://www.blogger.com/atom/ns#" term="meteor"/><category scheme="http://www.blogger.com/atom/ns#" term="route"/><category scheme="http://www.blogger.com/atom/ns#" term="vulcan"/><category scheme="http://www.blogger.com/atom/ns#" term="vulcanjs"/><title type='text'>vulcanJS - 9. Route 연결하기</title><content type='html'>Route들끼리 연결에 대해 알아보자고 지난 시간에 말씀드렸는데&lt;br /&gt;
Vulcan에선 React-router(&lt;a href=&quot;https://reacttraining.com/react-router/web/api/Link&quot;&gt;https://reacttraining.com/react-router/web/api/Link&lt;/a&gt;)를 사용하므로 그 규칙을 그대로 따르면 됩니다.&lt;br /&gt;
&amp;lt;Link to=&#39;경로명&#39;&amp;gt;표시할 이름&amp;lt;/Link&amp;gt; 형식으로 씁니다.&lt;br /&gt;
PostsSingleComponent 에&amp;nbsp;&amp;lt;Link to=&#39;/&#39;&amp;gt;to Home&amp;lt;/Link&amp;gt; 를 추가하여 처음으로 돌아가도록 합시다.&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
import React, { Component } from &#39;react&#39;;&lt;br /&gt;
import { Link } from &#39;react-router&#39;;&lt;br /&gt;
import { registerComponent, withDocument } from &#39;meteor/vulcan:core&#39;;&lt;br /&gt;
import Posts from &quot;../modules/posts/collection.js&quot;;&lt;br /&gt;
class PostsSingleComponent extends Component {&lt;br /&gt;
&amp;nbsp; render () {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; return (&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;div&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;h1&amp;gt;{this.props.document.title}&amp;lt;/h1&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {this.props.document.body}&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;Link to=&#39;/&#39;&amp;gt;to Home&amp;lt;/Link&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; );&lt;br /&gt;
&amp;nbsp; }&lt;br /&gt;
}&lt;br /&gt;
registerComponent(&#39;PostsSingleComponent&#39;, PostsSingleComponent, [withDocument, {&lt;br /&gt;
&amp;nbsp; collection: Posts&lt;br /&gt;
}]);&lt;br /&gt;
export default PostsSingleComponent;&lt;/blockquote&gt;
&lt;div&gt;
평범합니다. 이제 본문 글 목록에서도 상세로 들어올 수 있도록 제목마다 연결해 봅니다.&lt;/div&gt;
&lt;div&gt;
PostsListComponent에서&amp;nbsp;&amp;lt;div key={o._id}&amp;gt;{o.title}&amp;lt;/div&amp;gt; 이 부분에 &amp;lt;Link&amp;gt;를 적용하면 아래와 같습니다.&lt;/div&gt;
&lt;div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
import React, { Component } from &#39;react&#39;;&lt;br /&gt;
import { Link } from &#39;react-router&#39;;&lt;br /&gt;
import { registerComponent, withList } from &#39;meteor/vulcan:core&#39;;&lt;br /&gt;
import Posts from &quot;../modules/posts/collection.js&quot;;&lt;br /&gt;
class PostsListComponent extends Component {&lt;br /&gt;
&amp;nbsp; render () {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; return (&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;div&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; this.props.results&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;amp;&amp;amp; this.props.results.map(o=&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;div key={o._id}&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;Link to={`/posts/${o._id}`}&amp;gt;{o.title}&amp;lt;/Link&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; )&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; );&lt;br /&gt;
&amp;nbsp; }&lt;br /&gt;
}&lt;br /&gt;
registerComponent(&#39;PostsListComponent&#39;, PostsListComponent, [withList, {&lt;br /&gt;
&amp;nbsp; collection: Posts&lt;br /&gt;
}]);&lt;br /&gt;
export default PostsListComponent;&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div&gt;
o._id를 문자열 안에 넣기 위해 `(backtick)을 사용했습니다.&lt;/div&gt;
&lt;div&gt;
&amp;lt;Link to={`/posts/${o._id}`}&amp;gt;{o.title}&amp;lt;/Link&amp;gt; 이렇게 접근하는 것이지요.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
근데, 실제로 목록에서 상세로 연결하려고 눌러보면 생각대로 한번에 잘 들어가지지 않습니다.&lt;/div&gt;
&lt;div&gt;
어떻게 된 일 일까요?&lt;/div&gt;
&lt;div&gt;
콘솔 창에서 보면&lt;/div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
PostsSingleComponent.jsx:11 Uncaught TypeError: Cannot read property &#39;title&#39; of undefined&lt;/blockquote&gt;
&lt;div&gt;
위와 같은 오류가 납니다.&lt;/div&gt;
&lt;div&gt;
즉 원인은 PostsListComponent가 아니라 PostsSingleComponent를 렌더링하는 시점에 this.props에 document라는 속성이 생기지 않았고 그 상태에서 document.title에 접근하고자 하였기 때문입니다.&lt;br /&gt;
Swift나 Coffeescript 같은 곳엔 optional 혹은 existential operator라고 불리는 연산자가 있어서 해당 속성 뒤에 ?를 써놓으면 미리 존재여부를 검사할 수 있는데 현재 ECMA6/7에선 지원하지 않습니다.&lt;/div&gt;
&lt;div&gt;
그렇다면 withDocument로부터 데이터가 다 불러졌는지 알려면 어떻게 해야할까요?&lt;/div&gt;
&lt;div&gt;
&lt;a href=&quot;http://docs.vulcanjs.org/data-loading.html#withDocument&quot;&gt;http://docs.vulcanjs.org/data-loading.html#withDocument&lt;/a&gt;를&amp;nbsp;보면 Document말고 loading이라는 속성이 있습니다. 로딩 여부를 반환하는 속성입니다. 이것을 이용합니다.&lt;/div&gt;
&lt;div&gt;
보통 로딩 시에 로딩 에니메이션을 화면에 보여주는데 화면이 번쩍하는게 싫으니 그냥 빈 &amp;nbsp;&amp;lt;div /&amp;gt;를 잠시 노출하도록 합니다.&lt;/div&gt;
&lt;div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
import React, { Component } from &#39;react&#39;;&lt;br /&gt;
import { Link } from &#39;react-router&#39;;&lt;br /&gt;
import { registerComponent, withDocument } from &#39;meteor/vulcan:core&#39;;&lt;br /&gt;
import Posts from &quot;../modules/posts/collection.js&quot;;&lt;br /&gt;
class PostsSingleComponent extends Component {&lt;br /&gt;
&amp;nbsp; render () {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; return (&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; this.props.loading &amp;amp;&amp;amp;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;div /&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; ||&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;div&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;h1&amp;gt;{this.props.document.title}&amp;lt;/h1&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {this.props.document.body}&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;Link to=&#39;/&#39;&amp;gt;to Home&amp;lt;/Link&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; );&lt;br /&gt;
&amp;nbsp; }&lt;br /&gt;
}&lt;br /&gt;
registerComponent(&#39;PostsSingleComponent&#39;, PostsSingleComponent, [withDocument, {&lt;br /&gt;
&amp;nbsp; collection: Posts&lt;br /&gt;
}]);&lt;br /&gt;
export default PostsSingleComponent;&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div&gt;
더 이상 오류가 발생하지 않습니다.&lt;/div&gt;
&lt;div&gt;
비동기 상황은 꼼꼼하게 점검해서 오류를 막아주도록 합시다.&lt;/div&gt;
&lt;div&gt;
다음은 수정과 삭제에 대해 알아보록 하겠습니다.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://spectrumdig.blogspot.com/feeds/3763697002384608257/comments/default' title='댓글'/><link rel='replies' type='text/html' href='http://spectrumdig.blogspot.com/2017/09/vulcanjs-9-route.html#comment-form' title='0개의 덧글'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/3763697002384608257'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8182501247262747119/posts/default/3763697002384608257'/><link rel='alternate' type='text/html' href='http://spectrumdig.blogspot.com/2017/09/vulcanjs-9-route.html' title='vulcanJS - 9. Route 연결하기'/><author><name>spectrum</name><uri>http://www.blogger.com/profile/06120635035623961975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>