Skip to content

flyway를 사용한 데이터 형상관리

dictionary-api 프로젝트는 flyway를 스키마 변경 이력을 엄격하게 형상관리하는 용도가 아니라, 개발 초반 단계의 테이블 생성(ddl) + 테스트 데이터 생성(data) 용도로 사용한다.

  1. 패키지 설정
    build.gradle
    dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-flyway'
    implementation 'org.flywaydb:flyway-mysql'
    }
  2. 프로파일별 로케이션 설정
    config/local/application-local.yaml
    spring:
    flyway:
    locations: classpath:db/migration/mysql
    config/test/application-test.yaml
    spring:
    flyway:
    locations: classpath:db/migration/h2
  • 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
  1. 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;
  2. 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;
  3. 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');
  4. Y/N 플래그 컬럼(use_yn, admin_yn, lock_yn, guest_yn 등)은 VARCHAR(1)이 아니라 CHAR(1)을 쓴다 — 값 길이가 항상 고정이라는 의미를 컬럼 타입에 그대로 드러내기 위함이다.
  1. 파일명 패턴
    V{ddl 계열: 1, data 계열: 2}_{일련번호}__{설명}.sql
  2. ddl은 V1_01_x, data는 V1_02_x를 각 계열 안에서 독립적으로 순서대로 채번한다 — 새 테이블 ddl은 마지막 V1_01_x 다음 번호, 새 데이터는 마지막 V1_02_x 다음 번호를 쓴다. 전체 시퀀스 순서를 억지로 맞추려고 두 계열을 뒤섞지 않는다.
  3. flyway는 전체를 하나의 순서로만 비교하므로, 이미 data(V1_02_x) 버전이 로컬 MySQL에 적용된 뒤 그보다 낮은 V1_01_x 번호의 새 ddl을 추가하면 outOfOrder 오류가 날 수 있다 — 형상관리 이력이 아니라 초반 개발용 스크립트이므로 번호를 억지로 맞추지 않는다.
  1. 스프링 부트 서버가 기동될 때마다 flyway가 실행된다.
  2. flyway 이력 테이블 이름은 flyway_schema_history이다.
  3. flyway가 기동되었을 때, 마지막 버전보다 상위 버전의 파일이 있으면 해당 파일을 실행하고 이력 데이터를 생성한다.
  4. test/prod는 인메모리 H2라 매번 새로 마이그레이션되지만, local(MySQL)은 이력이 그대로 누적된다.
  1. 테이블 컬럼을 바꾸거나 outOfOrder 오류가 났을 때: 기존 파일 내용을 그대로 수정한다.
  2. 로컬 MySQL의 flyway_schema_history 테이블을 삭제한다.
  3. 애플리케이션을 재기동한다 — flyway가 테이블/데이터를 처음부터 다시 생성한다.
  4. 테스트(./gradlew test)는 항상 test 프로파일(H2)로 실행되어 이 문제와 무관하다 — local 프로파일로 로컬 MySQL을 기동할 때만 직접 확인해야 한다.

create_user_id, update_user_id 컬럼 정책

Section titled “create_user_id, update_user_id 컬럼 정책”
  1. flyway를 통해 생성되는 데이터는 값을 1000(SYSTEM)으로 설정한다.
  2. 런타임에 생성되는 경우에는 로그인한 사용자의 ID를 사용한다. 로그인하지 않은 상태로 생성되는 경우에는 1000(SYSTEM)으로 설정한다.