패키지 설치 위치 - 본인의 프로젝트 폴더에서 실행.
패키지 설치 방법
1. go get -u github.com/gorilla/mux
D:\workspace\GO\tuckersGo\goWeb\web05>go get -u github.com/gorilla/mux
go: downloading github.com/gorilla/mux v1.8.0
go get: added github.com/gorilla/mux v1.8.0
환경 설정은 아래와 같이 설정함.
PS D:\workspace\GO\tuckersGo\goWeb> cd .\web05\
PS D:\workspace\GO\tuckersGo\goWeb\web05> go mod init GO/tuckersGo/goWeb/web05
go: creating new go.mod: module GO/tuckersGo/goWeb/web05
go: to add module requirements and sums:
go mod tidy
PS D:\workspace\GO\tuckersGo\goWeb\web05> go mod tidy
PS D:\workspace\GO\tuckersGo\goWeb\web05> code .
//go.mod
module GO/tuckersGo/goWeb/web05
go 1.17
app_test.go
package myapp
import "testing"
func TestIndex(t *testing.T) {
}
// goconvey 실행하고 TDD 방식으로 개발 진행
PS D:\workspace\GO\tuckersGo\goWeb\web05> goconvey
2021/11/03 17:25:13 goconvey.go:116: GoConvey server:
2021/11/03 17:25:13 goconvey.go:121: version: v1.7.2
2021/11/03 17:25:13 goconvey.go:122: host: 127.0.0.1
2021/11/03 17:25:13 goconvey.go:123: port: 8080
2021/11/03 17:25:13 goconvey.go:124: poll: 250ms
2021/11/03 17:25:13 goconvey.go:125: cover: true
2021/11/03 17:25:13 goconvey.go:126:
2021/11/03 17:25:13 tester.go:19: Now configured to test 10 packages concurrently.
2021/11/03 17:25:13 goconvey.go:243: Serving HTTP at: http://127.0.0.1:8080
2021/11/03 17:25:13 goconvey.go:146: Launching browser on 127.0.0.1:8080
2021/11/03 17:25:13 integration.go:122: File system state modified, publishing current folders... 0 4907867274
2021/11/03 17:25:13 goconvey.go:159: Received request from watcher to execute tests...
2021/11/03 17:25:13 goconvey.go:152: exec: "start": executable file not found in %PATH%
2021/11/03 17:25:13 goconvey.go:154:
2021/11/03 17:25:14 executor.go:69: Executor status: 'executing'
2021/11/03 17:25:14 coordinator.go:46: Executing concurrent tests: GO/tuckersGo/goWeb/web05
2021/11/03 17:25:14 coordinator.go:46: Executing concurrent tests: GO/tuckersGo/goWeb/web05/myapp
2021/11/03 17:25:14 shell.go:89: Coverage output: ? GO/tuckersGo/goWeb/web05 [no test files]
2021/11/03 17:25:14 shell.go:91: Run without coverage
2021/11/03 17:25:15 parser.go:24: [no test files]: GO/tuckersGo/goWeb/web05
2021/11/03 17:25:15 parser.go:24: [passed]: GO/tuckersGo/goWeb/web05/myapp
2021/11/03 17:25:15 executor.go:69: Executor status: 'idle'
여기까지 기본 스타일입니다. 이제 코딩을 시작합니다.
중간에 assert 패키지 임포트 오류가 발생하여 다시 go get 처리함.
D:\workspace\GO\tuckersGo\goWeb\web05>go get github.com/stretchr/testify/assert
go get: added github.com/davecgh/go-spew v1.1.0
go get: added github.com/pmezard/go-difflib v1.0.0
go get: added github.com/stretchr/testify v1.7.0
go get: added gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
go.mod & go.sum - 패키지를 추가하면 변경되는 사항들, 참고용입니다.
//go.mod
module GO/tuckersGo/goWeb/web05
go 1.17
require (
github.com/davecgh/go-spew v1.1.0 // indirect
github.com/gorilla/mux v1.8.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/testify v1.7.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
)
//go.sum
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
./main.go
package main
import (
"GO/tuckersGo/goWeb/web05/myapp"
"net/http"
)
const portNumber = ":3000"
func main() {
mux := myapp.NewHttpHandler()
http.ListenAndServe(portNumber, mux)
}
./myapp/app.go
// 핸들러를 등록 관리하는 파일
package myapp
import (
"encoding/json"
"fmt"
"log"
"net/http"
"strconv"
"time"
"github.com/gorilla/mux"
)
type User struct {
Id int `json:"id"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Email string `json:"email"`
CreatedAt time.Time `json:"created_at"`
}
var userMap map[int]*User
var lastId int
func indexHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello World")
}
func usersHandler(w http.ResponseWriter, r *http.Request) {
if len(userMap) == 0 {
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, "No Users")
return
}
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 createUserHandler(w http.ResponseWriter, r *http.Request) {
user := new(User)
err := json.NewDecoder(r.Body).Decode(user)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprint(w, err)
return
}
// created User
lastId += 1
user.Id = lastId
user.CreatedAt = time.Now()
userMap[user.Id] = user
log.Println("createUserHandler:", *userMap[user.Id])
w.Header().Add("content-type", "application/json")
w.WriteHeader(http.StatusCreated)
data, _ := json.Marshal(user)
fmt.Fprint(w, string(data))
}
func getUserInfoHandler(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
}
user, ok := userMap[id]
if !ok {
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, "No User ID:", id)
log.Println(userMap[id])
return
}
w.Header().Add("content-type", "application/json")
w.WriteHeader(http.StatusOK)
data, _ := json.Marshal(user)
fmt.Fprint(w, string(data))
}
// delete a user
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)
log.Println("user.Id:", id)
return
}
delete(userMap, id)
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, "Deleted User ID:", id)
}
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)
log.Println("http.StatusBadRequest:", 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] = user
w.Header().Add("content-type", "application/json")
w.WriteHeader(http.StatusOK)
data, _ := json.Marshal(user)
fmt.Fprint(w, string(data))
}
// making a new my handler
func NewHttpHandler() http.Handler {
userMap = make(map[int]*User)
lastId = 0
// 인스턴스를 만들고 해당 인스턴스에 등록해서 사용하는 예제 코드.
mux := mux.NewRouter() // gorilla mux 사용법
// mux := http.NewServeMux() //gorilla mux 로 대체
mux.HandleFunc("/", indexHandler)
mux.HandleFunc("/users", usersHandler).Methods("GET")
mux.HandleFunc("/users", createUserHandler).Methods("POST")
mux.HandleFunc("/users", updateUserHandler).Methods("PUT")
mux.HandleFunc("/users/{id:[0-9]+}", getUserInfoHandler).Methods("GET")
mux.HandleFunc("/users/{id:[0-9]+}", deleteUserHandler).Methods("DELETE")
return mux
}
./myapp/app_test.go
package myapp
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/http/httptest"
"strconv"
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
func TestIndex(t *testing.T) {
assert := assert.New(t)
ts := httptest.NewServer((NewHttpHandler()))
defer ts.Close()
resp, err := http.Get(ts.URL)
assert.NoError(err)
assert.Equal(http.StatusOK, resp.StatusCode)
data, _ := ioutil.ReadAll(resp.Body)
assert.Equal("Hello World", string(data))
}
func TestUsers(t *testing.T) {
assert := assert.New(t)
ts := httptest.NewServer((NewHttpHandler()))
defer ts.Close()
resp, err := http.Get(ts.URL + "/users")
assert.NoError(err)
assert.Equal(http.StatusOK, resp.StatusCode)
data, _ := ioutil.ReadAll(resp.Body)
assert.Equal(string(data), "No Users")
}
func TestGetUserInfo(t *testing.T) {
assert := assert.New(t)
ts := httptest.NewServer((NewHttpHandler()))
defer ts.Close()
resp, err := http.Get(ts.URL + "/users/1")
assert.NoError(err)
assert.Equal(http.StatusOK, resp.StatusCode)
data, _ := ioutil.ReadAll(resp.Body)
log.Println("TestGetUserInfo:", string(data))
assert.Contains(string(data), "No User ID:1")
}
func TestCreateUser(t *testing.T) {
assert := assert.New(t)
ts := httptest.NewServer((NewHttpHandler()))
defer ts.Close()
resp, err := http.Post(ts.URL+"/users", "application/json",
strings.NewReader(`{"first_name":"bs", "last_name":"kim", "email":"kimbs@kimbs.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)
id := user.Id
log.Println("TestCreateUser user.Id:", id)
resp, err = http.Get(ts.URL + "/users/" + strconv.Itoa(id))
assert.NoError(err)
assert.Equal(http.StatusOK, resp.StatusCode)
user2 := new(User)
err = json.NewDecoder(resp.Body).Decode(user2)
assert.NoError(err)
assert.Equal(user.Id, user2.Id)
assert.Equal(user.FirstName, user2.FirstName)
}
func TestDeleteUser(t *testing.T) {
assert := assert.New(t)
ts := httptest.NewServer((NewHttpHandler()))
defer ts.Close()
resp, err := http.Post(ts.URL+"/users", "application/json",
strings.NewReader(`{"first_name":"bs", "last_name":"kim", "email":"kimbs@kimbs.com"}`))
assert.NoError(err)
assert.Equal(http.StatusCreated, resp.StatusCode)
req, _ := http.NewRequest("DELETE", ts.URL+"/users/1", nil)
resp, err = http.DefaultClient.Do(req)
assert.NoError(err)
assert.Equal(http.StatusOK, resp.StatusCode)
data, err := ioutil.ReadAll(resp.Body)
log.Println("TestDeleteUser:", string(data))
assert.NoError(err)
assert.Equal("Deleted User ID:1", string(data))
}
func TestUpdateUser(t *testing.T) {
assert := assert.New(t)
ts := httptest.NewServer((NewHttpHandler()))
defer ts.Close()
// update wrong user
req, _ := http.NewRequest("PUT", ts.URL+"/users",
strings.NewReader(`{"id":1, "first_name":"updated", "last_name":"updated", "email":"updated@kimbs.com"}`))
resp, err := http.DefaultClient.Do(req)
assert.NoError(err)
assert.Equal(http.StatusOK, resp.StatusCode)
// no user updated
data, _ := ioutil.ReadAll(resp.Body)
assert.Contains(string(data), "No User ID:1")
log.Printf("TestUpdateUser:[%s]", string(data))
// create test user info
resp, err = http.Post(ts.URL+"/users", "application/json",
strings.NewReader(`{"first_name":"bs", "last_name":"kim", "email":"kimbs@kimbs.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)
// update the test user info from create
newUser := new(User)
newUser.Id = user.Id
newUser.FirstName = "updated"
newUser.LastName = "up"
newUser.Email = "up@up.com"
updateStr := fmt.Sprintf(`{"id":%d, "first_name":"%s", "last_name":"%s", "email":"%s"}`,
newUser.Id, newUser.FirstName, newUser.LastName, newUser.Email)
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)
// checking update result from updateUserHandler
updateUser := new(User)
err = json.NewDecoder(resp.Body).Decode(updateUser)
assert.NoError(err)
log.Printf("TestUpdateUser Id[%d] data:[%v]", updateUser.Id, *updateUser)
}
func TestUsers_withUsersData(t *testing.T) {
assert := assert.New(t)
ts := httptest.NewServer((NewHttpHandler()))
defer ts.Close()
// create test user info
resp, err := http.Post(ts.URL+"/users", "application/json",
strings.NewReader(`{"first_name":"bs", "last_name":"kim", "email":"kimbs@kimbs.com"}`))
assert.NoError(err)
assert.Equal(http.StatusCreated, resp.StatusCode)
// create test user info
resp, err = http.Post(ts.URL+"/users", "application/json",
strings.NewReader(`{"first_name":"jason", "last_name":"park", "email":"jason@kimbs.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))
for _, d := range users {
log.Print("TestUsers_withUsersData:", *d)
}
}
test result
log.Println 함수를 이용해서 서버쪽 로그 및 테스트쪽 로그를 출력
- 주의사항)
// 정상동작함, json 형식으로 보여짐
w.Header().Add("content-type", "application/json")
w.WriteHeader(http.StatusCreated)
// 정상동작 안함, 문자열처럼 일렬로 보여짐
w.WriteHeader(http.StatusCreated)
w.Header().Add("content-type", "application/json")
참고자료 [https://www.youtube.com/watch?v=0sp6KhLgNxg&list=PLy-g2fnSzUTDALoERcKDniql16SAaQYHF&index=5]
'GO lang > web' 카테고리의 다른 글
[GO] Todo list - interface 구현1 (0) | 2021.11.18 |
---|---|
[GO] Todo list - refactoring(구조변경) (0) | 2021.11.17 |
[GO] Todo list - map 자료구조 (0) | 2021.11.17 |
[GO] Web - 테스트 코드 작성방법(TDD) (0) | 2021.11.04 |
[GO] Web - Text, json, URL 쿼리 방식 (0) | 2021.11.04 |