golang http 패키지

728x90

http.Servemux & http.HandleFunc

ServeMux는 HTTP 요청 멀티플렉서이다. 여기서 멀티플렉서는 라우터, 컨트롤러랑 동일한 뜻을 갖고 있다고 보면 된다.

수신되는 각 요청의 URL 을 등록된 패턴과 비교하고, URL과 가장 일치하는 패턴의 핸들러를 호출한다. 

MVC 패턴의 Controller 역할이라고 보면 된다.

 

또한 ServeMux는 URL Request Path 와 Host Header 를 임의로 처리하는데, 포트 번호를 제거하거나 반복되는 슬래시가 포함된 요청을 동등하고 깔끔한 URL로 리다이렉션한다.

 

아래는 server.go 에 작성된 http 패키지 코드이다.

// DefaultServeMux is the default ServeMux used by Serve.
var DefaultServeMux = &defaultServeMux

var defaultServeMux ServeMux

// HandleFunc registers the handler function for the given pattern
// in the DefaultServeMux.
// The documentation for ServeMux explains how patterns are matched.
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	DefaultServeMux.HandleFunc(pattern, handler)
}

 

HandleFunc 메소드는 DefaultServeMux 의 HandleFunc 를 사용한다.

mux := http.NewServeMux()
mux.HandleFunc("/", handlerFunc)

http.HandleFunc("/", handlerFunc)

 

http.HandlFunc() 메소드는 ServeMux 구조체의 메소드이다. 그래서 mux 를 새로 생성하여 HandleFunc 를 구현할 수도 있다.

http.HandlerFunc

아래 코드는 server.go 에 작성된 http 패키지 코드이다.

 

http 패키지를 보면 HandlerFunc 라는 함수 타입을 선언했는데 해당 타입은 ResponseWriter 와 *Request 를 매개변수로 갖는 함수 타입이다. 즉 구조체 타입을 생성하는 것 말고도 함수 타입을 생성할 수 있다.

 

함수 타입은 다른 타입과 동일하게 취급된다. 중요한 점이자 신기한 점은 함수 타입이 구조체 타입처럼 메소드를 가질 수 있다는 점이다.

그래서 http.HandleFunc 타입은 스스로를 호출하는 ServeHTTP 메소드를 구현할 수 있다.

 

http.HandleFunc 은 http.HandlerFunc 를 매개변수로 받는다. http.HandlerFunc 는 함수 타입이며, ServeHTTP 라는 메소드를 갖는다.

ServeHTTP 메소드를 구현하기 때문에 HandlerFunc 는 Handler 인터페이스이다.

 

http.Handler 는 ServeHTTP(ResponseWriter, *Request) 메소드를 갖는 인터페이스 타입이다.

type Handler interface {
	ServeHTTP(ResponseWriter, *Request)
}

// The HandlerFunc type is an adapter to allow the use of
// ordinary functions as HTTP handlers. If f is a function
// with the appropriate signature, HandlerFunc(f) is a
// Handler that calls f.
type HandlerFunc func(ResponseWriter, *Request)

// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
	f(w, r)
}

 

pathHandler  http.HandlerFunc 타입의 메서드 시그니처와 일치하기 때문에 HandlerFunc로 변환되어 router 변수에 할당된다.

 

여기서 메서드 시그니처는 함수 또는 메서드의 형태를 설명하는 것으로 메서드의 이름, 매개변수 유형, 반환 유형 등을 포함한다.

pathHandler 함수가 http.HandlerFunc 의 메서드 시그니처를 따르는 것을 확인할 수 있다.

 

다시 말해 func(w http.ResponseWriter, r *http.Request) 와 같은 형태를 가지면 http.HandlerFunc 타입으로 변환되어 http.Handler 인터페이스를 구현하는데 사용할 수 있다.

func pathHandler(w http.ResponseWriter, r *http.Request) {
	switch r.URL.Path {
	case "/":
		homeHandler(w, r)
	case "/contact":
		contactHandler(w, r)
	default:
		pageNotFoundHandler(w, r)
	}
}

func main() {
  var router http.HandlerFunc
  router = pathHandler
  fmt.Println("Starting the server on :3000...")
  http.ListenAndServe(":3000", router)
}


func main() {
  fmt.Println("Starting the server on :3000...")
  // int(r) 이나 float64(a) 처럼 pathHandelr 를 변환하는 것이다.
  // 즉, router 변수를 선언하고 pathHandler 를 값으로 넣은 것을 
  // http.HandleFunc(pathHandler) 로 줄인 것
  http.ListenAndServe(":3000", http.HandlerFunc(pathHandler)) 
}

http.ListenAndServe 와 Interface

아래는 server.go 에 작성된 http 패키지 코드이다.

// A Handler responds to an HTTP request.
//
// ServeHTTP should write reply headers and data to the ResponseWriter
// and then return. Returning signals that the request is finished; it
// is not valid to use the ResponseWriter or read from the
// Request.Body after or concurrently with the completion of the
// ServeHTTP call.
//
// Depending on the HTTP client software, HTTP protocol version, and
// any intermediaries between the client and the Go server, it may not
// be possible to read from the Request.Body after writing to the
// ResponseWriter. Cautious handlers should read the Request.Body
// first, and then reply.
//
// Except for reading the body, handlers should not modify the
// provided Request.
//
// If ServeHTTP panics, the server (the caller of ServeHTTP) assumes
// that the effect of the panic was isolated to the active request.
// It recovers the panic, logs a stack trace to the server error log,
// and either closes the network connection or sends an HTTP/2
// RST_STREAM, depending on the HTTP protocol. To abort a handler so
// the client sees an interrupted response but the server doesn't log
// an error, panic with the value ErrAbortHandler.
type Handler interface {
	ServeHTTP(ResponseWriter, *Request)
}

...

// ListenAndServe listens on the TCP network address addr and then calls
// Serve with handler to handle requests on incoming connections.
// Accepted connections are configured to enable TCP keep-alives.
//
// The handler is typically nil, in which case the DefaultServeMux is used.
//
// ListenAndServe always returns a non-nil error.
func ListenAndServe(addr string, handler Handler) error {
	server := &Server{Addr: addr, Handler: handler}
	return server.ListenAndServe()
}

 

http.ListenAndServe 메소드는 Handler 인터페이스를 매개변수로 입력받는다.

 

Handler 인터페이스는 ServeHttp(ResponseWriter, *Request) 메소드를 구현하면 Handler 인터페이스로 취급을 할 수 있다.

 

아래 코드처럼 Router 구조체를 만들고 해당 구조체가 ServeHttp(ResponseWriter, *Request) 메소드를 구현하여 Handler 인터페이스 타입이 됐다.

 

그래서 http.ListenAndServe 메소드에 매개변수로 router 를 입력할 수 있다.

 

추가적으로 Router 구조체에 필드를 추가하여 ServeHTTP 메소드에서 구조체 필드를 가져와 사용하는 등의 작업을 할 수 있다.(db 커넥션 등..)

package main

import (
	"fmt"
	"net/http"
)

type Router struct {
}

func (s Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	switch r.URL.Path {
	case "/":
		homeHandler(w, r)
	case "/contact":
		contactHandler(w, r)
	default:
		pageNotFoundHandler(w, r)
	}
}

func main() {
	var router Router
	fmt.Println("Starting the server on :3000!")
	http.ListenAndServe(":3000", router)
}
728x90