flyway를 사용한 데이터 형상관리
dictionary-api 프로젝트는 flyway를 스키마 변경 이력을 엄격하게 형상관리하는 용도가 아니라, 개발 초반 단계의
테이블 생성(ddl) + 테스트 데이터 생성(data) 용도로 사용한다.
flyway 설정
Section titled “flyway 설정”- 패키지 설정
build.gradle dependencies {implementation 'org.springframework.boot:spring-boot-starter-flyway'implementation 'org.flywaydb:flyway-mysql'} - 프로파일별 로케이션 설정
config/local/application-local.yaml spring:flyway:locations: classpath:db/migration/mysqlconfig/test/application-test.yaml spring:flyway:locations: classpath:db/migration/h2
디렉토리 구조
Section titled “디렉토리 구조”Directoryresources/db/migration/
Directorymysql/
local프로파일(로컬 MySQL)이 사용Directoryddl/
- V1_01_001__drop_tables.sql
- V1_01_002__create_t_board.sql
Directorydata/
- V1_02_002__insert_t_board.sql
Directoryh2/
test/prod프로파일(인메모리 H2)이 사용Directoryddl/
- V1_01_001__drop_tables.sql
- V1_01_002__create_t_board.sql
Directorydata/
- V1_02_002__insert_t_board.sql
- mysql/h2 두 디렉토리로 DB별로 나눈다 — 파일명(버전)은 동일하게 맞추고, MySQL 전용 문법만 H2 문법으로 바꾼다.
mysql/ddl/V1_01_002__create_t_board.sql CREATE TABLE IF NOT EXISTS t_board (board_id BIGINT AUTO_INCREMENT NOT NULL COMMENT '게시물ID',title VARCHAR(200) NULL COMMENT '제목',content VARCHAR(1000) NOT NULL COMMENT '본문',create_dt TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP COMMENT '등록일시',create_user_id INTEGER NULL DEFAULT NULL COMMENT '등록 ID',create_ip VARCHAR(40) NULL DEFAULT NULL COMMENT '등록 IP',PRIMARY KEY(board_id)) ENGINE=InnoDB ROW_FORMAT=DYNAMIC;ALTER TABLE t_board COMMENT = 'CRUD 샘플 게시판';ALTER TABLE t_board AUTO_INCREMENT = 1000;h2/ddl/V1_01_002__create_t_board.sql CREATE TABLE IF NOT EXISTS t_board (board_id BIGINT AUTO_INCREMENT NOT NULL COMMENT '게시물ID',title VARCHAR(200) NULL COMMENT '제목',content VARCHAR(1000) NOT NULL COMMENT '본문',create_dt TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP COMMENT '등록일시',create_user_id INTEGER NULL DEFAULT NULL COMMENT '등록 ID',create_ip VARCHAR(40) NULL DEFAULT NULL COMMENT '등록 IP',PRIMARY KEY(board_id));COMMENT ON TABLE t_board IS 'CRUD 샘플 게시판';ALTER TABLE t_board ALTER COLUMN board_id RESTART WITH 1000; - ddl 디렉토리 최상단(
V1_01_001__drop_tables.sql)에는 공용 DROP 목록을 둔다 — 테이블 재생성이 필요할 때 유용하다.ddl/V1_01_001__drop_tables.sql DROP TABLE IF EXISTS t_board;DROP TABLE IF EXISTS t_menu;DROP TABLE IF EXISTS t_user; - data 디렉토리 파일 샘플
data/V1_02_002__insert_t_board.sql INSERT INTO t_board (title, content, create_dt, create_user_id, create_ip)VALUES('[금상] 눈보라 휘날리며(유영복作)', '철도 사진 공모전 - 13회', NOW(), 1000, '::1'),('[은상] 억새밭 무궁화호(변양옥作)', '철도 사진 공모전 - 13회', NOW(), 1000, '::1'); Y/N플래그 컬럼(use_yn,admin_yn,lock_yn,guest_yn등)은VARCHAR(1)이 아니라CHAR(1)을 쓴다 — 값 길이가 항상 고정이라는 의미를 컬럼 타입에 그대로 드러내기 위함이다.
파일명 규칙(버전 채번)
Section titled “파일명 규칙(버전 채번)”- 파일명 패턴
V{ddl 계열: 1, data 계열: 2}_{일련번호}__{설명}.sql
- ddl은
V1_01_x, data는V1_02_x를 각 계열 안에서 독립적으로 순서대로 채번한다 — 새 테이블 ddl은 마지막V1_01_x다음 번호, 새 데이터는 마지막V1_02_x다음 번호를 쓴다. 전체 시퀀스 순서를 억지로 맞추려고 두 계열을 뒤섞지 않는다. - flyway는 전체를 하나의 순서로만 비교하므로, 이미
data(V1_02_x) 버전이 로컬 MySQL에 적용된 뒤 그보다 낮은V1_01_x번호의 새ddl을 추가하면outOfOrder오류가 날 수 있다 — 형상관리 이력이 아니라 초반 개발용 스크립트이므로 번호를 억지로 맞추지 않는다.
작업 프로세스
Section titled “작업 프로세스”- 스프링 부트 서버가 기동될 때마다 flyway가 실행된다.
- flyway 이력 테이블 이름은
flyway_schema_history이다. - flyway가 기동되었을 때, 마지막 버전보다 상위 버전의 파일이 있으면 해당 파일을 실행하고 이력 데이터를 생성한다.
test/prod는 인메모리 H2라 매번 새로 마이그레이션되지만,local(MySQL)은 이력이 그대로 누적된다.
변경사항 적용 전략
Section titled “변경사항 적용 전략”- 테이블 컬럼을 바꾸거나
outOfOrder오류가 났을 때: 기존 파일 내용을 그대로 수정한다. - 로컬 MySQL의
flyway_schema_history테이블을 삭제한다. - 애플리케이션을 재기동한다 — flyway가 테이블/데이터를 처음부터 다시 생성한다.
- 테스트(
./gradlew test)는 항상test프로파일(H2)로 실행되어 이 문제와 무관하다 —local프로파일로 로컬 MySQL을 기동할 때만 직접 확인해야 한다.
create_user_id, update_user_id 컬럼 정책
Section titled “create_user_id, update_user_id 컬럼 정책”- flyway를 통해 생성되는 데이터는 값을
1000(SYSTEM)으로 설정한다. - 런타임에 생성되는 경우에는 로그인한 사용자의 ID를 사용한다. 로그인하지 않은 상태로 생성되는 경우에는
1000(SYSTEM)으로 설정한다.