Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- vscode
- 도커
- web ide
- 중첩가상화
- 코어 쿠버네티스
- 504
- ALB
- code-server
- curl
- ansible
- containerd
- Kind
- passwd
- kubernets
- Pane
- kubernetes
- 쿠버네티스
- network namespace
- WSL
- go
- Docker
- 컨테이너
- windows
- 워커노드
- 네트워크 네임스페이스
- 묘공단
- nested virtualization
- calico
- ubuntu
- 패스워드 재설정
Archives
- Today
- Total
a story
Go스터디: 7주차(31장) 본문
이 글은 골든래빗 ‘Tucker의 Go 언어 프로그래밍의 31장 써머리입니다.
이 책의 마지막 스토디 노트입니다.
Todo 리스트 웹 서비스 만들기
Todo 리스트 웹 서비스는 프론트 엔드 코드와 백엔드 코드로 나눠진다.
프론트 엔드는 웹서비스의 화면을 담당하고, 백엔드는 데이터와 로직을 담당한다.
구현 순서
- 먼저 RESTful API에 맞춰 서비스를 정의한다.
- Todo 구조체를 만든다.
- RESTful API에 맞춰 각 핸들러를 만든다.
- 화면을 구성하는 HTML 문서를 만든다.
- 프론트엔드 동작을 나타내는 자바스크립트 코드를 만든다.
- 웹 브라우저로 동작을 확인한다.
시작 하기 전에 웹서버를 만들기 앞서 gorilla/mux 외 두 가지 패키지를 더 설치한다.
- urfave/negroni 패키지: 자주 사용되는 웹 핸들러를 제공하는 패키지이다. 추가로 로그 기능, panic 복구 기능, 파일 서버 기능을 제공한다.
- unrolled/render 패키지: 웹 서버 응답으로 HTML, JSON, TEXT 같은 포맷을 간단히 사용할 수 있다.
$ go mod init goprojects/todo31
$ go get github.com/gorilla/mux
$ go get github.com/urfave/negroni
$ go get github.com/unrolled/render
이제 백엔드의 RESTful API를 아래와 같이 작성한다.
// ch31/ex31.1/ex31.1.go
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 { // 할 일 정보를 담는 Todo 구조체
ID int `json:"id,omitempty"` // json 포맷으로 변환 옵션 -> JSON 포맷으로 변환시 ID가 아닌 id로 변환됨
Name string `json:"name"`
Completed bool `json:"completed,omitempty"`
}
var todoMap map[int]Todo
var lastID int = 0
func MakeWebHandler() http.Handler { // 웹 서버 핸들러 생성
rd = render.New()
todoMap = make(map[int]Todo)
mux := mux.NewRouter()
mux.Handle("/", http.FileServer(http.Dir("public"))) // "/"" 경로에 요청이 들어올 때 public 아래 폴더의 파일을 제공하는 파일 서버
// "/todos" 에 대해서 GET, POST, DELETE, PUT에 대한 핸들러 구현
mux.HandleFunc("/todos", GetTodoListHandler).Methods("GET")
mux.HandleFunc("/todos", PostTodoHandler).Methods("POST")
mux.HandleFunc("/todos/{id:[0-9]+}", RemoveTodoHandler).Methods("DELETE")
mux.HandleFunc("/todos/{id:[0-9]+}", UpdateTodoHandler).Methods("PUT")
return mux
}
type Todos []Todo // ID로 정렬하는 인터페이스
func (t Todos) Len() int {
return len(t)
}
func (t Todos) Swap(i, j int) {
t[i], t[j] = t[j], t[i]
}
func (t Todos) Less(i, j int) bool {
return t[i].ID > t[j].ID
}
func GetTodoListHandler(w http.ResponseWriter, r *http.Request) {
list := make(Todos, 0)
for _, todo := range todoMap {
list = append(list, todo)
}
sort.Sort(list)
rd.JSON(w, http.StatusOK, list) // ID로 정렬하여 전체 목록 반환
}
func PostTodoHandler(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
}
lastID++ // 새로운 ID로 등록하고 만든 Todo 반환
todo.ID = lastID
todoMap[lastID] = todo
rd.JSON(w, http.StatusCreated, todo)
}
type Success struct {
Success bool `json:"success"`
}
func RemoveTodoHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r) // ID에 해당하는 할 일 삭제
id, _ := strconv.Atoi(vars["id"])
if _, ok := todoMap[id]; ok {
delete(todoMap, id)
rd.JSON(w, http.StatusOK, Success{true})
} else {
rd.JSON(w, http.StatusNotFound, Success{false})
}
}
func UpdateTodoHandler(w http.ResponseWriter, r *http.Request) {
var newTodo Todo // ID에 해당하는 할 일 수정
err := json.NewDecoder(r.Body).Decode(&newTodo)
if err != nil {
log.Fatal(err)
w.WriteHeader(http.StatusBadRequest)
return
}
vars := mux.Vars(r)
id, _ := strconv.Atoi(vars["id"])
if todo, ok := todoMap[id]; ok {
todo.Name = newTodo.Name
todo.Completed = newTodo.Completed
rd.JSON(w, http.StatusOK, Success{true})
} else {
rd.JSON(w, http.StatusBadRequest, Success{false})
}
}
func main() {
m := MakeWebHandler() // 기본 핸들러 (핸들러들이 등록된 mux가 반환됨)
n := negroni.Classic() // negroni 기본 핸들러
n.UseHandler(m) // negroni 기본 핸들러로 만든 핸들러 MakeWebHandler 을 감싼다.
// HTTP 요청 수신 시 negroni에서 제공하는 부가 기능 핸들러들을 수행하고 난 뒤, MakeWebHandler()를 수행한다.
log.Println("Started App")
err := http.ListenAndServe(":3000", n) // negroni 기본 핸들러가 동작함
if err != nil {
panic(err)
}
}
프론트 엔드는 핵심이 아니므로 github(https://github.com/tuckersGo/musthaveGo/tree/master/ch31/ex31.1/public)의 파일을 참조해서 웹서버의 위치에 /public 폴더를 만들고 넣는다.
실행해보면 아래와 같이 실행된다.
negroni 를 사용해서 로그도 그럴싸하게 남는다.
$ go run .\\main.go
2023/11/09 22:57:37 Started App
[negroni] 2023-11-09T22:57:51+09:00 | 200 | 213.3309ms | localhost:3000 | GET /
[negroni] 2023-11-09T22:57:51+09:00 | 200 | 22.9704ms | localhost:3000 | GET /todo.css
[negroni] 2023-11-09T22:57:51+09:00 | 200 | 39.5097ms | localhost:3000 | GET /todo.js
[negroni] 2023-11-09T22:57:51+09:00 | 200 | 976.8µs | localhost:3000 | GET /todos
[negroni] 2023-11-09T22:57:51+09:00 | 404 | 257.9µs | localhost:3000 | GET /favicon.ico
[negroni] 2023-11-09T22:58:04+09:00 | 201 | 0s | localhost:3000 | POST /todos
[negroni] 2023-11-09T22:58:16+09:00 | 201 | 0s | localhost:3000 | POST /todos
[negroni] 2023-11-09T22:58:22+09:00 | 201 | 0s | localhost:3000 | POST /todos
'Book Study > Tucker의 Go Programming' 카테고리의 다른 글
Go스터디: 6주차(27~30장) (0) | 2023.11.05 |
---|---|
Go스터디: 5주차(23~26장) (0) | 2023.10.29 |
Go스터디: 4주차(18~22장) (1) | 2023.10.22 |
Go스터디: 3주차(12~17장) (1) | 2023.10.15 |
Go스터디: 2주차(3~11장) (1) | 2023.10.08 |