[Go] Go Web Programming Basic 06 - 응답 모듈 구성하기

Write Response Module

이번에는 공통적인 쓰기를 이위한 응답 모듈을 작성해 보자.

REST API 는 단순히 JSON 으로 응답을 보내는 경우도 많고, 단순 텍스트를 응답으로 보내는 경우가 많다.

그러나 좀더 개발을 편리하게 하고, 공통적인 응답 구조를 작성하기 위해서는 응답 모듀를 별도로 작성하는 것이 좋다.

정상 케이스 응답 모듈 작성하기.

우리는 응답 모듈을 작서하기 위해서 response.go 파일을 하나 만들고 이를 이용할 것이다.

response.go 파일을 다음과 같이 작성하자.

package main

import (
	"encoding/json"
	"net/http"
)

func responseWithJSON(w http.ResponseWriter, code int, payload interface{}) {
	response, _ := json.Marshal(payload)

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(code)
	w.Write(response)
}

위에서 보는바와 같이 우리의 응답 모드 구조는

  • 응답 헤더
  • 응답 헤더의 컨텐츠 타입 application/json 으로 응답을 수해할 것이다.
  • 응답 메시지 (메시지는 json 타입으로 마샬링(객체를 JSON 으로 변환))한다.

  • payload interface{}: payload 로 인터페이스 타입을 받는다. 즉 어떠한 데이터도 받아 들인다.
  • json.Marshal: 들어온 페이로드를 json 타입의 텍스트로 변환하는 작업을 수행한다.
  • w.Header().Set(Key, Value): 헤더 값을 세팅한다. 여기서는 Content-Type 을 application/json 으로 반환하고 있다.
  • w.WriteHeader: 헤더를 응답으로 보낸다.
  • w.Write(<응답 메시지="">): 응답 메시지를 반환한다.

적용하기.

이제 api_user.go 파일의 응답값을 다음과 같이 바꿔주자.

func GetUserSummaryByKey(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	userID := vars["id"]
	summaryKey := vars["key"]

	// fmt.Fprintf(w, "UserByID :%v, key: %v", userID, summaryKey)
	payload := map[string]string{"message": fmt.Sprintf("UserByID :%v, key: %v", userID, summaryKey)}
	responseWithJSON(w, http.StatusOK, payload)

}

결과 확인하기.

 curl http://localhost:8080/api/users/kido/summary/height
{"message":"UserByID :kido, key: height"}

보는바와 같이 응답을 JSON 형태로 응답을 보내고 있다. 나머지 부분은 header 로 전송되었기 때문에 다음과 같이 결과를 확인하자.

 curl http://localhost:8080/api/users/kido/summary/height -i
HTTP/1.1 200 OK
Content-Type: application/json
Date: Fri, 11 Dec 2020 16:36:22 GMT
Content-Length: 41

{"message":"UserByID :kido, key: height"}

응답은 200 OK 이며, Content-Type: application/json 으로 응답 컨텐츠 타입도 정상적으로 들어갔다.

오류 응답 메시지 작성하기.

이제는 오류에 대해서 응답 모듈을 작성하자.

동일하게 response.go 파일에 다음과 같이 추가한다.

func responseWithError(w http.ResponseWriter, code int, message string) {
	responseWithJSON(w, code, map[string]string{"error:": message})
}

역시 응답코드는 정상과 별반 다르지 않다. 코드와 메시지로는 JSON 값이 될 수 있도록 맵으로 작성했다.

키값은 “error” 이며 값은 들어온 문자 메시지 이다.

오류 응답 적용하기.

api_user.go 파일에 다음과 같이 응답을 만들어 보자.

func usersHandlers() []handler {
	return []handler{
		{path: "/users/{id}/summary/{key}", fun: GetUserSummaryByKey, methods: []string{"GET"}},

        // 이 부분을 아직 지원하지 않은경우라고 가정하자. 
		{path: "/users/secret", fun: NotSupportedFunction, methods: []string{"GET"}},
		{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"}},
		{path: "/users/secret", fun: NotSupportedFunction, methods: []string{"GET"}},
	}
}

func NotSupportedFunction(w http.ResponseWriter, r *http.Request) {
	responseWithError(w, http.StatusNotImplemented, "Your request not implemented yet.")
}

위와 같이 /users/secret 에 대해서 GET 으로 요청이 들어온경우 NotSupportedFunction 이 동작하도록 작성했다.

NotSupportedFunction 에는 응답 코드로 http.StatusNoImplemented 를 반환한다.

결과 확인하기.

 curl http://localhost:8080/api/users/secret -i
HTTP/1.1 501 Not Implemented
Content-Type: application/json
Date: Fri, 11 Dec 2020 16:43:57 GMT
Content-Length: 46

{"error:":"Your request not implemented yet."}

위와 같이 응답이 501로 내려가고, 에러 메시지가 JSON 으로 반환되었음을 알 수 있다.

Wrap up

이제 응답을 위한 구조를 만들었으므로 보다더 REST API 답게 응답을 내려줄 수 있게 되었다.

응답을 구조화 하면 클라이언트 코드가 깔끔해진다. 그러므로 응답 구조를 잘 구성하여 활용하도록 하자.

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