Database Migration에 관하여...(with go)

이우길·2022년 3월 4일
0
post-thumbnail

프로젝트를 진행하면서 배포가 되기까지 많은 설계 변동 및 schema 변동이 일어나게 됩니다.

단순히 코드가 몇개 추가 된거야 일이 아니겠지만 (이것 또한 중요합니다..!)

만약 다수의 table이 변동되거나 column이 추가 혹은 삭제가 될 때 이러한 이력관리를 어떻게 처리해야할까요?

또한 협업을 진행중에 다른 개발자분에 의해 Database가 변경되면 어떻게 해야할까요?

이러한 경우에 마이그레이션을 이용하여 프로젝트에 전반적인 Database의 버전을 관리할 수 있습니다.

개발을 할 때 local에서 작성된 code는 형상관리(git...) 툴을 이용하여 변경된 이력을 관리하지만 Database에 대한 변경이력은 migration tool을 이용하여 처리를 하게 됩니다.


시작하기

현재 제가 backend 언어로 사용하는 언어가 Go이기 때문에 Go언어 라이브러리 중 migrate와 함께 하겠습니다.

해당 라이브러리를 사용하는 방법은 2가지가 있습니다.

  1. CLI를 이용

  2. GO code에서 사용하기 (go언어 프로젝트에만 사용할 수 있습니다!)


사용법

먼저 CLI 사용법에 대해 살펴보겠습니다.


CLI 설치

CLI로 이용하려면 각 OS에 맞게 설치를 해줘야 합니다. 설치를 할 때는 아래의 링크를 참조하면 됩니다.
(https://github.com/golang-migrate/migrate/tree/master/cmd/migrate)

설치가 완료되면 migrate가 정상적으로 설치가 되었는지 확인하기 위해 version을 찍어봅시다!

저는 4.14.1 버전을 설치하였습니다.


버전을 관리할 sql 파일 생성하기

migrate의 명령어 option중에 create라는 명령어가 있습니다. 명령어 창에 migrate -help를 입력하면 더 자세한 내용을 확인할 수 있습니다.

$ migrate -help
...
Commands:
  create [-ext E] [-dir D] [-seq] [-digits N] [-format] NAME
           Create a set of timestamped up/down migrations titled NAME, in directory D with extension E.
           Use -seq option to generate sequential up/down migrations with N digits.
           Use -format option to specify a Go time format string. Note: migrations with the same time cause "duplicate migration version" error.
...

그럼 한번 생성해보겠습니다.

migrate create -ext sql -dir ./migrations -seq create_initial_table

위의 명령어를 입력하면 지정한 경로에 파일이 2개 생깁니다.

파일을 자세히 보면 up,down이라는 파일이 생겼습니다. 명령어에서 지정을 해준것처럼 extention은 sql이고 migrations라는 디렉토리에 create_initial_table라는 파일이 생긴 것 입니다.

migration의 대상이 되는 파일을 생성할 때 명명규칙을 확인하고 싶으시면 아래의 링크에서 확인할 수 있습니다.
(https://github.com/golang-migrate/migrate/blob/master/MIGRATIONS.md)


버전을 관리할 sql 작성

생성된 up,down파일을 IDE로 열어 SQL을 작성하면 됩니다.

up sql 안에 Database에 table을 CREATE하는 SQL을 작성하였습니다.

CREATE TABLE IF NOT EXISTS School.Student(
    id INT NOT NULL comment '학생 id',
    name VARCHAR(30) comment '학생 이름'
) comment = '학생에 대한 정보를 가지고 있는 table';

down sql 안에는 table을 DROP하는 SQL을 작성하였습니다.

DROP TABLE IF EXISTS School.Student;

이와 같이 up,down은 한 쌍입니다. Database의 버전이 올라갈 때 up에 있는 SQL문이 실행되고 Database의 버전이 내려갈 때 down에 있는 SQL문이 실행됩니다.


CLI로 Migrate 실행하기

Getting started에서 볼 수 있듯 명령어로 Database를 Migration할 수 있습니다.

명령어는 다음과 같습니다.

$ migrate -source file:${마이그레이션 파일 위치} -database ${database명}://${database host}/${database 명} ${up || down n}

// Ex
migrate -source file:./migrations -database mysql://root:password@tcp(localhost:3306)/School up 1
  • -source file: 뒤에 migration 파일의 위치를 입력 합니다.
  • -database 뒤에는 migrate 라이브러리가 지원하는 Database목록을 확인하여 database 연결에 필요한 host format 등을 확인하여 작성을 합니다.
  • up || down n의 명령어로 Database의 버전을 몇 단계 올릴 것인지 내릴 것인지 결정을 합니다.

Ex를 작성한 것을 풀어보자면 localhost:3306에 있는 mysql School Database에 계정 정보를 이용하여 접속을 하여 버전을 1 올린다는 의미입니다.

실행해보면 결과는 create_initial_table이 성공적으로 up 된 것을 확인할 수 있습니다.

Database에도 Table이 잘 생겼는지 확인하면 만들지 않은 schema_migrations라는 Table이 생성된 것을 확인할 수 있습니다.

해당 Table은 현재 Database의 버전을 저장하고 있는 Table로 조회를 하게 되면 현 Database의 버전을 알 수 있습니다.


Migration 도중 error가 발생했다면?

만약 Migration file안에 SQL을 작성하다가 syntax가 잘못되는 등 Error가 발생하는 경우가 생길 수 있습니다.

migration file을 하나 더 생성하여 Create문을 하나 더 작성하여 진행할 때 syntax에 맞지 않는 Crate문을 작성하여 진행해보겠습니다.

CREATE TABLE IF NOT EXISTS School.Teacher(
    id INT NOT NULL comment '선생님 id',
    name VARCHAR(30) comment '선생님 이름', // 마지막 column에 ,을 추가하여 실패
) comment = '선생님에 대한 정보를 가지고 있는 table';

이 후 up을 진행하면 다음과 같은 결과가 나옵니다.

syntax가 맞지 않아 Table을 생성할 수 없다는 error 내용입니다. 그렇다면 schema_migrations 테이블은 어떻게 변했을까요?

다음과 같이 version은 올라갔지만 dirty라는 값이 1로 변했습니다.

migration이 실행되기 전에 각 데이터베이스는 dirty 플래그를 설정합니다. 그 이유는 실패한 마이그레이션 위에 더 많은 마이그레이션을 실행하려는 시도를 방지기 위해서 입니다.


해결 방법

table은 생성되지 않았지만 version이 올라갔기 때문에 down 명령어를 이용해 버전을 낮춰보겠습니다.

버전 2가 더럽혀졌기 때문에 고치고 force를 이용하여 버전을 지정하라는 error 메세지 입니다.
(https://github.com/golang-migrate/migrate/blob/master/GETTING_STARTED.md#forcing-your-database-version)

force 명령어는 CLI를 실행할 때와 명령어는 동일하지만 뒤에 force가 붙게 됩니다. 현재 버전 2로 올리다가 error가 발생하였으니 force를 이용하여 버전 1로 강제하겠습니다.

migrate -source file:./migrations -database mysql://root:password@tcp(localhost:3306)/School force 1

명령어를 실행하면 버전은 1로 내려가고 dirty값은 다시 0으로 변경된 것을 볼 수 있습니다.

이 후 SQL을 수정한 다음 다시 up을 하게 되면 정상적으로 버전을 올릴 수 있습니다.


마치며...

이와 같이 migrate라는 Go라이브러리를 이용하여 Database의 버전관리에 대해 알아볼 수 있었습니다. 실무에서도 적용해본 결과 협업 할 때도 code뿐만 아니라 Database의 버전을 관리할 수 있다는 점이 작업 효율을 올릴 수 있었습니다:)

profile
leewoooo

0개의 댓글