DevYGwan
Flyway 도입 및 오픈소스 기여 본문
회사에서 프로젝트를 진행하면서, 프로젝트 초기에 DB 관련 DDL 관리가 힘들다는 것을 느꼈습니다. 프로젝트 초기다보니 DDL 수정이 자주 발생하고, 환경 별로 DB 서버를 다르게 하다보니 DB의 변경사항이 발생했을 때, DB 동기화 작업을 매번 하는 것이 번거로워 개발 생산성 저하로 이어지는 문제가 발생했습니다. 그러다보니 자체적으로 초기에는 DB 환경을 하나로 만들어 개발하는 전략으로 진행했습니다... 하지만 이는 환경 별로 종속성이 생겨 협업에 취약하고 서비스 배포 이후에 큰 문제를 야기할 수 있습니다.
따라서 이러한 문제를 해결하기 위해 방법을 찾아보던 중 Flyway라는 DB 형상관리 툴을 알게 되었습니다. 따라서 이를 적용하려고 겪은 시행착오 경험과 결론적으로 Flyway 공식 레포에 기여를 시도한 경험을 공유하려고 합니다.
※ 프로젝트에서 겪은 문제 상황
저희가 프로젝트에서 겪은 문제 상황은 다음과 같습니다.
1. DB 관련 DDL 관리 X
DDL 관련 파일 관리가 제대로 되지 않았습니다. DDL 파일 생성 및 수정에 대한 가이드라인도 없어 ddl, dml 파일들이 혼재되어 있었고, 팀별로 다르게 파일을 관리하다보니 스키마가 변경될 때 이에 대한 DB 반영이 실제로 파일을 변경한 사람만이 제대로 할 수 있었습니다. 따라서 변경에 의존이 생겨 개발 생산성이 저하됐습니다.
2. DB 변경점 추적 불가
DDL 관련 레거시 코드가 많아져 전체 DDL 코드를 모아보니 10,000줄이 넘었습니다. 사실 각각 다른 파일로 관리가 되어 있어 하나의 파일로 합쳐보니 중복된 코드도 많았고 이를 수정한 사람과 어떤 부분을 수정했는지에 대한 변경점을 추적하기가 사실상 불가능 했습니다.
3. DB 변경시 휴먼 에러 발생 가능성 有
프로젝트 진행 시 개발 환경을 DEV, STG, PROD 환경으로 분리했습니다. 또한 ddl-auto 전략을 none으로 처리했습니다. 따라서 DB에 변경이 생기면 모든 환경에서 해당 변경 사항을 사람이 직접 DB에 반영해줘야 합니다. 사람이 직접 하다보니 중간에 변경 사항이 누락되면 큰 장애가 발생할 수 있고 환경 별로 매번 DB에 직접 반영해야되니 번거롭습니다. 또한 DB 변경 같이 민감한 작업에 대한 권한 설정을 하기가 어려워 보안에 취약했습니다.
따라서 이러한 문제로 인해 결국 아래와 같은 문제가 발생하게 되었습니다.
따라서 이러한 문제를 해결하기 위해 오픈소스를 찾아보던 중 DB 형상관리라는 키워드를 알게 되었습니다.
※ DB 형상관리란
형상 관리란, 소프트웨어 구성 관리 또는 형상관리는 소프트웨어의 변경사항을 체계적으로 추적하고 통제하는 것으로, 형상 관리는 일반적인 단순 버전관리 기반의 소프트웨어 운용을 좀 더 포괄적인 학술 분야의 형태로 넓히는 근간을 이야기한다.
즉 DB 형상관리는 데이터베이스의 변경사항을 체계적으로 추적하고 통제하는 것을 의미합니다. 따라서 DB 형상관리를 적용한다면 저희의 문제인 DB 변경에 대한 작업을 통제할 수 있어 문제를 해결할 수 있을 것입니다. 이러한 DB 형상관리를 제공하는 툴은 크게
- Liquibase
- Flyway
가 있습니다. 다른 DB 형상관리 툴도 존재하지만, 주로 DB 벤더 사에서 제공하는 형상관리 툴로 특정 DB에 종속되어있다는 특징이 있었습니다.(Redgate SQL Source Control - SQL SERVER, Alembic - SQLAlchemy 등)
그렇다면 간단하게 Liquibase와 Flyway를 설명하고 제가 Flyway를 선택한 이유와 겪은 문제점을 위주로 설명드리겠습니다.
※ Liqubase
- 데이터베이스 형상관리 오픈소스 도구입니다.
- 데이터베이스의 스키마를 xml, sql, yaml, json으로 관리할 수 있게 지원합니다.
- Oracle, MySQL, PostgreSQL 등 많은 데이터베이스를 지원합니다.
※ Flyway
- flyway is an open-source database-migration tool
- 대표적인 형상관리 툴로 SQL 스크립트를 사용하여 데이터베이스 변경 사항을 버전별로 관리합니다.
- Oracle, MySQL, PostgreSQL 등 많은 데이터베이스를 지원합니다.
- 간편한 설정을 통해 Java, SpringBoot 등 다양한 환경에서 쉽게 적용하여 사용할 수 있습니다.
이렇듯 여러 DB를 지원할 수 있는 DB 마이그레이션 툴은 대표적으로 크게 Liqubase, Flyway가 존재했습니다. 저희는 이중 Flyway를 사용하여 DB 마이그레이션을 하려고 했습니다. Liqubase가 아닌 Flyway를 사용한 이유는,
- 저희 팀원 중에 Flyway를 사용한 경험이 있는 팀원이 있었습니다.
- Flyway의 경우 Liqubase보다 공식 문서가 잘되어 있었고 기여에 관한 자료 또한 공식문서에 잘 나와 있었습니다.
- Flyway가 Liqubase보다 Java & Springboot와의 연동이 더 쉽고 자료가 많았습니다.
- 저희의 최종 목표는 결국 해당 코드의 오픈소스 기여인데, Flyway의 경우 오픈소스 기여에 관한 PR이 더 활발하게 이루어졌고 지원하지 않는 DB에 대한 지원 방법이 더 자세히 나와 있었습니다.
따라서, 이러한 이유로 저희는 Flyway를 선택하여 DB 형상관리를 시도하려 했습니다. 하지만 Flyway가 지원하는 DB 목록은 정해져 있었습니다. 그런데, 저희가 사용하는 DB는 Flyway를 지원하지 않는 것을 확인했습니다. (물론 Liqubase도 저희가 사용하는 DB는 지원하지 않습니다...)
실제로 이전 flyway의 PR을 보니 저희가 사용하는 DB에 대한 지원 질문이 Issue로 올라와 있었고 직접 구현해야 된다는 답변을 받은 내용이 존재했습니다. 그래서 저희 팀은 이를 직접 구현해서 기여하자는 목표를 세웠습니다.
다행히도 flyway-community-db-support 라는 flyway 공식 레포지토리가 존재했습니다. 해당 레포지토리는 flyway에서 지원하지 않는 DB들을 직접 지원 코드를 작성하여 PR로 올릴 수 있는 레포지토리입니다. 또한, 아래 URL 처럼 flyway에 기여할 수 있는 공식 문서를 제공해줘 이러한 자료를 기반으로 flyway 지원 코드를 작성하는 것을 목표로 세웠습니다.
- https://documentation.red-gate.com/flyway/flyway-cli-and-api/contribute/flyway-community-database-support
- https://github.com/flyway/flyway-community-db-support
정리하자면, 저희 팀의 목표는
이렇게 3가지로 정해서 flyway를 지원할 수 있도록 하고, 나아가 실제 프로젝트에 적용하는 것을 목표로 프로젝트를 진행했습니다.
※ Flyway 기여 과정
Flyway가 제공하는 기능은 크게 7가지가 존재합니다.
- Migrate: 데이터베이스의 스키마를 최신 상태로 변경
- Baseline: 기존 데이터베이스를 초기화하여 마이그레이션 관리의 기준점을 설정 (개발 중간에 flyway 도입)
- Info: 데이터베이스에 적용된 모든 마이그레이션 이력을 조회하고 저장
- Repair: 실패한 마이그레이션 파일을 복구
- Validate: 데이터베이스 스키마가 정의된 마이그레이션과 일치하는지 검증
- Undo: 최근에 적용된 마이그레이션을 이전 상태로 되돌림
- Clean: 데이터베이스의 모든 테이블과 데이터를 제거하여 초기화
이 중 저희는 유료 기능인 Undo 기능을 제외한 나머지 기능을 구현했습니다. 구현 코드의 경우 내용을 일일이 설명하기 보다는, 구현 과정에서 겪은 내용을 중점으로 설명드리겠습니다. 혹시나 구현한 코드가 궁금하시다면, 아래 링크를 통해 접근해서 flyway-database-tibero 모듈의 내용을 살펴보시면 코드를 확인하실 수 있습니다.
※ 구현 과정
기본적으로 flyway가 제공하는 기능에 대한 구현은 flyway-core 라이브러리가 일반적으로 처리해줬습니다. 저희는 이 중, DB 연동 부분과 DB마다 다른 SQL 및 스키마 제공 부분을 중점적으로 처리했습니다. 따라서 기본적인 기능 구현은 그렇게 어렵지 않았지만 데이터베이스의 모든 테이블과 데이터를 제거하여 초기화하는 Clean 작업이 가장 번거로웠습니다.
Clean 작업이 번거로운 이유는, Clean의 경우 해당 DB가 제공하는 스키마들을 SQL문으로 직접 다 조회하고, 삭제하는 과정으로 초기화 작업이 이루어집니다. 이러한 과정에서 DB 벤더마다 차이점이 가장 많이 존재했기 때문입니다. 차이점은
- DB 벤더마다 제공하는 스키마 종류가 다르다.
- 제공하는 스키마마다 조회하는 SQL문이 다르다.
- 제공하는 스키마마다 삭제하는 SQL문이 다르다.
따라서 이러한 자료들을 DB 공식문서를 참고하여 일일이 찾아봐야 한다는 번거로움이 존재했습니다. 따라서 Clean 작업이 가장 번거로웠던 것 같습니다. 코드에 대한 내용은 위에 구현한 코드에 대한 Repository에 대한 링크를 첨부해 두었으니 확인하시면 됩니다.
※ 테스트 과정
이렇게 지원하지 않는 DB에 대한 지원 코드를 작성하고 직접 간단하게 테스트 해본 결과 제대로 수행되는 것을 확인할 수 있었습니다. 하지만 저희가 제공하는 오픈 소스를 사용하는 사람의 입장에서, 테스트를 확인할 수 있는 방법이 있어야 해당 오픈소스가 신뢰성이 있다고 생각하여 테스트 코드를 작성하는 것이 좋다고 생각했습니다. 따라서 Flyway가 지원하는 기능들에 대한 테스트 코드를 작성했습니다. 기본적으로 flyway는 버저닝 된 sql 파일을 읽어 DB 형상관리를 진행하기 때문에 sql 버전 파일을 통해 테스트 시나리오를 만들고 Flyway를 연결하여 테스트하는 방법으로 테스트 코드를 작성했습니다. 테스트가 제대로 되었는지에 대한 성공 결과는 flyway_schema_history 테이블을 확인하여 검증하는 방식으로 진행했습니다.
기본적으로 flyway 설정 및 인스턴스 생성에 관한 코드입니다. 데이터베이스 마이그레이션 스크립트의 위치를 지정하기 위한 locations 변수와, 기본적으로 clean모드가 비활성화 되어 있는데 clean 기능 테스트 시 이를 무효화 시킬 수 있는 cleanDisabled 변수를 입력 설정할 수 있습니다.
public static Flyway createFlyway(String locations) {
return createFlyway(locations, false);
}
public static Flyway createFlyway(String locations, boolean cleanDisabled) {
return Flyway.configure()
.locations(locations)
.cleanDisabled(cleanDisabled)
.dataSource(TIBERO_URL, USER, PASSWORD)
.load();
}
이 후, flyway가 제공하는 기능들을 사용한 후 flyway_history_schema 테이블 조회하여 테스트를 진행했습니다.
SoftAssertions softAssertions = new SoftAssertions();
for (String migrationDir : EVOLUTION_SCHEMA_MIGRATION_DIRS) {
softAssertions.assertThat(createFlyway("classpath:db/{file_path}").migrate().success).isTrue();
}
try (Connection conn = DriverManager.getConnection(TIBERO_URL, USER, PASSWORD)) {
Statement stmt = conn.createStatement();
ResultSet schema_history_cnt = stmt.executeQuery("SELECT COUNT(*) FROM TIBERO.\"flyway_schema_history\" WHERE \"success\" = 1");
schema_history_cnt.next();
softAssertions.assertThat(schema_history_cnt).isEqualTo(2);
}
migrate 기능 테스트 시 파일 경로를 입력하고 해당 경로에 있는 처리해야할 migrate SQL 파일 수 만큼 제대로 flyway_schema_history 테이블에 success 상태가 1로 저장되어 있는지를 확인합니다.
flyway_schema_history 테이블이란?
- flyway_schema_history 테이블은 Flyway가 데이터베이스 마이그레이션을 추적하고 관리하기 위해 사용하는 특별한 테이블입니다
- Flyway에서 사용하는 메타데이터 테이블로, 데이터베이스에 적용된 각 마이그레이션 스크립트의 상태와 버전 정보를 저장합니다.
- 해당 테이블을 통해 Flyway는 데이터베이스에 적용된 마이그레이션 스크립트의 상태를 추적하고, 새로운 마이그레이션 스크립트를 실행할 때 이전에 적용된 스크립트를 건너뛰지 않고 진행할 수 있습니다.
※ 정리
이렇게 테스트 코드 작성까지 완료하고 실제 프로젝트에 적용하여 문제 없이 사용이 가능하다는 것을 추가적으로 테스트 했습니다. 따라서 저희가 작업한 코드를 tibero를 사용하는 사람들 중 flyway를 적용하려하는 다른 사람들께 도움이 되고자 오픈소스 기여를 시도했습니다.
아직 관리자의 답변이 오지 않아 PR만 등록된 상태이고 다른 PR을 보니, 생각보다 확인에 시간이 오래 걸릴 것이라고 판단했습니다. 따라서 저희는 저희가 라이브러리 배포를 해서 사내에서라도 간편하게 사용할 수 있게끔 사내 라이브러리 배포를 처리했습니다. 그에 대한 내용은 아래 링크에 정리해두었습니다.
따라서 이렇게 전반적인 flyway 관련 처리가 끝났습니다. 저희가 프로젝트를 진행하면서 불편했던 개선 사항을 처리하기 위해 오픈소스를 조사하고, 조사한 오픈소스의 지원 문제로 인해 추가적으로 공식 문서 & 구현 코드를 분석하여 이를 처리하는 일련의 과정을 거치면서 많이 배울 수 있었습니다. 한번도 해보지 못한 오픈소스 기여를 목적으로 일을 진행하니 더욱 더 문서화와 테스트의 중요성을 느낄 수 있었습니다.
'Study > Spring' 카테고리의 다른 글
비동기 처리 시 @Async와 Springboot의 마법 (0) | 2024.07.18 |
---|---|
Mocking 시 예상대로 동작하지 않는다면, 인스턴스 동등성 문제를 의심해라 (1) | 2024.06.08 |
DB관련 Test 코드 작성 시 유의할 점 :: @DataJpaTest (0) | 2024.05.04 |
JVM Memory의 3가지 메트릭 지표 - Committed Memory란? (0) | 2024.04.02 |
조회 성능 향상을 위한 캐시 처리 With Redis (1) | 2024.01.29 |