본문 바로가기

GO lang/web

[GO] Todo list - sqlite

기본 구조는 아래와 같습니다.

 

 

이번에는 sqlliteHandler.go 파일을 마무리하고 디비파일 위치와 이름을 파라미터로 처리하게 변경하였음.

그래서 테스트시에는 test.db 를 사용하고 실제 운영시에는 todo.db 를 사용하게 처리 가능하였음.

 

./model/sqlliteHandler.go - 완료

 

package model

import (
	"database/sql"
	"time"

	_ "github.com/mattn/go-sqlite3"
)

type sqliteHandler struct {
	db *sql.DB
}

func (s *sqliteHandler) GetTodos() []*Todo {
	todos := []*Todo{}
	sql_string := "SELECT id, name, completed, createdAt From todos"
	rows, err := s.db.Query(sql_string)
	if err != nil {
		panic(err)
	}
	defer rows.Close()

	for rows.Next() {
		var todo Todo
		rows.Scan(&todo.ID, &todo.Name, &todo.Completed, &todo.CreatedAt)
		todos = append(todos, &todo)
	}
	return todos
}

func (s *sqliteHandler) AddTodo(name string) *Todo {
	sql_string := "INSERT INTO todos (name, completed, createdAt) VALUES(?,?,datetime('now'))"
	stmt, err := s.db.Prepare(sql_string)
	if err != nil {
		panic(err)
	}

	result, err := stmt.Exec(name, false)
	if err != nil {
		panic(err)
	}

	id, _ := result.LastInsertId()
	var todo Todo
	todo.ID = int(id)
	todo.Name = name
	todo.Completed = false
	todo.CreatedAt = time.Now()

	return &todo
}

func (s *sqliteHandler) RemoveTodo(id int) bool {
	sql_string := "DELETE FROM todos WHERE id = ?"
	stmt, err := s.db.Prepare(sql_string)
	if err != nil {
		panic(err)
	}

	result, err := stmt.Exec(id)
	if err != nil {
		panic(err)
	}
	cnt, _ := result.RowsAffected()

	return cnt > 0
}

func (s *sqliteHandler) CompleteTodo(id int, complete bool) bool {
	sql_string := "UPDATE todos SET completed = ? WHERE id = ?"
	stmt, err := s.db.Prepare(sql_string)
	if err != nil {
		panic(err)
	}

	result, err := stmt.Exec(complete, id)
	if err != nil {
		panic(err)
	}
	cnt, _ := result.RowsAffected()

	return cnt > 0
}

func (s *sqliteHandler) Close() {
	s.db.Close()
}

func newSqliteHandler(filepath string) DBHandler {
	database, err := sql.Open("sqlite3", filepath)
	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{db: database}
	// return &sqliteHandler{}
}

 

./model/model.go 및 관련 파일 수정 - filepath 를 main.go 에서 설정가능하게 모두 수정함.

 

// sqliteHandler.go 파일의 파라미터 추가
func newSqliteHandler(filepath string) DBHandler {
	database, err := sql.Open("sqlite3", filepath)

// sqliteHandler.go 파일의 파라미터 추가로 상위 파일인 model.go 파일수정.

func NewDBHandler(filepath string) DBHandler {
	return newSqliteHandler(filepath)
}

// 관련하여 app.go 파일도 수정
func MakeNewHandler(filepath string) *AppHandler {

	mux := mux.NewRouter()
	a := &AppHandler{
		Handler: mux,
		db:      model.NewDBHandler(filepath),
	}

// 최상위 호출파일인 main.go 수정
func main() {
	mux := myapp.MakeNewHandler("./todo.db")
	defer mux.Close()

 

test result 

 

PS D:\workspace\GO\tuckersGo\goWeb\web19-todo_sqlite\myapp>
PS D:\workspace\GO\tuckersGo\goWeb\web19-todo_sqlite\myapp> go test
2021/11/19 14:03:52 app_test.go / add result > {1 Test todo false 2021-11-19 14:03:52.6294806 +0100 CET} 1
2021/11/19 14:03:52 app_test.go / add result > {2 Test todo2 false 2021-11-19 14:03:52.6647899 +0100 CET} 2
2021/11/19 14:03:52 after getting whole data > {1 Test todo false 2021-11-19 13:03:52 +0000 UTC}
2021/11/19 14:03:52 after getting whole data > {2 Test todo2 false 2021-11-19 13:03:52 +0000 UTC}
2021/11/19 14:03:52 after complete for > {1 Test todo false 2021-11-19 13:03:52 +0000 UTC}
2021/11/19 14:03:52 after complete for > {2 Test todo2 true 2021-11-19 13:03:52 +0000 UTC}
2021/11/19 14:03:52 delete id1:  1
2021/11/19 14:03:52 after delete for > {2 Test todo2 true 2021-11-19 13:03:52 +0000 UTC}
PASS
ok      GO/tuckersGo/goWeb/web18-todo_sqlite/myapp      0.387s
PS D:\workspace\GO\tuckersGo\goWeb\web19-todo_sqlite\myapp>

 

참고로 app_test.go 파일은 아래 부분을 수정함.

테스트용 db 를 별도로 생성한 이유는 테스트시 insert 된 내용이 중복으로 db 에 저장되므로 검증시 오류로 인식함.

 

func TestTodos(t *testing.T) {
	os.Remove("./test.db") // insert 결과가 계속 저장되어 삭제하고 테스트를 시작함
	assert := assert.New(t)
	appH := MakeNewHandler("./test.db") // 테스트용 db를 별도로 생성, 위치는 ./myapp/test.db
	defer appH.Close() // 자동종료 추가

 

이제는 아래의 호출자에 따라서 SQLite3 또는 메모리상의 map 구조로 데이터를 저장가능하게 되었음.

그러나, sqlite3는 서버를 재실행해도 기존 todo lists 가 저장되어 있는 반면, map 구조는 서버를 재실행시 todo lists 가 초기화 되어 이전 리스트가 사라지는 차이점이 있음.

 

// sqlite3 로 처리한 경우
func NewDBHandler(filepath string) DBHandler {
	return newSqliteHandler(filepath)
}

// map 으로 처리한 경우
func NewDBHandler(filepath string) DBHandler {
	return newMemoryHandler()
}

 

sqlite DB browser 는 다음 툴로 확인 가능함 ==> [DB Browser for SQLite (sqlitebrowser.org)]

 

참고자료 [유투브 링크]

 

- 패키지 설치 및 컴파일 오류 대처

 

1. sqlite3를 사용하기 위해서는 tdm-gcc를 설치해야 함 - 관련 설명은 아래 링크 참고
[https://www.youtube.com/watch?v=UVhprQ5CYh4&list=PLy-g2fnSzUTDALoERcKDniql16SAaQYHF&index=19]

2. go get github.com/mattn/go-sqlite3 패키지 설치
tdm-gcc 설치 후, MinGW  prompt 상에서 "go get github.com/mattn/go-sqlite3" 을 실행해야 함.

[MinGW  prompt 실행화면]
Setting up environment for using MinGW with GCC from C:\TDM-GCC-64\.

C:\TDM-GCC-64>go get github.com/mattn/go-sqlite3
go: downloading github.com/mattn/go-sqlite3 v1.14.9

C:\TDM-GCC-64>

3. 강좌에서는 1,2 번만 처리하는데, 나는 오류가 발생하여 아래와 같이 추가 처리함.
(tdm-gcc 설치후 해당 프롬프트에서 "go get github.com/mattn/go-sqlite3" 명령어 처리)

/* 오류 내용은 아래와 같음.
PS D:\workspace\GO\tuckersGo\goWeb\web19-todo_sqlite\myapp> go test
..\model\sqlliteHandler.go:6:2: no required module provides package github.com/mattn/go-sqlite3; to add it:
        go get github.com/mattn/go-sqlite3
PS D:\workspace\GO\tuckersGo\goWeb\web19-todo_sqlite\myapp> 
*/

PS D:\workspace\GO\tuckersGo\goWeb\web19-todo_sqlite\myapp> go get github.com/mattn/go-sqlite3
go get: added github.com/mattn/go-sqlite3 v1.14.9
PS D:\workspace\GO\tuckersGo\goWeb\web19-todo_sqlite\myapp>