티스토리 뷰

Route using module

REST API 를 만들면 각 서비스 도메인에 따라 라우팅도 다양하게 일어나야한다.

이럴때 이전 예제와 같이 라우팅을 하나의 go 파일에 모두 작성하면 파일이 비대해지고, 관리도 쉽지 않게 될 것이 자명하다.

이럴때 Route를 수행하는 모듈을 만들고 코드를 분리하면 관리도 편하고, 필요에 따라 코드 작성도 쉽게 수행할 수 있는 이점이 있다.

Route 모듈 작성하기.

지금은 패키지를 사용하지 않고 동일한 패키지 내에서 라우팅 모듈을 작성할 것이다.

route_module.go 파일을 하나 만들고 다음과 같이 모듈을 작성하자.

package main

import (
	"net/http"

	"github.com/gorilla/mux"
)

// 핸들러 구조체를 작성한다.
// 핸들러의 기본 내용은 경로, 핸들러함수, 메소드 이므로 3가지를 작성했다.
type handler struct {
	path    string
	fun     http.HandlerFunc
	methods []string
}

// 핸들러 구조체 목록을 받아서 핸들러를 등록한다.
func makeHandlers(hs []handler, r *mux.Router, prefix string) {
	apiRouter := r.PathPrefix(prefix).Subrouter()

	for _, h := range hs {
		if len(h.methods) == 0 {
			apiRouter.PathPrefix(h.path).HandlerFunc(h.fun)
		} else {
			apiRouter.PathPrefix(h.path).HandlerFunc(h.fun).Methods(h.methods...)
		}
	}
}

위 내용을 보면 우선 구조체를 생성했다.

이전 코드에서 알 수 있듯이 라우팅을 위한 구성 요소는 path, handler함수, 메소드 가 필요하므로 위와 같이 구조체를 정하였다.

이후 makeHandlers 는 핸들러 목록을 받고, 라우터, 프리픽스를 입력 받아서 라우팅을 생성하고 있다.

내용을 보면 알듯이 핸들러 슬라이스를 순회하면서 라우팅에 등록해주는 작업을 수행한다.

메소드가 존재하지 않는경우 단지 HandlerFunc 만 등록하며, 메소드가 존재하는 경우 메소드를 위와 같이 등록해 준다.

Router 등록을 위한 api 모듈 작성하기

이제 필요한 작업은 api 를 모듈로 분리하는 것이다.

이전 예제에서 확인한 것과 같이 우리는 users와 subjects 라는 엔드포인트를 이용하여 REST API 를 작성했었다.

  • /api/users: api_users.go 파일을 만들고, users 에 대한 서비스 엔드포인트를 작성한다.
  • /api/subjects: api_subjects.go 파일을 만들고, subjects 에 대한 서비스 엔드포인트를 작성한다.

이제 main.go 를 분리하여 api_users.go, api_subjects.go 파일을 작성해보자.

api_users.go 파일 작성하기.

  • /api/users: POST 로 사용자 등록을 수행한다.
  • /api/users: GET 으로 사용자 전체 목록을 조회한다.
  • /api/users/{id}: GET으로 특정 사용자 아이디 내역을 반환한다.
  • /api/users: PUT 으로 사용자 정보를 수정한다.
  • /api/users/{id}: DELETE 으로 사용자 id를 이용하여 사용자를 삭제한다.

위와 같이 사용자에 대한 api 엔드포인트를 정의했다. 이제 코드를 생성해 보자.

api_users.go

package main

import (
	"fmt"
	"net/http"
)

func usersHandlers() []handler {
	return []handler{
		{path: "/users/{id}", fun: UserByID, methods: []string{"GET"}},
		{path: "/users/{id}", fun: DeleteUserByID, methods: []string{"DELETE"}},
		{path: "/users", fun: CreateUser, methods: []string{"POST"}},
		{path: "/users", fun: AllUsers, methods: []string{"GET"}},
		{path: "/users", fun: UpdateUser, methods: []string{"PUT"}},
	}
}

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

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

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

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

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

위 코드를 보면 2가지 부분으로 나뉘어 져 있다.

  1. 핸들러 목록생성하는 부분
func usersHandlers() []handler {
	return []handler{
		{path: "/users/{id}", fun: UserByID, methods: []string{"GET"}},
		{path: "/users/{id}", fun: DeleteUserByID, methods: []string{"DELETE"}},
		{path: "/users", fun: CreateUser, methods: []string{"POST"}},
		{path: "/users", fun: AllUsers, methods: []string{"GET"}},
		{path: "/users", fun: UpdateUser, methods: []string{"PUT"}},
	}
}

우리가 정의한 핸들러를 기술하고 있다.

주의: 핸들러는 위에서 아래로 먼저 매칭되는 핸들러로 요청을 보내기 때문에 /users 보다 먼저 /users/{id} 를 기술했음을 주위해야한다.

  1. 핸들러 함수를 기술하는 부분

핸들러 메소드 지정부분은 이전 코드와 동일하다. 핸들러에 대해서 fun 에 매핑되는 함수를 만들며 만들어 주면 된다.

api_subjects.go 파일 작성하기.

  • /api/subjects: POST 로 subjects 등록을 수행한다.
  • /api/subjects: GET 으로 subjects 전체 목록을 조회한다.
  • /api/subjects/{id}: GET으로 특정 subjects 아이디 내역을 반환한다.
  • /api/subjects: PUT 으로 subjects 정보를 수정한다.
  • /api/subjects/{id}: DELETE 으로 subjects id를 이용하여 subjects를 삭제한다.

위와 같이 subjects 에 대한 api 엔드포인트를 정의했다. 이제 코드를 생성해 보자.

코드의 형식은 api_users.go 와 동일하다.

api_subjects.go

package main

import (
	"fmt"
	"net/http"
)

func subjectsHandlers() []handler {
	return []handler{
		{path: "/subjects/{id}", fun: SubjectByID, methods: []string{"GET"}},
		{path: "/subjects/{id}", fun: DeleteSubjectByID, methods: []string{"DELETE"}},
		{path: "/subjects", fun: CreateSubjects, methods: []string{"POST"}},
		{path: "/subjects", fun: AllSubjects, methods: []string{"GET"}},
		{path: "/subjects", fun: UpdateSubjects, methods: []string{"PUT"}},
	}
}

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

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

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

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

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

main.go 수정하기.

이제는 핸들러 등록 모듈과, 각 api 핸들러 모듈을 만들었으므로 하나로 조합해 보자.

main.go 메소드의 내용을 모두 지우고 다음과 같이 변경하자.

package main

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

	"github.com/gorilla/mux"
)

func main() {

	var handlers []handler
	handlers = append(handlers, usersHandlers()...)
	handlers = append(handlers, subjectsHandlers()...)

	router := mux.NewRouter()
	makeHandlers(handlers, router, "/api")

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

  • handlers = append(handlers, usersHandlers()…): 슬라이스에 새로운 핸들러 슬라이스를 append 하고 있다. 이렇게 우리가 만든 핸들러를 반복해서 등록해주면 된다.
  • makeHandlers(handlers, router, “/api”): prefix는 /api 이고 핸들러 목록과 router 전달해서 subrouter 를 등록했다.

실해하기.

지금까지 작성이 완료 되었으면 이제 실행해보자.

테스트를 go run … 을 수행하기 우해서는 모듈 명을 모두 기술해 주어야한다.

✗ go run main.go route_module.go api_subjects.go api_users.go
Server start on port:  8080

테스트 하기.

✗ curl http://localhost:8080/api/users/1
UserByID                                                                                   
✗ curl http://localhost:8080/api/users/ -X POST
CreateUser                                                                              
✗ curl http://localhost:8080/api/subjects/1
SubjectByID                                                                          
✗ curl http://localhost:8080/api/subjects/ -X POST
CreateSubjects

기대했던 결과를 확인 할 수 있다.

Wrap Up

모듈을 분리하여 REST Api 의 용도에 따라 분리된 파일로 작성했다.

이렇게 함으로써 소스도 깔끔하게 관리 되었고, 모듈도 동일한 일들을 수행하는 기능으로 묶어져서 전번적으로 Coheision 향상시켜 보았다.

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

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함