goose: DB 마이그레이션 버전관리 도구
마이그레이션을 버전 단위로 관리하고 돌리게 해주는 간단한 도구다.
Go로 만들어졌고, 딱 마이그레이션 책임만 담당한다. ORM이 아니다.
언어와 별개로 CLI만 설치해서 쓸 수도 있고, Go 코드 내에서 라이브러리로서 사용할 수도 있다.
사용법만 대강 정리한다.
현재 지원되는 목록은 이 정도다.

CLI 설치
go를 통해 설치할 수 있다.
go install github.com/pressly/goose/v3/cmd/goose@latest

설치하면 즉시 가져다가 쓸 수 있어야 한다.
초기화
이건 마이그레이션 버전 관리나 롤백을 위해서 자체적으로 데이터 관리를 또 한다.
근데 기본 세팅을 하려면 마이그레이션 파일이 하나라도 있어야 한다. CLI를 써서 깡통 마이그레이션을 하나 추가하자.
goose -dir migrations create init sql

이러면 이제 저 폴더에 SQL 파일이 쌓일 것이다.
이렇게 폴더 하나 잡고, 저기에만 계속 마이그레이션 스크립트를 append only로 추가하게 될 것이다.
이 상태에서 status나 아무 명령을 하나 날리면
goose -dir migrations postgres "host=localhost port=5432 user=testuser password=q1w2e3r4 dbname=testdb sslmode=disable" status

마이그레이션을 돌리면서 이렇게 기본 테이블을 하나 추가해준다. 저기에 마이그레이션 히스토리가 기록된다.
up 명령을 써서 한번 돌려보면
goose -dir migrations postgres "host=localhost port=5432 user=testuser password=q1w2e3r4 dbname=testdb sslmode=disable" up

돌면서 기록을 쌓을 것이다. 버전 ID가 일시 기반으로 만드는 파일명의 접두사다.
마이그레이션 돌리기
테이블 하나 추가하는걸 마이그레이션으로 추가해보자.
CLI로 껍데기를 만든 다음에


저기다가 알아서 잘 채워넣으면 된다.
down 절에 있는게 롤백할때 사용할 값이다.
저대로 up을 돌리면

테이블을 추가하고, 히스토리에도 쌓여있을 것이다.
마이그레이션 롤백
이런 마이그레이션 툴을 쓸 때 얻는 이점 중 하나는, 롤백이 용이하다는 것이다.
로그 테이블 마이그레이션을 하나 추가하고



만약 저걸 롤백하고 싶다고 하면, down 명령을 쓰면 된다.
그럼 역순으로, 최신 마이그레이션부터 하나씩 롤백을 돌려준다.

버전에서도 사라진다.
그 마이그레이션을 돌리기 이전처럼 돌아가는 것이다.
down-to를 쓰면 특정 버전 시점으로 돌아가게 하는 것도 가능하다.
그리고 기타 명령어

Go에서 사용하기
CLI로만 쓸 수도 있지만, Go를 쓴다면 프로그래머블하게 쓸 수도 있다.
사용법은 뭐, 크게 다르지 않다. 커넥션만 잘 설정해주고 up 같은 함수만 날려주면 된다.
다음은 간단한 예제 코드다.


예제코드 전체
package main
import (
"database/sql"
"fmt"
"log"
"os"
"github.com/joho/godotenv"
_ "github.com/lib/pq"
"github.com/pressly/goose/v3"
)
func main() {
if err := godotenv.Load(); err != nil {
log.Fatal("failed to load .env:", err)
}
dsn := fmt.Sprintf(
"host=%s port=%s user=%s password=%s dbname=%s sslmode=disable",
os.Getenv("POSTGRES_HOST"),
os.Getenv("POSTGRES_PORT"),
os.Getenv("POSTGRES_USERNAME"),
os.Getenv("POSTGRES_PASSWORD"),
os.Getenv("POSTGRES_DB"),
)
db, err := sql.Open("postgres", dsn)
if err != nil {
log.Fatal("failed to open db:", err)
}
defer db.Close()
if err := db.Ping(); err != nil {
log.Fatal("failed to ping db:", err)
}
goose.SetDialect("postgres")
// 현재 마이그레이션 상태 출력
if err := goose.Status(db, "migrations"); err != nil {
log.Fatal("goose status:", err)
}
// 마이그레이션 실행 (최신 버전으로 업그레이드)
if err := goose.Up(db, "migrations"); err != nil {
log.Fatal("goose up:", err)
}
fmt.Println("migration complete")
}