| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 |
- 마이크로서비스아키텍처
- 아키텍처
- java
- 컨테이너
- Flyway
- 마이크로서비스
- 마이그레이션
- PostgreSQL
- dockercompose
- 백엔드
- 분산시스템
- Database
- springboot
- GitHub Packages
- gradle
- 자바
- 인프라
- 백엔드면접준비
- 멀티모듈
- CS
- Java 8
- ci/cd
- MSA
- SpringCloud
- GCP
- github actions
- 트러블슈팅
- 공통모듈
- docker
- 도커
- Today
- Total
NYO_O
Flyway 실전 도입기 3편: 하이버네이트가 만든 스키마의 5가지 문제점과 V1 스크립트 완벽 정제 본문
전체 작업의 순서는 다음과 같습니다.
- 로컬 DB 초기화 및 V1 스키마 생성하기
- pg_dump로 순수 스키마 SQL 추출하기
- 하이버네이트가 만든 스키마의 5가지 문제점과 V1 스크립트 완벽 정제 ← 오늘 다룰 내용
- Spring Boot 연동부터 마이그레이션 검증까지 완벽 가이드
1. 서론: 뽑아낸 스키마, 그대로 써도 될까?
지난 2편 포스팅에서는 pg_dump를 활용해 도커에 띄워둔 초기 데이터베이스에서 순수 스키마 구조만 SQL 파일(schema_dump.sql)로 추출하는 데 성공했습니다.
2026.05.20 - [기술 스택/Flyway] - Flyway 실전 도입기 2편: pg_dump로 순수 스키마 SQL 추출하기
Flyway 실전 도입기 2편: pg_dump로 순수 스키마 SQL 추출하기
오늘부터 진행할 전체 작업의 순서는 다음과 같습니다.로컬 DB 부팅 및 앱 실행 (현재 ddl-auto: update 활용)pg_dump로 기존 스키마만 SQL로 추출 ← 오늘 다룰 내용추출된 SQL을 정제하여 V1__init.sql 작성
ddangnyo.tistory.com
그렇다면 이 추출된 SQL 파일을 그대로 Flyway의 첫 번째 스크립트인 V1__init.sql로 사용해도 될까요? 결론부터 말씀드리면 '절대 안 됩니다'.
하이버네이트(Hibernate)의 ddl-auto: update 기능은 개발 초기엔 마법처럼 편리하지만, 실무 운영 환경의 기준에는 한참 미치지 못하는 엉성한 뼈대를 만들어냅니다. 오늘은 추출된 원본 스크립트를 분석하며 발견한 5가지 치명적인 문제점을 짚어보고, 이를 실무 표준에 맞게 정제하여 완벽한 V1 스크립트를 완성하는 과정을 공유해 보겠습니다.
2. 하이버네이트 자동 생성 스키마의 5가지 문제점
원시 덤프 파일을 열어 분석해 보니, 크게 5가지의 정제 대상이 발견되었습니다.
첫째, 불필요한 pg_dump 메타 정보
파일 상단과 중간중간에 \restrict, SET 명령, SELECT pg_catalog.set_config(...) 등 PostgreSQL 백업 복원을 위한 메타 정보와 주석들이 섞여 있었습니다. 환경 독립적으로 동작해야 하는 Flyway 마이그레이션 스크립트에는 전혀 필요 없는 구문이므로 과감하게 모두 제거했습니다.
둘째, 해독 불가한 제약조건(CONSTRAINT) 이름
이 부분이 운영 환경에서 가장 큰 골칫거리가 됩니다. 하이버네이트가 자동으로 만들어준 외래 키(FK)나 유니크 키(UK)의 이름은 다음과 같았습니다.
- uk6dotkott2kjsp8vw4d0m25fb7 (users 테이블의 email 유니크 키)
- fk12m0f88r4s6xye0k637svvnkv (외래 키)
만약 운영 중 데이터 무결성 에러가 발생해서 로그에 fk12m0... 제약조건 위반이 찍혔다고 상상해 보세요. 어떤 테이블의 무슨 컬럼 때문에 발생한 에러인지 찾기 위해 데이터베이스 툴을 켜고 스키마를 뒤져야 하는 디버깅 지옥이 열립니다. 이 의미 없는 해시값 이름들을 모두 uk_users_email, fk_reviews_user_id 처럼 직관적인 명명 규칙으로 전부 수정했습니다.
셋째, 누락된 외래 키(FK) 인덱스 (가장 치명적)
놀랍게도 하이버네이트는 외래 키(FK) 컬럼에 대해 데이터베이스 인덱스를 자동으로 만들어주지 않습니다. 이대로 운영에 배포하면 두 가지 큰 문제가 발생합니다.
- 조인(JOIN) 성능 저하: 자식 테이블을 user_id = ? 조건으로 조회할 때 인덱스가 없으므로 풀 테이블 스캔(Full Table Scan)이 발생합니다.
- 부모 데이터 삭제/수정 시 락(Lock) 지연: 부모 테이블의 데이터를 삭제할 때, 참조하고 있는 자식 데이터가 있는지 확인해야 합니다. 이때 인덱스가 없으면 자식 테이블 전체를 스캔하는 동안 ROW SHARE 락이 걸리며 데이터베이스 대기열이 폭증할 위험이 있습니다. 누락된 14개의 FK 컬럼을 꼼꼼히 찾아 파일 하단에 CREATE INDEX 구문을 명시적으로 추가했습니다.
넷째, 의미 없는 중복 인덱스
일부 컬럼에 명시적인 인덱스(idx_users_email)와 유니크 제약조건(uk_users_email)이 동시에 걸려 있었습니다. PostgreSQL을 포함한 대부분의 RDBMS는 PRIMARY KEY와 UNIQUE 제약조건을 생성하면 내부적으로 자동으로 인덱스를 만듭니다. 중복 인덱스는 디스크 공간만 낭비하고 INSERT/UPDATE 시 성능을 떨어뜨리므로 깔끔하게 제거했습니다.
다섯째, 기본 키(PK) 명명 오류
local_food_statistics 테이블의 기본 키 이름이 uk_local_food_type으로 되어 있었습니다. PK인데 이름이 UK(Unique Key)로 시작하는 명백한 명명 오류였습니다. 이를 올바르게 정규화했습니다.
3. 정제 과정에서 얻은 데이터베이스 학습 포인트
Q. 왜 ID 컬럼에 SERIAL 대신 IDENTITY를 썼을까? 수정된 스크립트를 보면 id BIGINT GENERATED BY DEFAULT AS IDENTITY라는 구문이 있습니다. 과거에는 BIGSERIAL을 많이 썼지만, 이는 PostgreSQL만의 고유 단축 문법입니다. 반면 IDENTITY는 SQL 표준 문법이므로 이식성이 좋고 의미가 명확합니다. 최신 하이버네이트가 기본적으로 이 방식을 채택하는 이유이기도 합니다.
Q. MySQL은 FK 인덱스를 자동 생성하던데, PostgreSQL은 왜 아닐까? 데이터베이스마다 철학이 다릅니다. MySQL(InnoDB)은 외래 키를 생성하면 인덱스를 강제로 자동 생성하지만, PostgreSQL은 '개발자가 성능을 고려해 필요할 때 명시적으로 추가하라'는 철학을 가지고 있어 자동 생성하지 않습니다. 따라서 PostgreSQL을 사용할 때는 FK 인덱스 누락 여부를 반드시 교차 검증해야 합니다.
Q. CHECK 제약 조건은 왜 남겨두었을까? auth_provider IN ('EMAIL', 'KAKAO')와 같은 CHECK 제약 조건은 자바 코드의 JPA @Enumerated (Enum 클래스)와 강하게 결합되어 있습니다. 향후 Enum 값이 추가될 때마다 DB 마이그레이션 스크립트도 함께 추가해 주어야 하는 관리 비용이 발생하지만, 데이터베이스 레벨에서 잘못된 값이 들어오는 것을 원천 차단하는 정합성 보장의 이점이 훨씬 크기 때문에 그대로 유지했습니다.
4. V1__init.sql 저장
위의 모든 고민과 수정을 거쳐 완성된 초기 스키마 스크립트 작성하여 프로젝트의 src/main/resources/db/migration/V1__init.sql 경로에 생성하여 저장해 줍니다.
'DevOps > Flyway' 카테고리의 다른 글
| Flyway 실전 도입기 4편: Spring Boot 연동부터 마이그레이션 검증까지 완벽 가이드 (0) | 2026.05.20 |
|---|---|
| Flyway 실전 도입기 2편: pg_dump로 순수 스키마 SQL 추출하기 (0) | 2026.05.20 |
| Flyway 실전 도입기 1편: 로컬 DB 초기화 및 V1 스키마 생성하기 (0) | 2026.05.20 |
| Flyway 도입 딜레마! 테스트 환경 H2와 PostgreSQL 충돌 해결 과정 (0) | 2026.05.20 |