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")
}


참조
https://github.com/pressly/goose