Golang - ToDo 리스트 웹 만들기 [서버]

Lumi·2022년 2월 19일
0

Golang

목록 보기
30/38
post-thumbnail

🔥 개요

2티어 아키텍처의 구조로 간단한 ToDo리스트 웹을 만들어 보고자 합니다.

참고사항은 책을 참고하여 작성을 하였고 천천히 작업을 해가면서 공부를 하고자 합니다.

후에는 3티어 아케틱처로 자주 사용은 하지는 않았지만 공부도 할겸 mysql과 결합하여 작업을 해보고자 합니다.

  • 이 부분은 언제 할지는 모르겠지만 그럴 계획입니다.

프론트 작업에 대해서는 따로 공부하지 않아도 알고 있는 부분이 많기 떄문에 서버에 대해서만 다루었으며 해당 내용은 전에 있는 내용과 동일합니다.

  • 복습한다는 의미로 다루어 보았습니다.

🔨 준비

저번에 이어 추가적으로 도움을 줄수 있는 외부 패키지를 불러옴으로써 준비를 하겠습니다.

go get github.com/urfave/negroni

  • 로그기능, panic복구 기능, 파일 서버 기능 등을 지원해주는 패키지 입니다.

go get github.com/unrolled/render

  • 웹 서버 응답을 구현하는 데 사용하는 패키지 입니다.
  • 주로 JSON 포맷으로 변환하여 응답하는데에 사용이 됩니다.

API에 대한 정의를 내리자면

GET - /todos : 전체 할 일 목록을 반환
POST - /todos : 새로 할 일을 등록
PUT - /todos/id : id에 해당하는 할 일 업데이트
DELETE - /todos/id : id에 해당하는 일 삭제

🔨 import 항목


import (
	"encoding/json"
	"log"
	"net/http"
	"sort"
	"strconv"

	"github.com/gorilla/mux"
	"github.com/unrolled/render"
	"github.com/urfave/negroni"
)

우리가 사용해야할 총 패키지 목록입니다.

  • 위에있는 내용이 이해가 안된다면 해당 패키지를 모두 불러오면 됩니다.

🔨 핸들러, Sort

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
}

이전 시간과 마찬가지로 핸들러를 작성해 주면 됩니다.

🔨 API에 해당하는 func

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)
	}
}
  • 이 부분은 위에적힌 코드를 합친 모양이며 중간 오타가 있어서 살짝 수정을 하였습니다.
  • 오타 부분은 go run 파일명.go 를 통해서 실행시켰을떄 발생하는 오류를 보고 수정하시면 됩니다.
profile
[기술 블로그가 아닌 하루하루 기록용 블로그]

0개의 댓글