공대생 정리노트

Server 함수 정리 본문

언어/Go

Server 함수 정리

woojinger 2020. 12. 29. 00:54

Go로 간단한 서버를 만들 때 사용되는 라이브러리들과 자주 사용되는 함수를 정리해보았다.

함수들은 모두 Go 강좌를 무료로 올려주신 Tucker Programming 채널을 보고 알게 되었다! 감사하신 분이다.

모든 라이브러리는 go get ~ 로 설치할 수 있다.

github.com/gorilla/pat

github.com/gorilla/pat

 

gorilla/pat

a pretty simple HTTP router for Go. Contribute to gorilla/pat development by creating an account on GitHub.

github.com

쉽게 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

github.com/gorilla/mux

 

gorilla/mux

A powerful HTTP router and URL matcher for building Go web servers with 🦍 - gorilla/mux

github.com

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

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

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

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
Comments