don't stop believing

체이닝 함수 본문

Golang/Web App

체이닝 함수

Tongchun 2019. 2. 6. 15:22

Go는 함수형 언어가 아니지만 함수형 언어의 특징을 포함합니다. 함수형 타입과 익명 함수, 그리고 클러저와 같은 것들이 있습니다.


만약 자바나 다른 언어에서 하나의 handler 함수에서 공통으로 사용하는 기능이 있다면 개별 공통 함수로 만들어 놓고 handler 함수안에서 불러다 사용할 것입니다. Go에서도 비슷한 계념으로 체이닝 함수가 있습니다. 두개 또는 여러개의 함수가 채인으로 연결되어 있다는 의미입니다.


예로 handler 함수가 호출됐을 때 log를 기록하는 공통 기능이 있다면 매번 작성하는 것이 아니라 체이닝 함수로 만들어 줍니다.

아래 예제 코드를 보겠습니다.

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 functin 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 함수가 선언되었습니다. 이 함수는 HandlerFunc를 취하고 HandlerFunc를 반환합니다. 기억해야 할 것은 hello는 HandlerFunc이며, hello 함수는 log 함수로 보내집니다. log 함수와 hello 함수는 묶여(chain)있다고 볼 수 있습니다.


log 함수는 익명 함수를 리턴하고, ResponseWriter와 Request를 가리키는 포인터를 취합니다. 이것이 의미하는 것은 익명 함수는 HandlerFunc이라는 것입니다.

위 코드를 빌드하고 실행해 보겠습니다.

$ go install ./src/chainingHandler/
$ ./bin/chainingHandler

 브라우저를 열고 localhost:8080/Hello를 호출합니다.

Hello!를 확인했다면 terminal 창을 확인합니다.

Handler functin called - main.hello

log 함수안의 익명함수가 실행되면서 호출된 함수의 이름이 기록됩니다.


위에서 처럼 두 핸들러 함수를 함께 연결했듯이 그 이상의 핸들러 함수도 묶을 수 있습니다. 같은 원리로 스택 핸들러(stac handlers)에 대한 여러 동작을 취하도록 할 수 있습니다. 이것을 다른 말로 파이프라인 처리(pipeline processing)라고 합니다.


이번에는 3개의 함수를 묶어 처리해 보겠습니다.

protect라는 함수를 추가하는데, 이 함수는 핸들러를 실행하기 전에 유저의 인증 여부를 검사하는 함수로 가정해 보겠습니다.

package main

import (
	"fmt"
	"net/http"
)

type TongchunHandler struct{}

func (h TongchunHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello Tongchun!")
}

func log(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Printf("Handler called - %T\n", h)
		h.ServeHTTP(w, r)
	})
}

func protect(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Printf("check user authorization!\n")
		h.ServeHTTP(w, r)
	})
}

func main() {
	server := http.Server{
		Addr: "127.0.0.1:8080",
	}

	hello := TongchunHandler{}
	http.Handle("/Tongchun", protect(log(hello)))
	server.ListenAndServe()
}

이번에는 TongchunHandler를 만들어 사용했습니다.

그리고 log 함수는 HandlerFunc 함수를 가져와 HandlerFunc을 반환하는 것이 아닌, Handler를 취하고 Handler를 반환합니다.

HandlerFunc이 아닌 Hanlder를 사용했기 때문에 ServeHTTP 함수를 호출해 사용했습니다.

hello := TongchunHandler{}

http.Handle("/Tongchun", protect(log(hello)))

server.ListenAndServe()

위에서 처럼 protect가 먼저 실행되고 log, 그다음 hello 함수가 차례대로 실행됩니다.

이제 빌드하고 브라우저에서 실행해 보겠습니다.

$ go install ./src/chainingHandler/
$ ./bin/chainingHandler

브라우저를 다시 열고 localhost:8080/Tongchun을 호출해 봅니다.

터미널 창을 보면 인증 과정과 로그 처리가 순서대로 출력돼있습니다.

check user authorization!
Handler called - main.TongchunHandler

핸들러와 핸들러 함수를 사용하는 것은 많은 웹사이트에서 사용하는 익숙한 방법입니다.


'Golang > Web App' 카테고리의 다른 글

Header와 body 확인하기  (1) 2019.02.09
Handler와 Multiplexer 이해하기  (0) 2019.02.07
핸들링 요청과 핸들링 함수  (0) 2019.02.02
첫 번째 웹 서버(Hello Tongchun!)  (0) 2019.01.31
Comments