공대생 정리노트
Server 함수 정리 본문
Go로 간단한 서버를 만들 때 사용되는 라이브러리들과 자주 사용되는 함수를 정리해보았다.
함수들은 모두 Go 강좌를 무료로 올려주신 Tucker Programming 채널을 보고 알게 되었다! 감사하신 분이다.
모든 라이브러리는 go get ~ 로 설치할 수 있다.
github.com/gorilla/pat
쉽게 Restful api를 만들 수 있는 라이브러리.
func main(){
// this example is from github.com/gorilla/pat
router := pat.New()
router.Get("/things", getAllTheThings)
router.Put("/things/{id}", putOneThing)
router.Delete("/things/{id}", deleteOneThing)
router.Get("/", homeHandler)
http.Handle("/", router)
log.Print("Listening on 127.0.0.1:8000...")
log.Fatal(http.ListenAndServe(":8000", nil))
}
router를 pat.New()로 정의할 수 있다.
아주 간단하게 만들 수 있다. 단점이라 하면 documentation이 너무 짧다.
CRUD만 빠르게 구현할 것이라면 이용하면 좋을 것 같다.
github.com/gorilla/mux
gorilla/pat에 비해 굉장히 많은 documentation을 지니고 있고, star의 개수가 증명하듯 go로 web application을 만들 때 많이 쓰인다.
func main() {
//this example is from github.com/gorilla/mux
r := mux.NewRouter()
r.HandleFunc("/", HomeHandler)
r.HandleFunc("/products", ProductsHandler)
r.HandleFunc("/articles", ArticlesHandler)
http.Handle("/", r)
}
기본적인 사용방법은 위와 같다. 그런데 pat과 다른 점은 Get, Post 등을 pat에서는 함수가 아예 따로 있지만 gorilla/mux에서는 option으로 들어간다
// this example is from gorilla/mux
r := mux.NewRouter()
// Only matches if domain is "www.example.com".
r.Host("www.example.com")
// Matches a dynamic subdomain.
r.Host("{subdomain:[a-z]+}.example.com")
// it is possible to combine several matchers in a single route
r.HandleFunc("/products", ProductsHandler).
Host("www.example.com").
Methods("GET").
Schemes("http")
route를 세분화할 수 있다는 것이 장점이다.
나는 다음과 같이 예제 코드를 짰다.
//youtube Tucker Programming 참고
func Handler1() http.Handler {
mux := mux.NewRouter()
mux.HandleFunc("/", indexHandler)
mux.HandleFunc("/users", UserHandler).Methods("GET")
mux.HandleFunc("/users", createUserHandler).Methods("POST")
mux.HandleFunc("/users", updateUserHandler).Methods("PUT")
mux.HandleFunc("/users/{id:[0-9]+}", getUserInfoHandler).Methods("GET")
mux.HandleFunc("/users/{id:[0-9]+}", deleteUserHandler).Methods("DELETE")
return mux
}
func main(){
http.ListenAndServe(":3000", Handler1())
}
gorilla/mux의 유용한 함수중 하나는 Handler에서 URL의 요소를 분리해서 사용하는 함수이다.
위에서 deleteUserHandler를 예로 들어보자.
func deleteUserHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id, err := strconv.Atoi(vars["id"])
if err != nil {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprint(w, err)
return
}
...
}
deleteUserHandler가 불리는 url은 /users/(숫자) 꼴일 것이다. 이때 mux.HanleFunc에서 {id : [0-9]+}를 이용해 url을 넣어주었기 때문에 vars["id"]를 이용해 id를 url에서 빼올 수 있다.
github.com/urfave/negroni
기본 미들웨어를 추가시켜준다.
// example from github.com/urfave/negroni
package main
import (
"fmt"
"net/http"
"github.com/urfave/negroni"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
fmt.Fprintf(w, "Welcome to the home page!")
})
n := negroni.Classic() // 기본 미들웨어들을 포함합니다
n.UseHandler(mux)
http.ListenAndServe(":3000", n)
}
negroni.Classic()은 다음과 같은 미들웨어를 제공한다.
negroni.Recovery : panic 복구
negroni.Logger : Request/Response 로깅
negroni.Static - "public" 디렉터리 아래의 정적 파일 serving
물론 Classic 이외에도 자신이 미들웨어를 등록해서 사용하는 등 다양한 기능을 이용할 수 있다.
gorilla/pat와 결합해서 사용한다면 다음과 같이 쓸 수 있다.
func main() {
mux := pat.New()
mux.Post("/users", addUserHandler)
mux.Delete("/users", leftUserHandler)
n := negroni.Classic()
n.UseHandler(mux)
http.ListenAndServe(":3000", n)
}
github.com/antage/eventsource
eventsource를 만드는 라이브러리로 chat 어플리케이션 등을 만들때 사용할 수 있다.
tucker programming 에서 나온 예제는 다음과 같다.
func main() {
msgCh = make(chan Message) // thread간 통신할 channel 초기화
es := eventsource.New(nil, nil)
defer es.Close() // eventsource는 꼭 닫아주어야 한다.
go processMsgCh(es) // go thread
mux := pat.New()
mux.Post("/messages", postMessageHandler)
mux.Handle("/stream", es) // eventsource 등록
mux.Post("/users", addUserHandler)
mux.Delete("/users", leftUserHandler)
n := negroni.Classic()
n.UseHandler(mux)
http.ListenAndServe(":3000", n)
}
github.com/unrolled/render
go의 struct, array, map 등을 JSON, XML, text, binary data, HTML template로 rendering 시켜주는 라이브러리다.
다음과 같은 예제를 보자.
var rd *render.Render
type Todo struct {
ID int `json:"id"`
Name string `json:"name"`
Completed bool `json:"completed"`
CreatedAt time.Time `json:"created_at"`
}
var todoMap map[int]*Todo
func addTodoHandler(w http.ResponseWriter, r *http.Request) {
name := r.FormValue("name")
id := len(todoMap) + 1
todo := &Todo{id, name, false, time.Now()}
todoMap[id] = todo
rd.JSON(w, http.StatusOK, todo)
}
..
func MakeHandler() http.Handler {
todoMap = make(map[int]*Todo)
..
rd = render.New()
r := mux.NewRouter()
r.HandleFunc("/todos", addTodoHandler).Methods("POST")
...
}
addTodoHandler에서 map[int]*Todo를 JSON형태로 변환해 client에게 전달해 주고 있다.
그외 Handler에서 유용한 함수들
1. query parsing
func (r *Request) FormValue(key string) string
query에서 named component의 첫번째 값을 반환한다. POST나 PUT의 경우 body parameter가 URL query string보다 우선한다.
예제 :
func postMessageHandler(w http.ResponseWriter, r *http.Request) {
msg := r.FormValue("msg")
name := r.FormValue("name")
sendMessage(name, msg)
}
2. Decode - 구조체에 response로 받은 것들을 할당할 때
go에서 JSON을 Go value로 바꾸는 것을 decoding이라고 하고 이때 json.Decoder가 사용된다.
func NewDecoder(r io.Reader) *Decoder
이중에서도 많이 사용되는 것은 Decode이다.
func (dec *Decoder) Decode(v interface{}) error
input에서 받은 JSON-encoded value를 v가 가리키는 곳에 저장한다.
예제를 한번 봐보자.
type User struct {
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Email string `json:"email"`
CreatedAt time.Time `json:"created_at"`
}
func (f *fooHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
user := new(User)
err := json.NewDecoder(r.Body).Decode(user)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprint(w, "Bad Request: ", err)
return
}
user.CreatedAt = time.Now()
data, _ := json.Marshal(user)
w.Header().Add("content-type", "application/json")
w.WriteHeader(http.StatusCreated)
fmt.Fprint(w, string(data))
}
request로 온 body를 새로 만든 User struct에 저장한다.
'언어 > Go' 카테고리의 다른 글
Effective Go (1) 요약 (0) | 2021.10.18 |
---|---|
동시성 패턴 (0) | 2021.05.05 |
고루틴 (0) | 2021.05.03 |
Test시 Cannot import "main"이 뜨며 build fail (0) | 2020.12.20 |
Local package import하기 / Test 자동화 (0) | 2020.12.19 |