Go로 만드는 웹4 - RESTful API

솔다·2023년 1월 30일
0

오늘은 지난 포스팅에 이어서 RESTful 로 구성하는 것을 마무리 해볼 예정이다.

이제 남은 기능은 Delete랑, 전체 list를 가져오는 거, 데이터 수정(update)하는 기능만 구현하면 끝난다.

func deleteUserHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id, err := strconv.Atoi(vars["id"])
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, err)
		return
	}

	_, ok := userMap[id]
	if !ok {
		w.WriteHeader(http.StatusOK)
		fmt.Fprint(w, "No User ID:", id)
		return
	}

	delete(userMap, id)
	w.WriteHeader(http.StatusOK)
	fmt.Fprint(w, "Deleted User ID:", id)
}

지난번에 작성하던 myapp.go에서 추가된 func이다. deleteUserHander()함수는 그 이름처럼, 유저 정보를 Map에서 삭제하는 기능을 하는 함수이다.

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

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

	req, _ := http.NewRequest("PUT", ts.URL+"/users",
		strings.NewReader(`{"id":1, "first_name":"updated", "last_name":"updated", "email":"updated@naver.com"}`))
	resp, err := http.DefaultClient.Do(req)
	assert.NoError(err)
	assert.Equal(http.StatusOK, resp.StatusCode)
	data, _ := ioutil.ReadAll(resp.Body)
	assert.Contains(string(data), "No User ID:1")

	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)

	user := new(User)
	err = json.NewDecoder(resp.Body).Decode(user)
	assert.NoError(err)
	assert.NotEqual(0, user.ID)

	updateStr := fmt.Sprintf(`{"id":%d, "first_name":"jason"}`, user.ID)

	req, _ = http.NewRequest("PUT", ts.URL+"/users",
		strings.NewReader(updateStr))
	resp, err = http.DefaultClient.Do(req)
	assert.NoError(err)
	assert.Equal(http.StatusOK, resp.StatusCode)

	updateUser := new(User)
	err = json.NewDecoder(resp.Body).Decode(updateUser)
	assert.NoError(err)
	assert.Equal(updateUser.ID, user.ID)
	assert.Equal("jason", updateUser.FirstName)
	assert.Equal(user.LastName, updateUser.LastName)
	assert.Equal(user.Email, updateUser.Email)
}

삭제기능 테스트라서 테스트 코드가 매우 길지만, 실질적으로 추가된 기능은 딱히 없다. golang에서 제공하는 http package에서는 Delete요청이 없기 때문에 새로 만들어줘야 한다.

http.NewRequest()로 요청을 만들어주면된다. PUT method를 테스트 할때는 새로운 데이터 업데이트를 위해서 NewRequest() 함수의 세번째 인자로 Body를 직접 작성해서 넣어줘야 한다.

그리고 NewRequest()로 method를 생성해준 뒤에는, 실행을 위해서 http패키지의 http.DefaultClient.Do()함수로 실행해줄 수 있으며, NewRequest() 함수로 return된 requst를 인자로 넣으면 된다.

그리고 새로 업데이트 된 데이터를 확인해보면 문제가 있음을 확인할 수 있는데, 업데이트를 원하지 않았던 데이터들이 ""으로 바뀌었음을 확인할 수 있다.

이런 문제를 해결 하기 위해서 app.go 에서 updateUserHandler 함수에 아래와 같은 구문을 추가해서 공백인 상태로 오게되면 이전의 데이터를 상속하는 방식으로 수정했다.

func updateUserHandler(w http.ResponseWriter, r *http.Request) {
	updateUser := new(User)
	err := json.NewDecoder(r.Body).Decode(updateUser)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, err)
		return
	}

	user, ok := userMap[updateUser.ID]

	if !ok {
		w.WriteHeader(http.StatusOK)
		fmt.Fprint(w, "No User ID:", updateUser.ID)
		return
	}

	if updateUser.FirstName != "" {
		user.FirstName = updateUser.FirstName
	}
	if updateUser.LastName != "" {
		user.LastName = updateUser.LastName
	}
	if updateUser.Email != "" {
		user.Email = updateUser.Email
	}

	userMap[updateUser.ID] = updateUser
	w.Header().Add("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	data, _ := json.Marshal(updateUser)
	fmt.Fprint(w, string(data))
}

이렇게 작성해도 문제점이 발생하는데, 이렇게 작성된 코드는 우리가 정보중 일부를 지우고 싶을때, 그 데이터가 비어있는 상태로 업데이트를 하고 싶은건지, 아니면 이전의 데이터를 상속받아야 하는지 알 수 없다.

그래서 현업에는 보통 업데이트에 사용하는 구조체를 별도로 만들고 bool 형 자료를 추가해 flag를 만들어서 이런 문제를 해결한다.

GET 요청으로 user 리스트 받기

func usersHandler(w http.ResponseWriter, r *http.Request) {
	if len(userMap) == 0 {
		w.WriteHeader(http.StatusOK)
		fmt.Fprint(w, "No Users")
	}
	users := []*User{}
	for _, u := range userMap {
		users = append(users, u)
	}

	data, _ := json.Marshal(users)
	w.Header().Add("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	fmt.Fprint(w, string(data))
}
func TestUsersWithUserData(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)

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

	resp, err = http.Get(ts.URL + "/users")
	assert.NoError(err)
	assert.Equal(http.StatusOK, resp.StatusCode)

	users := []*User{}
	err = json.NewDecoder(resp.Body).Decode(&users)
	assert.NoError(err)
	assert.Equal(2, len(users))
}

(출처: Go 로 만드는 웹-Tucker parogramming)

0개의 댓글