지난 포스팅때 예고했던 것처럼 오늘은 Handler를 구현해볼 예정이다.
package main
import (
"log"
"net/http"
"time"
"github.com/hoondal6970/Go_server_prac/web6/myapp"
)
func logger(w http.ResponseWriter, r *http.Request, h http.Handler) {
start := time.Now()
log.Print("[LOGGER1] Started")
h.ServeHTTP(w, r)
log.Println("[LOGGER1] Completed time: ", time.Since(start).Millisecond)
}
func NewHandler() http.Handler {
mux := myapp.NewHandler()
return mux
}
func main() {
mux := NewHandler()
//Decorator로 감싸준다.
mux = decoHandler.NewDecoHandler(mux, logger)
http.ListenAndServe("3000", mux)
}
우선 main.go
부터 만들고 시작한다. 앞선 포스팅에서 했던 것과 같이, NewHandler()
를 만들어주고, 서버 세팅부터 한다.
그리고, Logger라는 이름의 log를 찍어주는 역할의 Decorator를 만들어준다.
logger()
를 뜯어보면, log를 찍어주는데, Handler()
가 실행되기 전의 시간과, 그때의 시간부터 걸린 시간을 time.Since()
로 찍어주는 decorator이다.
당연히 test code도 한번 작성해보자.
func TestDecoHandler(t *testing.T) {
assert := assert.New(t)
ts := httptest.NewServer(NewHandler())
defer ts.Close()
buf := &bytes.Buffer{}
log.SetOutput(buf)
resp, err := http.Get(ts.URL)
assert.NoError(err)
assert.Equal(http.StatusOK, resp.StatusCode)
r := bufio.NewReader(buf)
line, _, err := r.ReadLine()
assert.NoError(err)
assert.Contains(string(line), "[LOGGER] Started")
}
log가 찍히는 것을 확인하는 테스트이다.
log.SetOutput()
은 log가 화면에 찍히는 것의 위치를 인자로 받는 곳으로 바꿔주는 함수인데 여기에 buffer를 하나 넣어서 buffer를 확인하는 방식으로 확인한다.
buffer에서 log가 어떤방식으로 되어있나? main.go
를 보면 한줄씩 log를 작성한다.
지금 저 buffer는 binary buffer이기 때문에, bufio.NewReader()
bufio 패키지의 NewReader()
를 사용해서 읽을 수 있도록 바꿔준다. 이후 Readline()
을 사용하면 한번에 한 줄씩 읽는다.
기본적인 세팅이 끝났으니, Decorator를 설정해주자.
package decoHandler
import "net/http"
type DecoratorFunc func(http.ResponseWriter, *http.Request, http.Handler)
type DecoHandler struct {
fn DecoratorFunc
h http.Handler
}
func (self *DecoHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
self.fn(w, r, self.h)
}
func NewDecoHandler(h http.Handler, fn DecoratorFunc) http.Handler {
return &DecoHandler{
fn: fn,
h: h,
}
}
Decorator를 위해서 간단한 decoHandler
패키지를 작성한 코드이다.
DecoratorFunc type
을 지정해주고, ServeHTTP()
인터페이스가 있다. 이 함수는 추후에 main.go
에서 Logger()
함수에서 handler()
를 호출해 줄 것이다.
말로만 적혀있어서 헷갈릴 수 있는데, 실행되는 순서대로 따라가면 이해에 도움이 된다.
main.go
파일에서, NewHandler()
로 생성된 mux를 Decorator로 감싸준다.NewDecoHandler()
로 리턴된 새로운 mux로 서버가 실행된다.
테스트 코드로 GET
요청을 보내면, 순서대로 Log를 먼저찍게되고 내부에서 h.ServeHTTP()
로 함수가 실행되면서 실제 request에 대한 response를 보내게된다. 그리고 다시 log로 실행에 걸린 시간이 문구와 함께 찍히게 된다.
아래와 같이 main.go
에 한번더 감싸주게 되면 쉽게 Decorator를 추가할 수 있다
func logger(w http.ResponseWriter, r *http.Request, h http.Handler) {
start := time.Now()
log.Print("[LOGGER1] Started")
h.ServeHTTP(w, r)
log.Println("[LOGGER1] Completed time: ", time.Since(start).Milliseconds())
}
func logger2(w http.ResponseWriter, r *http.Request, h http.Handler) {
start := time.Now()
log.Print("[LOGGER2] Started")
h.ServeHTTP(w, r)
log.Println("[LOGGER2] Completed time: ", time.Since(start).Milliseconds())
}
func NewHandler() http.Handler {
h := myapp.NewHandler()
h = decoHandler.NewDecoHandler(h, logger)
h = decoHandler.NewDecoHandler(h, logger2)
return h
}
위 코드는 main.go
파일에서 일부분만 추가했다. logger2
함수를 추가했고, 이를 NewDecoHandler()
로 한번 더 감싸줬다. 이렇게 작성하면
2023/02/01 11:18:51 [LOGGER2] Started
2023/02/01 11:18:51 [LOGGER1] Started
2023/02/01 11:18:51 [LOGGER1] Completed time: 0
2023/02/01 11:18:51 [LOGGER2] Completed time: 195
이런 로그를 얻게된다. Logger2()
가 먼저 실행되면서 Logger1()
을 내부에서 호출하는 식으로 실행되며 그대로 log가 찍힌 것을 볼 수 있다.
다음 포스팅에서는 Web Template에 대해서 작성할 예정이다. 더 자세한 강의를 보고 싶으면 유툽에 올라와있는 Tucker Programming의 강의를 보기를 추천한다.
(출처:Tucker의 Go로 만드는 웹)