지금까지 Todos를 Heroku에 배포를 했습니다.
heroku에서는 dyno라는 컨테이너를 쓰는데, 이것이 statelss라 fileDB를 사용할 수 없게 되어 실제 DB를 사용해야 하는데
heroku가 클라우드 서비스이기 때문에 PostgreDB라는 DB서비스를 10000레코드까지만 무료로 제공하고 있습니다.
우선 커맨드 창을 띄운 뒤에 Heroku 로그인을 해줍니다. 그 후 해당 명령어를 입력합니다.
heroku addons:create heroku-postgresql:hobby-dev
이렇게 하면 현재 앱에 postgresqlDB가 추가가 되고, 티어는 hobby-dev인 무료티어로 추가가 됩니다.
Created postgresql-round-47123 as DATABASE_URL
라는 의미는 DATABASE_URL라는 이름으로 환경변수가 추가되었다는 의미입니다.
그래서 heroku config를 사용하여 환경변수를 조회하면 DATABASE_URL라는 이름으로 환경변수가 추가되었다는 것을 확인 할 수 있습니다.
이것을 이용해서 PostgreSQL에 접속할 수 있습니다.
Go에서 사용하기 위해서 패키지를 받아주어야 한다. 다음과 같이 입력 해줍니다.
go get github.com/lib/pq
pq가 postgreSQL을 의미 합니다.
그리고 이전에 만들었던 model/sqliteHandler.go
로 와서 복사 붙여넣기해서 pqHandler.go로 이름만 바꾸어 줍니다.
그 후 코드를 다음과 같이 수정해 줍니다.
package model
import (
"database/sql"
"time"
_ "github.com/mattn/go-sqlite3"
)
type pqHandler struct {
db *sql.DB
}
func (s *pqHandler) GetTodos() []*Todo {
todos := []*Todo{}
rows, err := s.db.Query("SELECT id, name, completed, createdAt FROM todos")
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 *pqHandler) AddTodo(name string) *Todo {
stmt, err := s.db.Prepare("INSERT INTO todos (name, completed, createdAt) VALUES (?, ?, datetime('now'))")
if err != nil {
panic(err)
}
rst, err := stmt.Exec(name, false)
if err != nil {
panic(err)
}
id, _ := rst.LastInsertId()
var todo Todo
todo.ID = int(id)
todo.Name = name
todo.Completed = false
todo.CreatedAt = time.Now()
return &todo
}
func (s *pqHandler) RemoveTodo(id int) bool {
stmt, err := s.db.Prepare("DELETE FROM todos WHERE id=?")
if err != nil {
panic(err)
}
rst, err := stmt.Exec(id)
if err != nil {
panic(err)
}
cnt, _ := rst.RowsAffected()
return cnt > 0
}
func (s *pqHandler) CompleteTodo(id int, complete bool) bool {
stmt, err := s.db.Prepare("UPDATE todos SET completed=? WHERE id=?")
if err != nil {
panic(err)
}
rst, err := stmt.Exec(complete, id)
if err != nil {
panic(err)
}
cnt, _ := rst.RowsAffected()
return cnt > 0
}
func (s *pqHandler) Close() {
s.db.Close()
}
func newPQHandler(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}
}
이런식으로 기존 handler struct를 pqHandler로 바꾼 뒤 이름만 바꾸어 줍니다.
그 후 model/model.go
로 넘어와서 newPQHandler가 생성되도록 수정해 줍니다.
func NewDBHandler(dbConn string) DBHandler {
//handler = newMemoryHandler()
return newPQHandler(dbConn)
}
그리고 기존에는 filePath였는데 지금은 DB정보가 와야 하기 때문에 dbConn으로 변경해 줍니다.
마찬가지로 model/sqliteHandler.go
도 수정해 줍니다.
import (
"database/sql"
"time"
_ "github.com/lib/pq"
)
func newSqliteHandler(dbConn string) DBHandler {
database, err := sql.Open("postgres", dbConn)
if err != nil {
panic(err)
}
statement, err := database.Prepare(
`CREATE TABLE IF NOT EXISTS todos (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT,
completed BOOLEAN,
createdAt DATETIME
)`)
if err != nil {
panic(err)
}
_, err = statement.Exec()
if err != nil {
panic(err)
}
return &sqliteHandler{db: database}
}
마찬가지로 여기에서도 dbConn으로 변경해주었고, error를 받아서 error가 날 때마다 확인하도록 수정했고,
import 부분에서 go-sqlite3을 pq로, newSqliteHandler부분에서 postgres로 수정해 주었습니다.
그 후 main.go
에서 file을 넣었는데, dbConn을 넣어 줍니다.
func main() {
port := os.Getenv("PORT")
m := app.MakeHandler(os.Getenv("DATABASE_URL"))
defer m.Close()
log.Println("Started App")
err := http.ListenAndServe(":"+port, m)
if err != nil {
panic(err)
}
}
이렇게 하면 준비는 끝났습니다. 저장 후에, add, commit, push를 해줍시다!
그 후 잘 동작하는지 log를 확인합니다.
log창을 보면 panic: runtime error: invalid memory address or nil pointer dereference이라는 에러가 떴는데, model/sqliteHandler.go
에서 DB테이블을 만들 때
func newPQHandler(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}
}
id INTEGER PRIMARY KEY AUTOINCREMENT,
이 부분에서 에러가 났네요.
각 DB마다 문법이 다른데, 이 부분을
func newPQHandler(dbConn string) DBHandler {
database, err := sql.Open("postgres", dbConn)
if err != nil {
panic(err)
}
statement, err := database.Prepare(
`CREATE TABLE IF NOT EXISTS todos (
id SERIAL PRIMARY KEY,
sessionId VARCHAR(256),
name TEXT,
completed BOOLEAN,
createdAt TIMESTAMP
);`)
if err != nil {
panic(err)
}
_, err = statement.Exec()
if err != nil {
panic(err)
}
statement, err = database.Prepare(
`CREATE INDEX IF NOT EXISTS sessionIdIndexOnTodos ON todos (
sessionId ASC
);`)
if err != nil {
panic(err)
}
_, err = statement.Exec()
if err != nil {
panic(err)
}
return &sqliteHandler{db: database}
}
로 바꾸어주면 되며, 한 번에 여러개의 커맨드를 하나로 사용할 수 없기 때문에 나누어서 실행시켜주도록 수정해 주어야 하고,
STRING을 사용할 수 없기 때문에 VARCHAR(256)으로 수정해주고, DATETIME을 사용할 수 없어 같은 기능을 하는 TIMESTAMP로 변경 해줍니다. 이후 저장 후 다시 add, commit, push를 진행합니다.
그런데... 아래와 같은 문제가 발생했습니다. 뭐가 문제일까요...?
'프로그래밍(Web) > Golang' 카테고리의 다른 글
[바미] Go - Websocket (0) | 2020.12.18 |
---|---|
[바미] Go - PostgreDB(2) feat. 드디어 풀린 에러 (0) | 2020.12.18 |
[바미] Go - todos 배포 (0) | 2020.12.18 |
[바미] Go - HEROKU를 사용한 웹 호스팅 (0) | 2020.12.17 |
[바미] Go - 세션별 데이터 저장 (0) | 2020.12.17 |