Backend with Golang

Building HTTP Services - Handler (Network programming with Go)

아직개구리 2023. 7. 30. 15:42

Go HTTP Server는 먼저 Server의 multiplexer가 있고, 이 client request 를 받는 것이다. 

Multiplexer 는 request 의 목적지를 결정한다. 그리고 처리할 수 있는 것으로 전달한다. 처리하는 곳이 Handler이다. 

Multiplexer 자체도 handler 인데, request를 가장 적절한 handler로 라우팅하는 역할을 한다. 

Handler가 요청을 전달받기 전에 middleware를 거치게 된다. 

Middleware는 handler의 behavior를 변경하거나 auxiliary task들을 수행한다. 예를 들면 logging, authentication, access control 등이 있다. 

 

 

srv := &http.Server {
    Addr: 
    Handler: 
    IdleTimeout: // next request 가 올떄까지 TCP socket을 얼마 시간동안 열고 있을지. 
    ReadHeaderTimeout: // request header를 읽는데 얼마나 기다릴 것인지. 
}

l, err := net.Listen("tcp", srv.Addr) 
if err != nil {
	t.Fatal(err)
}

go func() {
    err := srv.Serve(l)
    if err != http.ErrServerClosed {
    	t.Error(err)
    }
}()

new net.Lister에 server's address를 바운드 시키고, server에게 이 listener를 통해 serve requests하도록 지시하는 코드이다. 

 

Handler 는 http.Handler interface를 implement하는데,  Handler interface는 ServeHTTP라는 하나의 method로 이루어져 있다.  http.HandlerFunc 으로 function으로 정의할 수도 있다. parameter로 들어가는 function은 server 가 ServeHTTP method를 부를 때 불리게 된다. 

handler := http.HandlerFunc(
	func(w http.ResponseWriter, r *http.Request) {
    	 _, _ = w.Write([]byte("Hello, world!"))
    }, 
)

이때 여기서 write에서 나올 수 있는 에러는 왜 처리하지 않는가? 다양한 이유로 에러가 나타나게 되는데, 이걸 logging하는 건 worthy하지 않고 차라리 frequency를 측정해서 어느정도 그 threshold를 넘어갔을 때 alert가 오도록 하는 것이 좋다. 

func DefaultHandler() http.Handler {
    return http.HandlerFunc(
        func(w http.ResponseWriter, r *http.Request) {
        	
        }
    )
}

http.HandlerFunc 타입은 http.Handler interface를 implement한다. 

client에서 TCP session을 재사용하기 위해서 drain & close response body를 하는 것이 중요한 것처럼 server도 request body에 대해서 똑같이 해줘야한다. GO HTTP client와 달리 close 하는 것이 request body를 drain시키지 않기 때문에 explicit하게 해줘야한다. 

 

How you write Response 

Response를 작성하는 것도 순서가 중요하다. Client는 response status code를 먼저 받고 이어서 response body를 받는다. 그래서 body를 서버에서 먼저 write한 경우, GO는 status code를 200  OK라고 인지하고, 그걸 response body를 보내기 전에 보내버린다. 이렇게 WriteHeader를 통해서 explicit, implicit하게 response의 status code가 정해진 경우에는 변경이 불가능하다. 

결과적으로 WriteHeader method를 통해서 response body에 쓰기 전에 response status code를 설정해 주는 방법이 유일한데, 이는 http.Error function으로 개선될 수 있다. 

 

Any Type Can Be a Handler 

http.Handler는 interface이기 때문에 client request들을 핸들링하는 powerful한 구조를 짤 수 있다. 

type Methods map[string]http.Handler : 이런 방식으로 Methods의 ServeHttp 메소드를 가지기 때문에  http.Handler interface 를 implement 한다고 할 수 있다. Methods 자체만으로 handler가 될 수 있는 것이다. 

Methods type 은 multiplexer라고 할 수 있다. 

Methods type을 선언할 때, method 종류별로 HandlerFunc()를 사용해서 serveHTTP 함수를 정의하게 된다. 

 

Injecting Dependencies into Handlers 

보통 이제 handler에 error를 기록할 수 있는 logger 혹은 response 를 생성하기 위해서 db obejct를 inject하고 싶을 것이다. 

가장 쉬운 방법은 closure를 사용하는 것이다. 하지만 여러가지 object를 참조하고 싶을 수도 있기 때문에, extensible한 구조가 필요하게 될 것이다. 아래와 같이 closure definition를 변경하는 대신에 struct field를 추가하는 것으로 바꿀 수 있다. 

type Handlers struct {
    db *sql.DB
    log *log.Logger 
}


h := &Handlers {
    db: db,
    log: log.New(os.Stderr, "handlers: ", log.Lshortfile)
}

http.Handle("/one", h.Handler1())
http.Handler("/two", h.Handler2())

그렇게 되면, struct를 정의할 때 database object와 logger의 pointer를 포함하도록 할 수 있고, 그렇게 되면 어떤 methods에서도 이 object들에 접근 가능하게 된다. 그리고 더 필요한 resource가 있을 때, 단순하게 struct에 추가만 하면 되는 것이다. 

 

 

 

 

 

 

**새로 알게 된 단어

- drop-in replacement