본문 바로가기

Language/Go

[Go] - web programming : handler 기초

Go를 이용하여 request handling 기초를 다뤄보자.

1. 간단한 handler를 작성하는 법과, 구조 및 DefaultServeMux가 무엇인지

2. HandleFunc과 Handle에 대해서

3. Chaining 에 대해서

4. 새로운 Mux 사용 및 패턴매칭

 

 

net/http 패키지 중.

 

func ListenAndServe(addr string, handler Handler) error

 

1. 간단한 handler와 DefaultServeMux

package main

import (
	"fmt"
	"net/http"
)

type handler struct {
	s string
}

func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, h.s)
}


func main() {
	hello := handler{"Hello"}
	world := handler{"world"}

	http.Handle("/hello", &hello)
	http.Handle("/world", &world)
	server := http.Server {
		Addr: "127.0.0.1:8080",
		Handler: nil,
	}
	server.ListenAndServe()
}

 

hello 와 world를 http.Handle에 등록 하면, 알아서 DefaultServeMux에 들어가기 때문에, url에 맞는 handler가 실행이 된다.

 

 

 

하지만 server의 Handler를 지정해 주게 되면,

package main

import (
	"fmt"
	"net/http"
)

type handler struct {
	s string
}

func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, h.s)
}


func main() {
	hello := handler{"Hello"}
	world := handler{"world"}
	Go := handler{"Go web programming!"}

	http.Handle("/hello", &hello)
	http.Handle("/world", &world)
	server := http.Server {
		Addr: "127.0.0.1:8080",
		Handler: &Go,
	}
	server.ListenAndServe()
}

 

world URL에 대해서도, Go handler의 함수가 실행된다.

 

ServeMux는 Handler 구조의 인스턴스 이며, DefaultServeMux는 서버의 Handler가 nil일 때, 자동으로 등록되는 mux이며, ServeMux의 인스턴스 이다. 실제로 Handler를 nil, http.Handle을 호출하면 DefaultServeMux에 각각의  URL에 handler가 등록이 된다.

 

2. HandleFunc

 

http.HandleFunc의 소스코드

func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	if handler == nil {
		panic("http: nil handler")
	}
	mux.Handle(pattern, HandlerFunc(handler))
}

 HandleFunc는 func(ResponseWriter, *Request)의 형태를 가진 함수를 핸들러 형태로 바꿔준다.

 

 

예제

package main

import (
	"fmt"
	"net/http"
)

type handler struct {
	s string
}

func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, h.s)
}

func helloHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "Hello!")
}

func worldHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "world!")
}
func main() {
	hello := handler{"Hello"}
	world := handler{"world"}
	http.Handle("/hello", &hello)
	http.Handle("/world", &world)
	http.HandleFunc("/hello2", helloHandler)
	http.HandleFunc("/world2", worldHandler)
	server := http.Server {
		Addr: "127.0.0.1:8080",
		Handler: nil,
	}
	server.ListenAndServe()
}

 

 

 

HandleFunc를 이용한 결과

 

실제로 ServeHTTP 인터페이스를 구현하지 않고, Handler와 같은 파라미터를 가진 함수만 만들어 주면, 쉽게 핸들러를 사용할 수 있다.

 

3. Chaining

- a 함수가 처리되고 난 후, b 함수가 실행되도록 하고 싶을때 사용

- 로깅이나 보안, 에러 핸들링은 cross-cutting concern 이다. --> 함수에 공통 요소들을 추가하는 것을 피하는 것

 

예제

package main

import (
	"fmt"
	"net/http"
	"reflect"
	"runtime"
)

func hello(w http.ResponseWriter, r *http.Request)  {
	fmt.Fprintf(w, "Hello!")
}

func log(h http.HandlerFunc) http.HandlerFunc  {
	return func(w http.ResponseWriter, r *http.Request) {
		name := runtime.FuncForPC(reflect.ValueOf(h).Pointer()).Name()
		fmt.Println("Handler function called - " + name)
		h(w, r)
	}
}

func main() {
	server := http.Server {
		Addr: "127.0.0.1:8080",
	}
	http.HandleFunc("/hello", log(hello))
	server.ListenAndServe()
}

hello 함수에 log 기능의 코드를 추가하지 않고, log 함수를 체이닝 하여 처리한다.

 

log 출력

 

4. ServeMux

- ServeMux는 HTTP 요청 멀티플렉서

- 요청 URL에 따라 요청 URL에 대응하여 적절한 핸들러로 연결

- DefaultServMux는 ServeMux의 "인스턴스"

- ServerMux는 URL 매칭을 처리할 수 있는 패턴 변수를 지원하지 않음

 

예제

package main

import (
	"fmt"
	"github.com/julienschmidt/httprouter"
	"net/http"
)

func hello(w http.ResponseWriter, r *http.Request, p httprouter.Params)  {
	fmt.Fprintf(w, "hello, %s\n", p.ByName("name"))
}

func main() {
		mux := httprouter.New()
		mux.GET("/hello/:name", hello)

		server := http.Server{
			Addr:    "127.0.0.1:8080",
			Handler: mux,
		}
		server.ListenAndServe()
}

github.com/julienschmidt/httprouter 서드파티 라이브러리 사용

go get github.com/julienschmidt/httprouter

 

 

위의 코드에서  httprouter를 통해 생성한 새로운 mux 인스턴스를 이용하여 get 요청을 하고, name이라는 값을 패턴매칭 하여 결과를 보여준다.