기본 콘텐츠로 건너뛰기

go를 사용하여 Google App Engine에 정적 웹서버를 올려보자.

작년에 미뤄뒀던 Go를 다시 보고 있다.
동기부여를 위해 회사 안내 홈페이지를 망할 일도 없고 비용이 안드는 곳으로 옮기고 싶은 생각이 있었는데
Google App Engine(이하 GAE)정도면 뭐 망할 일이 없겠지 싶다.
Google Apps 처럼 유료화해도 먼저 만들어 놓은 서비스/계정은 안건드니까 먼저 침이라도 발라놓아야지.

개발환경 세팅은 간단했다.
Download 에서 package 를 받고 설치한 다음
goide를 쓰고 싶었으나 아직 완성도 문제가 있어서 sublime을 사용하기로 결정.
http://wbond.net/sublime_packages/package_control/installation 는 전에 설치해놓았다.
그저 sublime을 띄운 상태에서 ctrl+` (OS X에서도 ctrl+` 임)을 누르면 나타나는 하단 콘솔창에

import urllib2,os; pf='Package Control.sublime-package'; ipp=sublime.installed_packages_path(); os.makedirs(ipp) if not os.path.exists(ipp) else None; urllib2.install_opener(urllib2.build_opener(urllib2.ProxyHandler())); open(os.path.join(ipp,pf),'wb').write(urllib2.urlopen('http://sublime.wbond.net/'+pf.replace(' ','%20')).read()); print 'Please restart Sublime Text to finish installation'
요 내용을 붙여넣으면 끝.

그 다음 sublime용 golang plugin인 GoSublime을 추가
데모 동영상을 보면서 사용법을 간단하게 알아보면

0:11즈음에 나오는 package 삽입 기능은 ctrl(cmd)+. , ctrl(cmd)+p 사용

package 삽입 뿐만이 아니라 삭제도 되어서 안쓰는 package가 있는 경우 컴파일 오류를 뿜어내는 go 에선 아주 편리.
ctrl(cmd)+b(0:22에 나옴)하면 go shell을 사용.
ctrl(cmd)+. , ctrl(cmd)+. (0:28 즈음) 연타하고 share 하면 play.golang.org 에 code snippet 올리고 URL까지 뽑아줌. 재밌는 기능.
실행시엔 그냥 ctrl(cmd)+. , ctrl(cmd)+R 하면 된다. (ctrl 혹은 cmd 키를 누른채로 .과 R을 연속해서 입력)

생각보다 정적 웹서버를 구현하는 건 어이없을 정도로 간단.

package main

import (
"log"
"net/http"
)

func main() {
log.Fatal(http.ListenAndServe(":3000", http.FileServer(http.Dir("./public"))))
}

이게 전부다. 진짜다;


:3000포트로 들어온 http Request를 FileServer를 통해 public 디렉토리 하위에 있는 문서랑 연결하는 방식이다.

node.js와 coffeescript로 twitter 포스팅용 simpleHTTPServer 소스(https://gist.github.com/4414368)를 올린적이 있는데
go도 역시 만만치 않다. 물론 실제로 FileServer를 풀어 헤쳐서 구현하려면 손이 더 타겠지만 일단은 간단하다. (하지만, node.js 의 방식이 더 누군가에게 설명하거나 이야기 하긴 더 좋다)

다음은 GAE Go SDK를 설치한다.
압축파일(https://developers.google.com/appengine/downloads)을 풀고
적당히 google_appengine 을 이동한뒤 PATH를 잡아준다.
https://developers.google.com/appengine/docs/go/gettingstarted/devenvironment 참조

자 이제 준비는 끝났다.

GAE용 어플리케이션을 만드는게 목적이므로 main 대신 init 으로 변경하자.
근데 문제가 있다.

http.ListenAndServer를 이미 GAE GO 에서 사용하고 있으므로
http.HandleFunc 를 처리해 줘야한다.

이런 식으로 코드를 짜보았다. node용 simpleHTTPServer 와 비슷하게.




package server

import (
"appengine"
"fmt"
"io/ioutil"
"net/http"
)

func init() {
http.HandleFunc("/", handler)
}

func handler(w http.ResponseWriter, r *http.Request) {
c := appengine.NewContext(r)
path := r.URL.Path
if path == "/" {
path = "/index.html"
}
file, err := ioutil.ReadFile("./public" + path)
if err != nil {
file, err = ioutil.ReadFile("./public/404.html")
c.Infof("404:Requested URL Not Found: %v", r.URL.Path)
}
fmt.Fprint(w, string(file))
}

이렇게 해도 되나 참내;;

디렉토리 구조는 아래와 같이
home/
  app.yaml
  server/
    server.go
  public/
    index.html
    404.html
   ....
    

작동이 정상적으로 되는 걸 확인하면 등록하자. 경로로 r.RequestURI를 사용했더니 로컬에선 되는데 서버에서 문제가 있음. 삽질하지말고 r.URL.Path 쓰자.
https://appengine.google.com 에서 어플리케이션 명을 등록하고 그 이름으로 app.yaml 파일을 수정한다.

application: <application name>
version: 1
runtime: go
api_version: go1

handlers:
- url: /.*
  script: _go_app

여기까지 하고
appcfg.py update .
를 사용하여 서버에 올리자. 올라가는 속도가 썩 빠르지는 않다.

Python으로 할껄 괜히 go로 한다고 삽질한 듯도 싶고.
그래도 잘 올라가니까 좋네.ㅎ

이 블로그의 인기 게시물

vulcanJS - 10. Posts Update/Delete

마지막으로 수정과 삭제를 구현해보면 목록 조회(List), 상세 조회, 쓰기, 수정, 삭제까지 모든 필요한 요소를 아우를 수 있을 것이다.
감이 좋은 분들은 눈치 챘을지도 모르겠지만 사실 수정이란 건 UI면에서 볼때 이미 양식이 채워져있는 신규 쓰기와 별반 다르지 않다.

먼저 해야할 것은 역시나 Component를 만드는 일이다.
$ vulcan g component
? Package name spectrum-simplebb
? Component name PostsEditComponent
? Component type Class Component
? Register component Yes
   create packages/spectrum-simplebb/lib/components/PostsEditComponent.jsx
 conflict packages/spectrum-simplebb/lib/components/index.js
? Overwrite packages/spectrum-simplebb/lib/components/index.js? overwrite
    force packages/spectrum-simplebb/lib/components/index.js PostsEditComponent를 만들었다.
route도 만들자. /posts/edit/:id 이렇게 경로를 만들면 좋겠다. 그러고보니 이전 글에서 만든 상세보기도 /posts/view/:id 형식으로 만들껄 그랬다.
$ vulcan g route
? Package name spectrum-simplebb
? Route name postsEdit
? Route path /posts/edit/:_id
? Component name PostsEditComponent
? Layout name
 conflict packages/spectrum-simplebb/lib/modules/routes.js
? Overwrite packages/spectrum-simplebb/lib/modules/routes.js? overwrite
   …

vulcanJS - 1. vulcan-cli 설치하고 프로젝트 만들기

vulcanJS를 학습하면서 알아낸 점을 기록하는 의미의 튜토리얼을 써본다.

Telescope도 그랬지만 forum 형태의 예제에서 embedly를 사용하는 URL 요소가 글에 들어가는 점이나 일반인들에게는 익숙하지 않은 Markdown 문법 등을 걷어내고 최소형태에서부터 접근해야할 필요성을 느껴서 단순 post만 목록 열람하고 읽고 쓰는 구현을 해보기로 했다.

먼저, vulcanJS를 github를 통해 clone 하고 meteor create --package를 통해 만들수도 있지만 Vulcan-Cli(https://github.com/VulcanJS/vulcanjs-cli)가 있어서 이걸 일단 사용해보기로 한다.

대충 내용을 읽어보니 Rails 생각이 나기도 하고 재밌어 보인다.
npm install -g vulcanjs-cli 혹은 meteor 에서만 한다면
meteor npm install -g vulcanjs-cli 이런 식으로 설치하고 쓰면 된다.
npm 에 global로 설치한 첫번째 경우 기준으로 진행해보겠다.
설치하고 난 뒤 vulcan 을 실행해보면
$ vulcan
vulcan usage:
Synopsis
  vulcan <action> <object> <...>
    <action>   Operation to perform
    <object>   Asset type (contextual to action)
    <...>      Parameters. If not provided, interactively entered
Project initialisation
  vulcan create <appName>
  vulcan init <appName>
Assets creation
  vulcan (generate|g) package <packageName>
  vulcan (generate|g) model <packageName>…

Troubleshooting - Meteor package가 적용이 되지 않을 때

버전 1.5 기준 package.js에서 Package.onUse 에 새 패키지를 추가했는데 인식하지 못하는 경우가 있다.
Package.onUse((api) => {
  api.use([
    'vulcan:core',
    'vulcan:forms',
    'vulcan:accounts' /* <-- 추가함! */
  ]);
...
} 내부패키지건 원격패키지건 안되는 안된다. 이럴 때 meteor add 후 meteor remove 해도 되지만 더 간단한 방법이 있다. meteor update vulcan:accounts 이렇게 update 해주는 방법이 있다. .meteor/package 파일을 건들지 않아서 좋다. 그래도 역시 좋지 않다. Meteor 스럽지 않다. https://github.com/meteor/meteor/issues/7721 현재 1.5.2에서도 해결이 안되었군요. 해결되어 적용되면 다시 글 올리겠습니다.