Go로 만드는 웹3 - RESTful API

솔다·2023년 1월 30일
0

Go로 만드는 웹 3번째 - RESTful API 구성하기

RESTful API가 무엇인지부터 명확하게 알고 넘어가야 한다. 포스팅의 가장 아래에 참조한 링크를 남겨두었다.

REST란 "Representational State Transfer"의 약자이다. 어떤 자원에 대한 CRUD(create, read, update, delete) 조작을 하는데, 이를 URL에 정확히는 URI에 표기하는 것이다.

어떤 Method로 어떤 URI로 보내면 어떤 요청이 되는건지 표시해주는 것.

하나의 명제로 정리하면,

REST 란?
HTTP URI(Uniform Resource Identifier)를 통해 자원(Resource)을 명시하고, HTTP Method(POST, GET, PUT, DELETE)를 통해 해당 자원에 대한 CRUD Operation을 적용하는 것을 의미한다.

  • 자원이란, 해당 소프트웨어가 관리하는 거의 모든 것을 말한다.
  • HTTP Method를 통해서 Resource를 처리하도록 설계된 아키텍쳐를 의미한다.

Rest API란?

API(Application Programming Interface)란 데이터와 기능의 집합을 제공하여서, 컴퓨터 프로그램간 상호작용하여 정보를 교환 가능하도록 하는 것을 말한다.

REST API란?
REST 기반으로 서비스 API를 구현한 것

이를 활용하면 얻을 수 있는 장점은 다음과 같다.

  • 확장성과 재사용성이 높아지고, 유지보수 및 운용을 편하게 할 수 있다.
  • HTTP 표준을 기반으로 구현하므로, HTTP를 지원하는 프로그램 언어로 서버와 클라이언트를 구현할 수 있다.

RESTful API 실제로 구현하기

RESTfh 서버를 구현하기 위해서 기초 서버를 구현했다.

main.go는 다음과 같이 구현했고,

package main

import (
	"net/http"
	"github.com/hoondal6970/Go_server_prac/web3/myapp"
)

func main() {
	http.ListenAndServe(":3000", myapp.NewHandler())
}

myapp으로 함수들을 패키지화해서 구성했으므로, app.go가 사실 본체이다.

아래의 코드를 보자.

package myapp

import (
	"fmt"
	"net/http"
	"github.com/gorilla/mux"
)

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

func usersHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "Get UserInfo by /users/{id}")
}

func getUserInfoHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	fmt.Fprint(w, "User ID:", vars["id"])
}

// NewHandler make a new myapp handler
func NewHandler() http.Handler {
	mux := mux.NewRouter()

	mux.HandleFunc("/", indexHandler)
	mux.HandleFunc("/users", usersHandler) 
	mux.HandleFunc("/users/{id:[0-9]+}", getUserInfoHandler)
	return mux
}

일단 시작에 앞서서 import를 보면 알 수 있듯이, gorilla/mux 패키지를 다운 받아서 사용했다.
URI에 오는 요청에서 파싱을 통해 요청을 처리해야 하는데 이를 직접 해주기 위해서는 우리가 request로 받은 것에서 r.URL.path로 직접 path에서 파싱해주는 방법도 가능하지만, 패키지를 사용하면 편하게 할 수 있다.

해당 코드는 mux 패키지의 mux.Vars(r)에 넣어주면 리턴값으로 파싱된 결과가 나온다. 이를 활용해서 w(response writer에 적어주면 된다.)

Test 코드 작성

package myapp

func TestGetUserInfo(t *testing.T) {
	assert := assert.New(t)

	ts := httptest.NewServer(NewHandler()) 
	defer ts.Close()

	resp, err := http.Get(ts.URL + "/users/89")
	assert.NoError(err)
	assert.Equal(http.StatusOK, resp.StatusCode)
	data, _ := ioutil.ReadAll(resp.Body)
	assert.Contains(string(data), "User ID:89")
}

import 부분은 생략했다. 이제 익숙해져가니까 assert를 사용한 테스트 방법을 활용한 것은 굳이 서술을 생략한다.

Test를 위한 서버를 생성해준다. httptest.NewServer()에 인자로 NewHandler()를 넣어주면, 자동으로 라우터를 생성해서 인자로 넣어주고, http.Get()으로 test를 위한 case를 넣어준다.

response의 body에 있는데이터에 우리가 넣어준 데이터가 제대로 파싱되어 있는지 확인해준다. assert에서 확인하기 위해서는 data를 string으로 바꾸어서 넣어줘야 하고, Equal()대신, Contains()를 이용해서 확인한다.

이 상태로 POST 요청을 보내는 새로운 Test를 실행하면 어떻게 될까

func TestCreateUser(t *testing.T) {
	assert := assert.New(t)

	ts := httptest.NewServer(NewHandler())
	defer ts.Close()

	resp, err := http.Post(ts.URL+"/users", "application/json",
		strings.NewReader(`{"first_name":"tucker", "last_name":"kim", "email":"tucker@naver.com"}`))
	assert.NoError(err)
	assert.Equal(http.StatusCreated, resp.StatusCode)

}

이 Test를 보면, Post 요청으로 정보를 보냈다. 그런데, 이 Test는 실패하게 된다. 그 이유는 위의 app.go를 보면, 요청에 따라서 다른 함수가 호출되어아 햐는데, 현재는 그런 기능이 없기 때문에 GET요청으로 간것과 똑같이 실행된 것이다.

이런 문제를 해결 하기 위해서 Method()함수의 인자에 어떤 요청일때 실행할 것인지 명시해주면 요청에 맞는 함수를 실행시킬 수 있다.

(참조: RESTful API란?)
(참조: Tucker의 GO로 만드는 Web 강의)

0개의 댓글