2티어 아키텍처의 구조로 간단한 ToDo리스트 웹을 만들어 보고자 합니다.
참고사항은 책을 참고하여 작성을 하였고 천천히 작업을 해가면서 공부를 하고자 합니다.
후에는 3티어 아케틱처로 자주 사용은 하지는 않았지만 공부도 할겸 mysql과 결합하여 작업을 해보고자 합니다.
프론트 작업에 대해서는 따로 공부하지 않아도 알고 있는 부분이 많기 떄문에 서버에 대해서만 다루었으며 해당 내용은 전에 있는 내용과 동일합니다.
저번에 이어 추가적으로 도움을 줄수 있는 외부 패키지를 불러옴으로써 준비를 하겠습니다.
go get github.com/urfave/negroni
go get github.com/unrolled/render
API에 대한 정의를 내리자면
GET - /todos : 전체 할 일 목록을 반환
POST - /todos : 새로 할 일을 등록
PUT - /todos/id : id에 해당하는 할 일 업데이트
DELETE - /todos/id : id에 해당하는 일 삭제
import (
"encoding/json"
"log"
"net/http"
"sort"
"strconv"
"github.com/gorilla/mux"
"github.com/unrolled/render"
"github.com/urfave/negroni"
)
우리가 사용해야할 총 패키지 목록입니다.
func MakeWebHandler() http.Handler{
todoMap = make(map[int]Todo)
mux := mux.NewRouter()
mux.Handle("/", http.FileServer(http.Dir("public")))
mux.HandleFunc("/todos", GetTotalList).Method("GET")
mux.HandleFunc("/todos", MakeTodo).Method("POST")
mux.HandleFunc("/todos/{id:[0-9]+", UpdataTodo).Method("PUT")
mux.HandleFunc("/todos/{id:[0-9]+", DeleteTodo).Method("DELETE")
return mux
}
type Todos []Todo
func (s Todos) Len() int {
return len(s)
}
func (s Todos) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s Todos) Less(i, j int) bool {
return s[i].ID < s[j].ID
}
이전 시간과 마찬가지로 핸들러를 작성해 주면 됩니다.
GetTotalList
func GetTotalList(w http.ResponseWriter, r *http.Request){
result := make(Todos, 0)
for _, data := range todoMap{
result = append(result, data)
}
sort.Sort(result)
// w.WriteHeader(http.StatusOK)
// json.NewEncoder(w).Encode(result)
rd.JSON(w,http.StatusOK, result)
}
이전과 같이 간단하게 작성이 가능합니다.
for문을 통해서 todoMap
에 있는 데이터를 가져와야 하기 떄문에 담아줄 공간이 필요하고
해당 공간은 앞서 선언한 Todos
타입을 이용하여 새로운 공간을 만들어주고
해당 공간에 넣어줌으로써 작동을 합니다.
그후 외부 패키지 모듈을 통해서 JSON으로 응답을 해주게 됩니다.
w.WriteHeader(http.StatusOK)
,json.NewEncoder(w).Encode(result)
이런식으로 추가해 주어야 합니다.MakeTodo
func MakeTodo(w http.ResponseWriter, r *http.Request){
var todo Todo
err := json.NewDecoder(r.Body).Decode(&todo)
if err != nil{
log.Fatal(err)
w.WriteHeader(http.StatusBadRequest)
return
}
index++
todo.ID = index
todoMap[index] = todo
rd.JSON(w,http.StatusCreated, todo)
}
이또한 이전과 동일합니다.
간단하게 Decode
해줌으로써 해당 Body를 통해 들어오는 데이터를 가져올수 있고
에러처리와 데이터를 추가해 줌으로써 종료됩니다.
UpdataTodo
func UpdataTodo(w http.ResponseWriter, r *http.Request) {
var data Todo
err := json.NewDecoder(r.Body).Decode(&data)
if err != nil {
log.Fatal(err)
rd.JSON(w, http.StatusBadRequest, false)
}
vars := mux.Vars(r)
id, _ := strconv.Atoi(vars["id"])
if todo, ok := todoMap[id]; ok {
todo.Name = data.Name
todo.Completed = data.Completed
rd.JSON(w, http.StatusOK, true)
} else {
rd.JSON(w, http.StatusBadRequest, false)
}
}
업데이트 같은경우에는 기존에 있던 데이터를 찾고 해당 데이터를 새로 들어오는 데이터로 바꿔 줘야 하기 떄문에 이렇게 작성을 하게 됩니다.
DeleteTodo
vars := mux.Vars(r)
id,_ := strconv.Atoi(vars["id"])
if _, ok := todoMap[id]; ok{
delete(todoMap,id)
rd.JSON(w, http.StatusOK, true)
}else{
rd.JSON(w, http.StatusNotFound, false)
}
이 또한 url에 들어오는 값을 가지고 데이터를 삭제하게 됩니다.
package main
import (
"encoding/json"
"log"
"net/http"
"sort"
"strconv"
"github.com/gorilla/mux"
"github.com/unrolled/render"
"github.com/urfave/negroni"
)
var rd *render.Render
type Todo struct {
ID int
Name string
Completed bool
}
var todoMap map[int]Todo
var index int = 0
func MakeWebHandler() http.Handler {
todoMap = make(map[int]Todo)
mux := mux.NewRouter()
mux.Handle("/", http.FileServer(http.Dir("public")))
mux.HandleFunc("/todos", GetTotalList).Methods("GET")
mux.HandleFunc("/todos", MakeTodo).Methods("POST")
mux.HandleFunc("/todos/{id:[0-9]+", UpdataTodo).Methods("PUT")
mux.HandleFunc("/todos/{id:[0-9]+", DeleteTodo).Methods("DELETE")
return mux
}
type Todos []Todo
func (s Todos) Len() int {
return len(s)
}
func (s Todos) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s Todos) Less(i, j int) bool {
return s[i].ID < s[j].ID
}
func GetTotalList(w http.ResponseWriter, r *http.Request) {
result := make(Todos, 0)
for _, data := range todoMap {
result = append(result, data)
}
sort.Sort(result)
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(result)
rd.JSON(w, http.StatusOK, result)
}
func MakeTodo(w http.ResponseWriter, r *http.Request) {
var todo Todo
err := json.NewDecoder(r.Body).Decode(&todo)
if err != nil {
log.Fatal(err)
w.WriteHeader(http.StatusBadRequest)
return
}
index++
todo.ID = index
todoMap[index] = todo
rd.JSON(w, http.StatusCreated, todo)
}
func UpdataTodo(w http.ResponseWriter, r *http.Request) {
var data Todo
err := json.NewDecoder(r.Body).Decode(&data)
if err != nil {
log.Fatal(err)
rd.JSON(w, http.StatusBadRequest, false)
}
vars := mux.Vars(r)
id, _ := strconv.Atoi(vars["id"])
if todo, ok := todoMap[id]; ok {
todo.Name = data.Name
todo.Completed = data.Completed
rd.JSON(w, http.StatusOK, true)
} else {
rd.JSON(w, http.StatusBadRequest, false)
}
}
func DeleteTodo(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id, _ := strconv.Atoi(vars["id"])
if _, ok := todoMap[id]; ok {
delete(todoMap, id)
rd.JSON(w, http.StatusOK, true)
} else {
rd.JSON(w, http.StatusNotFound, false)
}
}
func main() {
rd = render.New()
m := MakeWebHandler()
n := negroni.Classic()
n.UseHandler(m)
log.Println("서버 시작!")
err := http.ListenAndServe(":3000", n)
if err != nil {
panic(err)
}
}