[Go] Go Web Programming Basic 03 - Routing

Routing

이번에는 좀더 Routing 에 대해서 깊이 있게 알아보자.

HTTP Method

웹은 기본적으로 CRUD를 위한 GET, POST, PUT, DELETE 를 제공한다.

그 밖에도 HEAD, PATHCH 등 다양한 메소드를 제공한다.

이전 예제에서 핸들러를 단순 등록하면 모든 Method 를 받아 들일 수 있다.

그러나 이는 좋은 방법이 아니며, 용도에 맞는 메소드를 지정하여 endpoint 를 열어주는 것이 필요하다.

Method 이용하기.

main.go 에 다음과 같이 내용을 변경해보자.

package main

import (
	"fmt"
	"html"
	"log"
	"net/http"

	"github.com/gorilla/mux"
)

func main() {

	r := mux.NewRouter()
	r.HandleFunc("/", HelloWorld).Methods("GET")
	r.HandleFunc("/path/value", ResponsePath).Methods("GET")
	r.HandleFunc("/users", CreateUser).Methods("POST")
	r.HandleFunc("/users", UpdateUser).Methods("PUT")
	r.HandleFunc("/users", DeleteUser).Methods("DELETE")
	r.HandleFunc("/my", CallMy).Methods("POST", "PUT")
	http.Handle("/", r)

	fmt.Println("Server start on port: ", 8080)
	log.Fatal(http.ListenAndServe(":8080", nil))
}

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

func ResponsePath(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Your request path is :%q", html.EscapeString(r.URL.Path))
}

func CreateUser(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "CreateUser :")
}

func UpdateUser(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "UpdateUser :")
}

func DeleteUser(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "DeleteUser :")
}

func CallMy(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Call My Page :")
}


/users 경로에 대해서 POST, PUT, DELETE 메소드를 각자 지정하고, 필요한 핸들러를 작성해 주었다.

위처럼 Methods() 함수를 이용하여 다양한 요청을 처리할 수 있다.

/my 경로에 대해서는 POST, PUT 2개의 메소드만 허용하도록도 설정해 보았다.

테스트 하기.

 curl http://localhost:8080/users -X POST
CreateUser :

 curl http://localhost:8080/users -X PUT 
UpdateUser :

 curl http://localhost:8080/users -X DELETE
DeleteUser :

/users 에대서 3개의 메소드 호출이 정상적으로 라우팅 됨을 확인할 수 있다.

 curl http://localhost:8080/my -X PUT
Call My Page :

 curl http://localhost:8080/my -X POST
Call My Page :

 curl -i http://localhost:8080/my -X GET
HTTP/1.1 405 Method Not Allowed
Date: Thu, 10 Dec 2020 06:57:42 GMT
Content-Length: 0

/my 에 대해서는 PUT, POST 만 허용하고, GET 으로 호출했을때 헤더로 HTTP/1.1 405 Method Not Allowed 로 응답되었음을 확인할 수 있다.

이처럼 http method 별로 라우팅을 수행하여 적절한 엔드포인트를 제공할 수 있음을 알게 되었다.

Subrouting 으로 엔드포인트 구분하기.

보통 rest api 는 엔드포인트 prefix 를 구분하여 서비스를 제공한다.

예를 들어 /api 라는 prefix 를 붙여서 api 임을 알려주고, 특정 요구사항에 따라서 엔드포인트를 다양하게 분리할 수 있다.

예시:

  • /api/users POST, PUT
    • /api/users/{id} GET, DELETE
    • /api/users/{id}/summary GET
  • /api/subjects POST, PUT
    • /api/subjects/{id} GET, DELETE

위와 같다고 하면 다음과 같이 서브 경로를 지정해 줄 수 있다.

main.go 파일을 아래와 같이 수정해보자.

package main

import (
	"fmt"
	"html"
	"log"
	"net/http"

	"github.com/gorilla/mux"
)

func main() {

	r := mux.NewRouter()
	r.HandleFunc("/", HelloWorld).Methods("GET")
	r.HandleFunc("/path/value", ResponsePath).Methods("GET")
	// r.HandleFunc("/users", CreateUser).Methods("POST")
	// r.HandleFunc("/users", UpdateUser).Methods("PUT")
	// r.HandleFunc("/users", DeleteUser).Methods("DELETE")
	// r.HandleFunc("/my", CallMy).Methods("POST", "PUT")

	mainSubRouter := r.PathPrefix("/api").Subrouter()

	userSubRouter := mainSubRouter.PathPrefix("/users").Subrouter()
	userSubRouter.HandleFunc("", CreateUser).Methods("POST")
	userSubRouter.HandleFunc("/", UpdateUser).Methods("PUT")
	userSubRouter.PathPrefix("/{id}").HandlerFunc(SelectUser).Methods("GET")
	userSubRouter.PathPrefix("/{id}").HandlerFunc(DeleteUser).Methods("DELETE")

	subjectSubRouter := mainSubRouter.PathPrefix("/subjects").Subrouter()
	subjectSubRouter.PathPrefix("/").HandlerFunc(CreateSubject).Methods("POST")
	subjectSubRouter.PathPrefix("/").HandlerFunc(UpdateSubject).Methods("PUT")
	subjectSubRouter.PathPrefix("/{id}").HandlerFunc(SelectSubject).Methods("GET")
	subjectSubRouter.PathPrefix("/{id}").HandlerFunc(DeleteSubject).Methods("DELETE")

	fmt.Println("Server start on port: ", 8080)
	log.Fatal(http.ListenAndServe(":8080", r))
}

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

func ResponsePath(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Your request path is :%q", html.EscapeString(r.URL.Path))
}

func CreateUser(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "CreateUser :")
}

func SelectUser(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "SelectUser :")
}

func UpdateUser(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "UpdateUser :")
}

func DeleteUser(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "DeleteUser :")
}

func CallMy(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Call My Page :")
}

func CreateSubject(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "CreateSubject :")
}

func UpdateSubject(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "UpdateSubject :")
}

func SelectSubject(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "SelectSubject :")
}

func DeleteSubject(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "DeleteSubject :")
}

변경이 발생된 부분은 아래와 같다.

... 생략
    mainSubRouter := r.PathPrefix("/api").Subrouter()

	userSubRouter := mainSubRouter.PathPrefix("/users").Subrouter()
    userSubRouter.HandleFunc("", CreateUser).Methods("POST")
	userSubRouter.HandleFunc("/", UpdateUser).Methods("PUT")
... 생략
  • r.PathPrefix(“/api”).Subrouter(): Subrouter 를 생성하고, prefix 하위에 필요한 라우팅을 묶어줄 수 있다.
  • userSubRouter.HandleFunc(“”, CreateUser).Methods(“POST”): 부분은 prefix 를 ““로 두었고 핸들 함수를 정의하고 있다.
  • userSubRouter.HandleFunc(“/”, UpdateUser).Methods(“PUT”): 부분은 위 내역과 동일하며, prefix 를 “/”로 두었다.

테스트 수행하기

 curl http://localhost:8080/api/users/1
SelectUser :

 curl http://localhost:8080/api/users/ -X POST
CreateUser :

  curl http://localhost:8080/api/users/ -X PUT
404 page not found

 curl http://localhost:8080/api/subjects/1
SelectSubject :

 curl http://localhost:8080/api/subjects/ -X POST
CreateSubject :

보는 바와 같이 curl http://localhost:8080/api/users/ -X PUT 로 처리되면 404 page not found 가 되었음을 알 수 있다.

Wrap Up

지금까지 라우팅에 대한 기본적인 사항에 대해서 알아 보았다.

추가적인 필터링과 호스트 필터링 등에 대한 내역은 Document 문서를 참고하면 좋을듯 하다.

관련 Git 에서 소스코드를 확인 가능