기존에 gorilla mux를 사용하여 핸들러 처리를 해줄 때 코드를 살펴보자
Gorilla mux
go get github.com/gorilla/mux
package main
import (
"encoding/json"
"fmt"
"net/http"
"time"
"github.com/gorilla/mux"
)
type User struct {
Name string `json:"name"`
Email string `json:"email"`
CreatedAt time.Time `json:"created_at"`
}
func getUserInfoHandler(w http.ResponseWriter, r *http.Request) {
user := User{Name: "soosungp33", Email: "soosungp33@gamil.com"}
w.Header().Add("Content-type", "application/json")
w.WriteHeader(http.StatusOK)
data, _ := json.Marshal(user)
fmt.Fprint(w, string(data))
}
func addUserHandler(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, err)
return
}
user.CreatedAt = time.Now()
w.Header().Add("Content-type", "application/json")
w.WriteHeader(http.StatusOK)
data, _ := json.Marshal(user)
fmt.Fprint(w, string(data))
}
func main() {
mux := mux.NewRouter()
mux.HandleFunc("/users", getUserInfoHandler).Methods("GET")
mux.HandleFunc("/users", addUserHandler).Methods("POST")
http.ListenAndServe(":3000", mux)
}
이 코드를 pat을 사용한 코드와 비교해 차이점을 봐보자
Gorilla pat
go get github.com/gorilla/pat
package main
import (
"encoding/json"
"fmt"
"net/http"
"time"
"github.com/gorilla/pat"
)
type User struct {
Name string `json:"name"`
Email string `json:"email"`
CreatedAt time.Time `json:"created_at"`
}
func getUserInfoHandler(w http.ResponseWriter, r *http.Request) {
user := User{Name: "soosungp33", Email: "soosungp33@gmail.com"}
w.Header().Add("Content-type", "application/json")
w.WriteHeader(http.StatusOK)
data, _ := json.Marshal(user)
fmt.Fprint(w, string(data))
}
func addUserHandler(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, err)
return
}
user.CreatedAt = time.Now()
w.Header().Add("Content-type", "application/json")
w.WriteHeader(http.StatusOK)
data, _ := json.Marshal(user)
fmt.Fprint(w, string(data))
}
func main() {
mux := pat.New()
mux.Get("/users", getUserInfoHandler)
mux.Post("/users", addUserHandler)
http.ListenAndServe(":3000", mux)
}
mux와 차이점은 메소드를 뒤에 붙이냐 앞에 붙이냐 차이이다.
템플릿을 사용해 핸들러를 전달해보자
templates 폴더를 하나 만들어주고 폴더 안에 hello.tmpl이라는 파일을 만들어준다.
다음 main.go 코드에 템플릿을 사용하는 핸들러를 추가해보자
...
func helloHandler(w http.ResponseWriter, r *http.Request) {
tmpl, err := template.New("Hello").ParseFiles("templates/hello.tmpl")
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprint(w, err)
return
}
tmpl.ExecuteTemplate(w, "hello.tmpl", "soosungp33")
}
...
func main() {
mux := pat.New()
mux.Get("/users", getUserInfoHandler)
mux.Post("/users", addUserHandler)
mux.Get("/hello", helloHandler)
http.ListenAndServe(":3000", mux)
}
이렇게 템플릿을 사용해봤는데 매번 json쓰고 템플릿쓰고 하는게 반복되는 코드도 많고 귀찮다.
간단하게 해주는 패키지를 사용해보자
Render는 JSON, XML, HTML templates responses를 쉽게 해주는 패키지이다.
Render
go get github.com/unrolled/render
사용법은 되게 간단하다.
전역 변수로 render.Render를 포인터로 선언 후 사용하면 된다.
var rd *render.Render
핸들러 부분(addUserHandler)
(getUserInfoHandler도 비슷한 맥락, 에러부분도 확인해보기)
w.Header().Add("Content-type", "application/json")
w.WriteHeader(http.StatusOK)
data, _ := json.Marshal(user)
fmt.Fprint(w, string(data))
->
rd.JSON(w, http.StatusOK, user)
템플릿 부분(helloHandler)
tmpl, err := template.New("Hello").ParseFiles("templates/hello.tmpl")
if err != nil {
rd.Text(w, http.StatusBadRequest, err.Error())
return
}
rd.HTML(w, http.StatusOK, "hello.tmpl", "soosungp33")
tmpl.ExecuteTemplate(w, "hello.tmpl", "soosungp33")
->
rd.HTML(w, http.StatusOK, "hello", "soosungp33")
// render가 등록할 때 확장자를 빼고 등록함 -> 따라서 hello.tmpl이 아닌 hello
위와 같이 엄청 간단하게 구현한 것을 볼 수 있다.
html로 된 파일을 읽고 싶으면 옵션을 넣어줘야한다.
main.go
rd = redner.New(render.Optioins{
Extensions: []string{".html", ".tmpl"},
})
html확장자와 tmpl확장자를 둘다 읽어라 라고 옵션을 주면 된다.
폴더명을 templates에서 template로 변경하고 싶을 때
main.go
re = render,New(render.Optioins{
Directory: "template",
})
라고 옵션을 주면 된다.
전체 코드(템플릿 폴더 명을 template로 바꾼 상태)
package main
import (
"encoding/json"
"net/http"
"time"
"github.com/gorilla/pat"
"github.com/unrolled/render"
)
var rd *render.Render
type User struct {
Name string `json:"name"`
Email string `json:"email"`
CreatedAt time.Time `json:"created_at"`
}
func getUserInfoHandler(w http.ResponseWriter, r *http.Request) {
user := User{Name: "soosungp33", Email: "soosungp33@gmail.com"}
rd.JSON(w, http.StatusOK, user)
}
func addUserHandler(w http.ResponseWriter, r *http.Request) {
user := new(User)
err := json.NewDecoder(r.Body).Decode(user)
if err != nil {
rd.Text(w, http.StatusBadRequest, err.Error())
return
}
user.CreatedAt = time.Now()
rd.JSON(w, http.StatusOK, user)
}
func helloHandler(w http.ResponseWriter, r *http.Request) {
rd.HTML(w, http.StatusOK, "hello", "soosungp33")
// render가 등록할 때 확장자를 빼고 등록함 -> 따라서 hello.tmpl이 아닌 hello
}
func main() {
rd = render.New(render.Options{
Directory: "template",
Extensions: []string{".html", ".tmpl"},
})
mux := pat.New()
mux.Get("/users", getUserInfoHandler)
mux.Post("/users", addUserHandler)
mux.Get("/hello", helloHandler)
http.ListenAndServe(":3000", mux)
}
template 폴더안에 hello.html과 body.html을 만들어준다.
hello.html
<html>
<head>
<title>{{ partial "title" }}</title>
</head>
<body>
Hello World {{ yeild }}
</body>
</html>
템플릿 핸들러가
rd.HTML(w, http.StatusOK, "body", user)
이기 때문에 body라는 이름에 영향을 받는다.
따라서 yeild 부분에는 그냥 body.html이 들어가고
partial "title" 부분은 title 뒤에 body가 붙어서 title-body.html이 들어간다.
body.html
Name: {{.Name}}
Email: {{.Email}}
title-body.html
Partial Go In web
이 3개를 읽어서 레이아웃을 넣자.
main.go 변경
...
func helloHandler(w http.ResponseWriter, r *http.Request) {
user := User{Name: "soosungp33", Email: "soosungp33@gmail.com"}
rd.HTML(w, http.StatusOK, "body", user)
}
...
func main() {
rd = render.New(render.Options{
Directory: "template",
Extensions: []string{".html", ".tmpl"},
Layout: "hello",
})
...
}
이런식으로 부분 템플릿을 만들어서 레이아웃에다가 조합할 수 있다.
마지막으로 Negroni는 HTTP 미들웨어이다.
Negroni
go get github.com/urfave/negroni
기본적으로 많은 부가기능을 제공한다.
ex) FileServer를 기본적으로 제공
public 폴더에 index.html을 생성
index.html
<html>
<head>
<title>Go in Web 11</title>
</head>
<body>
<h1>Hello Go in Web</h1>
</body>
</html>
main.go
func main() {
mux := pat.New()
mux.Handle("/", http.FileServer(http.Dir("public")))
http.ListenAndServe(":3000", mux)
}
핸들러의 파일명이 생략되어 있으면 index.html
or default.htm
이라는 파일에 액세스한다.
따라서 실행하게 되면 public에 있는 index.html이 기본적으로 나타나게 된다.
이 코드를 negroni를 사용하면 어떻게 변경되는지 보자.
main.go
func main() {
mux := pat.New()
n := negroni.Classic()
n.UseHandler(mux)
http.ListenAndServe(":3000", n)
}
Classic은 기본 파일서버를 가지고 있고 추가로 로그 기능도 제공한다.
실행하면 FileServer를 사용할 때와 같은 결과를 얻고 추가로 로그도 터미널에 출력되는 걸 볼 수 있다.