sqllite 로 변경하는 중간과정으로 interface 구조를 구현하던중, DB 의 Close 기능은 main.go 까지 전달해야해서 변경사항이 예상보다 많았습니다.
1. 기존에 model 에서 처리하던 interface 이름을 공개하여 app.go -> main.go 까지 전달함.
2. 해당 interface 이름을 공개하며 인터페이스 및 매소드명을 대문자로 모두 변경함.
기본 구조는 아래와 같음.
model.go
package model
import (
"time"
)
type Todo struct {
ID int `json:"id"`
Name string `json:"name"`
Completed bool `json:"completed"`
CreatedAt time.Time `json:"created_at"`
}
// 인터페이스 외부로 공개를 해야함, close 함수의 권한을 main.go 에 넘겨주기위해서
// 인터페이스 이름 및 내부 함수들을 대문자로변경, 외부에 공개
type DBHandler interface {
GetTodos() []*Todo
AddTodo(name string) *Todo
RemoveTodo(id int) bool
CompleteTodo(id int, complete bool) bool
Close()
}
// init -> NewDBHandler
func NewDBHandler() DBHandler {
// handler = newMemoryHandler()
// sqlite hanlder 만들면 아래처럼 변경만 하면 됨.
// handler = newSqliteHandler()
return newMemoryHandler()
}
memoryHandler.go - 매소드 이름을 모두 대문자로 변경
package model
import (
"log"
"time"
)
type memoryHandler struct {
todoMap map[int]*Todo
}
func (m *memoryHandler) GetTodos() []*Todo {
list := []*Todo{}
for _, v := range m.todoMap {
list = append(list, v)
}
return list
}
func (m *memoryHandler) AddTodo(name string) *Todo {
id := len(m.todoMap) + 1
todo := &Todo{id, name, false, time.Now()}
m.todoMap[id] = todo
log.Println("add Todo success")
return todo
}
func (m *memoryHandler) RemoveTodo(id int) bool {
if _, ok := m.todoMap[id]; ok {
delete(m.todoMap, id)
return true
}
return false
}
func (m *memoryHandler) CompleteTodo(id int, complete bool) bool {
if todo, ok := m.todoMap[id]; ok {
todo.Completed = complete
return true
}
return false
}
func (m *memoryHandler) Close() {
}
func newMemoryHandler() DBHandler {
m := &memoryHandler{}
m.todoMap = make(map[int]*Todo)
return m
}
sqliteHandler.go - 신규추가, 기능 구현 미완료.
package model
import (
"database/sql"
_ "github.com/mattn/go-sqlite3"
)
type sqliteHandler struct {
db *sql.DB
}
func (s *sqliteHandler) GetTodos() []*Todo {
return nil
}
func (s *sqliteHandler) AddTodo(name string) *Todo {
return nil
}
func (s *sqliteHandler) RemoveTodo(id int) bool {
return false
}
func (s *sqliteHandler) CompleteTodo(id int, complete bool) bool {
return false
}
func (s *sqliteHandler) Close() {
s.db.Close()
}
func newSqliteHandler() DBHandler {
database, err := sql.Open("sqlite3", "./test.db")
if err != nil {
panic(err)
}
statement, _ := database.Prepare(
`CREATE TABLE IF NOT EXISTS todos (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT,
completed BOOLEAN,
createdAt DATETIME
)`)
statement.Exec()
return &sqliteHandler{}
}
app.go - sqlite handler 를 포함한 구조체를 전체를 전달
package myapp
import (
"GO/tuckersGo/goWeb/web18-todo_sqlite/model"
"net/http"
"strconv"
"github.com/gorilla/mux"
"github.com/unrolled/render"
)
var rd *render.Render = render.New()
// 인터페이스 외부로 공개를 해야함, close 함수의 권한을 main.go 에 넘겨주기위해서
// app.go 에도 언제 close 할지 모르므로 main.go 로 이관.
// 그래서 AppHandler 만들어서 넘겨줌.
type AppHandler struct {
// 임베디드 처리함. 이유는 ??? 상속과 비슷한데 조금 다름 is 관계는 아니고, has 관계임.
// 이름을 암시적으로 생략함. handler 생략함.
http.Handler
db model.DBHandler
}
func (a *AppHandler) indexHandler(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/todo.html", http.StatusTemporaryRedirect)
}
func (a *AppHandler) getTodoListHandler(w http.ResponseWriter, r *http.Request) {
list := a.db.GetTodos()
rd.JSON(w, http.StatusOK, list)
}
func (a *AppHandler) addTodoHandler(w http.ResponseWriter, r *http.Request) {
name := r.FormValue("name")
todo := a.db.AddTodo(name)
rd.JSON(w, http.StatusCreated, todo)
}
type Success struct {
Success bool `json:"success"`
}
func (a *AppHandler) removeTodoHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id, _ := strconv.Atoi(vars["id"])
ok := a.db.RemoveTodo(id)
if ok {
rd.JSON(w, http.StatusOK, Success{Success: true})
} else {
rd.JSON(w, http.StatusOK, Success{Success: false})
}
}
func (a *AppHandler) completeTodoHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id, _ := strconv.Atoi(vars["id"])
complete := r.FormValue("complete") == "true"
ok := a.db.CompleteTodo(id, complete)
if ok {
rd.JSON(w, http.StatusOK, Success{Success: true})
} else {
rd.JSON(w, http.StatusOK, Success{Success: false})
}
}
func (a *AppHandler) Close() {
a.db.Close()
}
// 리턴변경 http.Handler -> AppHandler
func MakeNewHandler() *AppHandler {
mux := mux.NewRouter()
a := &AppHandler{
Handler: mux,
db: model.NewDBHandler(),
}
mux.HandleFunc("/", a.indexHandler)
mux.HandleFunc("/todos", a.getTodoListHandler).Methods("GET")
mux.HandleFunc("/todos", a.addTodoHandler).Methods("POST")
mux.HandleFunc("/todos/{id:[0-9]+}", a.removeTodoHandler).Methods("DELETE")
mux.HandleFunc("/complete-todo/{id:[0-9]+}", a.completeTodoHandler).Methods("GET")
return a
}
main.go - DB Close 호출. 이부분을 위해서 하위 구조를 변경함
package main
import (
"GO/tuckersGo/goWeb/web18-todo_sqlite/myapp"
"log"
"net/http"
"github.com/urfave/negroni"
)
const portNumber = ":3000"
func main() {
mux := myapp.MakeNewHandler()
defer mux.Close()
ng := negroni.Classic()
ng.UseHandler(mux)
log.Println("Started App")
err := http.ListenAndServe(portNumber, ng)
if err != nil {
panic(err)
}
}
테스트 결과 정상
PS D:\workspace\GO\tuckersGo\goWeb\web19-todo_sqlite\myapp> go test
2021/11/18 00:12:36 add Todo success
2021/11/18 00:12:36 app_test.go / add result > {1 Test todo false 2021-11-18 00:12:36.0639536 +0100 CET} 1
2021/11/18 00:12:36 add Todo success
2021/11/18 00:12:36 app_test.go / add result > {2 Test todo2 false 2021-11-18 00:12:36.0986662 +0100 CET} 2
2021/11/18 00:12:36 after getting whole data > {2 Test todo2 false 2021-11-18 00:12:36.0986662 +0100 CET}
2021/11/18 00:12:36 after getting whole data > {1 Test todo false 2021-11-18 00:12:36.0639536 +0100 CET}
2021/11/18 00:12:36 after complete for > {1 Test todo false 2021-11-18 00:12:36.0639536 +0100 CET}
2021/11/18 00:12:36 after complete for > {2 Test todo2 true 2021-11-18 00:12:36.0986662 +0100 CET}
2021/11/18 00:12:36 delete id1: 1
2021/11/18 00:12:36 after delete for > {2 Test todo2 true 2021-11-18 00:12:36.0986662 +0100 CET}
PASS
ok GO/tuckersGo/goWeb/web18-todo_sqlite/myapp 0.375s
PS D:\workspace\GO\tuckersGo\goWeb\web19-todo_sqlite\myapp>
그리고 문법적인 오류가 발생하지 않는다면 ./myapp 폴더에 test.db 파일도 생성된것을 볼 수 있다.
'GO lang > web' 카테고리의 다른 글
[GO] Todo list - Google Oauth2 (0) | 2021.11.23 |
---|---|
[GO] Todo list - sqlite (0) | 2021.11.18 |
[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 |