Hibernate ddl-auto
TL;DR
- Hibernate ddl-auto의 핵심 개념과 사용 범위를 한눈에 정리
- 등장 배경과 필요한 이유를 짚고 실무 적용 포인트를 연결
- 주요 특징과 체크리스트를 빠르게 확인
1. 개념
``` DDL = Data Definition Language └── CREATE, ALTER, DROP 등 테이블 구조를 정의하는 SQL
2. 배경
Hibernate ddl-auto이(가) 등장한 배경과 기존 한계를 정리한다.
3. 이유
이 주제를 이해하고 적용해야 하는 이유를 정리한다.
4. 특징
- 약자/이름
- 전통적인 개발 방식의 문제
- Hibernate의 해결책
- 설정 방법
- 옵션별 동작
5. 상세 내용
작성일: 2026-01-30 카테고리: Backend / Java / JPA / Hibernate 포함 내용: ddl-auto, 스키마 자동 생성, update, validate, create, Flyway, Liquibase, 마이그레이션
1. ddl-auto란?
약자/이름
DDL = Data Definition Language
└── CREATE, ALTER, DROP 등 테이블 구조를 정의하는 SQL
ddl-auto = DDL 자동화
└── Hibernate가 Java 엔티티 기준으로 DB 스키마를 자동 관리
설정 위치: application.yml의 spring.jpa.hibernate.ddl-auto
2. 등장 배경
전통적인 개발 방식의 문제
┌─────────────────────────────────────────────────────────────────┐
│ 전통적인 개발 방식 (수동) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1단계: DBA가 SQL로 테이블 생성 │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ CREATE TABLE users ( │ │
│ │ id BIGINT PRIMARY KEY, │ │
│ │ name VARCHAR(100), │ │
│ │ email VARCHAR(255) │ │
│ │ ); │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 2단계: 개발자가 Java 클래스 작성 │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ public class User { │ │
│ │ private Long id; │ │
│ │ private String name; │ │
│ │ private String email; │ │
│ │ } │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 문제점: │
│ ├── 테이블과 클래스를 "두 번" 정의 (중복 작업) │
│ ├── 필드 추가 시 SQL도 수정, 클래스도 수정 │
│ ├── 오타나 불일치 발생 가능 │
│ ├── 개발자가 SQL을 잘 모를 수 있음 │
│ └── 환경마다 (로컬, 개발, 스테이징) 스키마 동기화 어려움 │
│ │
└─────────────────────────────────────────────────────────────────┘
Hibernate의 해결책
┌─────────────────────────────────────────────────────────────────┐
│ Hibernate의 아이디어 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ "Java 클래스만 정의하면, DB 테이블은 자동으로 만들어주지!" │
│ │
│ 핵심 철학: "코드가 진실의 원천(Single Source of Truth)" │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ @Entity │ │
│ │ @Table(name = "users") │ │
│ │ public class User { │ │
│ │ │ │
│ │ @Id │ │
│ │ @GeneratedValue(strategy = GenerationType.IDENTITY) │ │
│ │ private Long id; │ │
│ │ │ │
│ │ @Column(length = 100) │ │
│ │ private String name; │ │
│ │ │ │
│ │ @Column(length = 255, unique = true) │ │
│ │ private String email; │ │
│ │ } │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ Hibernate가 자동 생성: │ │
│ │ │ │
│ │ CREATE TABLE users ( │ │
│ │ id BIGINT AUTO_INCREMENT PRIMARY KEY, │ │
│ │ name VARCHAR(100), │ │
│ │ email VARCHAR(255) UNIQUE │ │
│ │ ); │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 장점: │
│ ├── SQL 몰라도 테이블 생성 가능 │
│ ├── 한 곳만 수정하면 됨 (Java 클래스) │
│ ├── 개발 속도 향상 │
│ └── 오타/불일치 방지 │
│ │
└─────────────────────────────────────────────────────────────────┘
3. ddl-auto 옵션 종류
설정 방법
# application.yml (Spring Boot)
spring:
jpa:
hibernate:
ddl-auto: update # ← 여기서 설정
옵션별 동작
┌─────────────────────────────────────────────────────────────────┐
│ ddl-auto 옵션 종류 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┬────────────────────────────────────────────────┐ │
│ │ 옵션 │ 동작 │ │
│ ├──────────┼────────────────────────────────────────────────┤ │
│ │ none │ 아무것도 안 함 (수동 관리) │ │
│ │ │ → 프로덕션 권장 │ │
│ │ │ → Hibernate가 스키마에 손대지 않음 │ │
│ ├──────────┼────────────────────────────────────────────────┤ │
│ │ validate│ 엔티티와 테이블이 맞는지 검증만 │ │
│ │ │ → 불일치 시 앱 시작 실패 (에러) │ │
│ │ │ → 스키마 수정 안 함 │ │
│ │ │ → 프로덕션에서 안전하게 검증용 │ │
│ ├──────────┼────────────────────────────────────────────────┤ │
│ │ update │ 엔티티 기준으로 테이블 변경 ⭐ │ │
│ │ │ → 새 컬럼 추가 O │ │
│ │ │ → 컬럼 삭제 X (안전장치) │ │
│ │ │ → 타입 변경 X (안전장치) │ │
│ │ │ → 개발 환경에서 편리 │ │
│ ├──────────┼────────────────────────────────────────────────┤ │
│ │ create │ 앱 시작 시 테이블 삭제(DROP) 후 새로 생성 │ │
│ │ │ → ⚠️ 기존 데이터 모두 삭제됨! │ │
│ │ │ → 테스트용 │ │
│ ├──────────┼────────────────────────────────────────────────┤ │
│ │create-drop│ create + 앱 종료 시 테이블 삭제 │ │
│ │ │ → 앱 끄면 테이블도 사라짐 │ │
│ │ │ → 인메모리 DB 테스트용 (H2) │ │
│ └──────────┴────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
옵션 비교 다이어그램
┌─────────────────────────────────────────────────────────────────┐
│ 옵션별 동작 비교 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 앱 시작 시: │
│ │
│ none ─────────────────────────────────────→ 아무것도 안함 │
│ │
│ validate ───[엔티티↔DB 비교]───┬─ 일치 ─→ 정상 시작 │
│ └─ 불일치 → 에러! 앱 종료 │
│ │
│ update ───[엔티티↔DB 비교]───┬─ 일치 ─→ 정상 시작 │
│ └─ 차이 → ALTER TABLE 실행 │
│ │
│ create ───[DROP TABLE]───[CREATE TABLE]───→ 새 테이블 │
│ ⚠️ 데이터 삭제! │
│ │
│ create-drop ──[DROP]──[CREATE]─── 앱 실행 ───[앱 종료시 DROP] │
│ │
└─────────────────────────────────────────────────────────────────┘
4. update 상세 동작
필드 추가 시나리오
┌─────────────────────────────────────────────────────────────────┐
│ ddl-auto: update 동작 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 시나리오: 엔티티에 phone 필드 추가 │
│ │
│ [Before] │
│ ┌────────────────────┐ ┌────────────────────┐ │
│ │ User.java │ │ users 테이블 │ │
│ │ - id │ │ - id │ │
│ │ - name │ │ - name │ │
│ │ - email │ │ - email │ │
│ └────────────────────┘ └────────────────────┘ │
│ │
│ [개발자 작업] 필드 추가 │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ @Entity │ │
│ │ public class User { │ │
│ │ // 기존 필드들... │ │
│ │ │ │
│ │ @Column(length = 20) │ │
│ │ private String phone; // ← 새 필드 추가 │ │
│ │ } │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ [앱 재시작 시 Hibernate 동작] │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ 1. DB 메타데이터 조회 (현재 테이블 구조 확인) │ │
│ │ 2. 엔티티 클래스 분석 │ │
│ │ 3. 차이점 발견: phone 컬럼 없음 │ │
│ │ 4. DDL 생성 및 실행: │ │
│ │ │ │
│ │ ALTER TABLE users ADD COLUMN phone VARCHAR(20); │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ [After] │
│ ┌────────────────────┐ ┌────────────────────┐ │
│ │ User.java │ │ users 테이블 │ │
│ │ - id │ │ - id │ │
│ │ - name │ │ - name │ │
│ │ - email │ │ - email │ │
│ │ - phone ✅ │ │ - phone ✅ │ │
│ └────────────────────┘ └────────────────────┘ │
│ │
│ 개발자 입장: │
│ → Java 코드만 수정하면 끝! │
│ → SQL 작성 불필요 │
│ → 앱 재시작만 하면 자동 반영 │
│ │
└─────────────────────────────────────────────────────────────────┘
update의 한계 (중요!)
┌─────────────────────────────────────────────────────────────────┐
│ update가 안 되는 것들 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ❌ 1. 컬럼 삭제 │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ // 엔티티에서 필드를 삭제해도 │ │
│ │ // DB 컬럼은 그대로 남아있음 │ │
│ │ │ │
│ │ 이유: 데이터 손실 방지를 위한 안전장치 │ │
│ │ 해결: 수동으로 ALTER TABLE DROP COLUMN 실행 │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ ❌ 2. 컬럼 타입 변경 │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ // String → Integer 변경해도 │ │
│ │ // DB는 VARCHAR 그대로 │ │
│ │ │ │
│ │ 이유: 기존 데이터 변환 실패 가능 │ │
│ │ 예: "hello" → Integer로 변환 불가 │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ ❌ 3. 컬럼 이름 변경 │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ // name → fullName 으로 필드명 변경하면 │ │
│ │ // Hibernate는 "새 컬럼 추가"로 인식 │ │
│ │ │ │
│ │ 결과: │ │
│ │ - name 컬럼 그대로 (삭제 안 됨) │ │
│ │ - fullName 컬럼 추가 │ │
│ │ - 기존 데이터는 name에 남아있음 │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ ❌ 4. 제약조건 변경 │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ // @Column(nullable = false) 추가해도 │ │
│ │ // 기존에 NULL 데이터가 있으면 실패 │ │
│ │ │ │
│ │ // @Column(length = 50) → length = 100 변경 │ │
│ │ // 적용 안 될 수 있음 (DB 종류에 따라 다름) │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ 결론: │ │
│ │ update는 "추가"만 잘 됨 │ │
│ │ "변경/삭제"는 안전하게 막혀있음 │ │
│ │ → 복잡한 변경은 수동 마이그레이션 필요 │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
5. 환경별 권장 설정
┌─────────────────────────────────────────────────────────────────┐
│ 환경별 권장 ddl-auto 설정 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┬─────────────┬──────────────────────────────┐ │
│ │ 환경 │ ddl-auto │ 이유 │ │
│ ├─────────────┼─────────────┼──────────────────────────────┤ │
│ │ 로컬 개발 │ update │ 빠른 개발, 자동 스키마 변경 │ │
│ │ │ 또는 create│ (매번 초기화해도 OK) │ │
│ ├─────────────┼─────────────┼──────────────────────────────┤ │
│ │ 테스트 │ create-drop│ 매 테스트마다 깨끗한 DB │ │
│ │ (CI/CD) │ │ H2 인메모리 DB와 함께 │ │
│ ├─────────────┼─────────────┼──────────────────────────────┤ │
│ │ 스테이징 │ validate │ 스키마 검증만 │ │
│ │ │ │ 수동 마이그레이션 사용 │ │
│ ├─────────────┼─────────────┼──────────────────────────────┤ │
│ │ 프로덕션 │ none │ 절대 자동 변경 금지! │ │
│ │ │ 또는 │ Flyway/Liquibase 사용 │ │
│ │ │ validate │ │ │
│ └─────────────┴─────────────┴──────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
프로덕션에서 update 쓰면 안 되는 이유
┌─────────────────────────────────────────────────────────────────┐
│ ⚠️ 프로덕션에서 update 금지 이유 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. 예측 불가능한 스키마 변경 │
│ └── 개발자가 실수로 필드 추가하면 즉시 ALTER 실행 │
│ └── 코드 리뷰 없이 DB 변경됨 │
│ │
│ 2. 롤백 불가능 │
│ └── 배포 실패 시 코드는 롤백해도 │
│ └── 이미 추가된 컬럼은 그대로 │
│ │
│ 3. 다운타임 발생 가능 │
│ └── 대용량 테이블 ALTER는 락(Lock) 발생 │
│ └── 수백만 행 테이블 → 수 분~수 시간 락 │
│ └── 서비스 장애로 이어짐 │
│ │
│ 4. 감사(Audit) 추적 불가 │
│ └── 누가 언제 무슨 변경을 했는지 기록 없음 │
│ └── 보안/컴플라이언스 문제 │
│ │
│ 5. 동시 배포 문제 │
│ └── 여러 서버가 동시에 시작하면 │
│ └── 같은 ALTER 여러 번 실행 시도 │
│ └── 충돌/에러 발생 가능 │
│ │
└─────────────────────────────────────────────────────────────────┘
6. 프로덕션: 마이그레이션 도구 사용
Flyway 소개
┌─────────────────────────────────────────────────────────────────┐
│ Flyway - DB 마이그레이션 도구 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 개념: SQL 파일로 스키마 변경을 버전 관리 │
│ │
│ resources/db/migration/ │
│ ├── V1__create_users_table.sql # 버전 1 │
│ ├── V2__add_phone_column.sql # 버전 2 │
│ ├── V3__add_index_on_email.sql # 버전 3 │
│ └── V4__create_orders_table.sql # 버전 4 │
│ │
│ 파일명 규칙: │
│ V{버전}__{설명}.sql │
│ └── V는 대문자 │
│ └── 버전은 숫자 │
│ └── __ (언더스코어 2개) │
│ └── 설명은 snake_case │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ -- V2__add_phone_column.sql │ │
│ │ │ │
│ │ ALTER TABLE users ADD COLUMN phone VARCHAR(20); │ │
│ │ CREATE INDEX idx_users_phone ON users(phone); │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 동작: │
│ 1. 앱 시작 시 Flyway가 실행됨 │
│ 2. flyway_schema_history 테이블에서 적용된 버전 확인 │
│ 3. 아직 적용 안 된 SQL 파일만 순서대로 실행 │
│ 4. 실행 기록 저장 │
│ │
└─────────────────────────────────────────────────────────────────┘
Flyway vs ddl-auto 비교
┌─────────────────────────────────────────────────────────────────┐
│ Flyway vs ddl-auto 비교 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┬─────────────────┬─────────────────────┐ │
│ │ 항목 │ ddl-auto │ Flyway │ │
│ ├─────────────────┼─────────────────┼─────────────────────┤ │
│ │ 버전 관리 │ ❌ 없음 │ ✅ Git에 저장 │ │
│ │ 코드 리뷰 │ ❌ 불가 │ ✅ PR로 리뷰 │ │
│ │ 롤백 │ ❌ 불가 │ ✅ 가능 │ │
│ │ 변경 이력 │ ❌ 없음 │ ✅ 테이블에 기록 │ │
│ │ 복잡한 변경 │ ❌ 제한적 │ ✅ SQL 직접 작성 │ │
│ │ 데이터 마이그 │ ❌ 불가 │ ✅ SQL로 가능 │ │
│ │ 개발 편의성 │ ✅ 매우 편함 │ ⚠️ SQL 작성 필요 │ │
│ │ 프로덕션 안정성│ ❌ 위험 │ ✅ 안전 │ │
│ └─────────────────┴─────────────────┴─────────────────────┘ │
│ │
│ 결론: │
│ ├── 개발: ddl-auto: update (편리함) │
│ └── 프로덕션: ddl-auto: none + Flyway (안전함) │
│ │
└─────────────────────────────────────────────────────────────────┘
Spring Boot에서 함께 사용
# application.yml (로컬 개발)
spring:
jpa:
hibernate:
ddl-auto: update
flyway:
enabled: false # 로컬에서는 Flyway 비활성화
---
# application-prod.yml (프로덕션)
spring:
jpa:
hibernate:
ddl-auto: none # 자동 변경 금지
flyway:
enabled: true # Flyway로 마이그레이션
locations: classpath:db/migration
7. 실제 설정 예시
개발 환경
# application-local.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb_dev
username: root
password: password
jpa:
hibernate:
ddl-auto: update # 자동 스키마 업데이트
show-sql: true # SQL 로그 출력
properties:
hibernate:
format_sql: true # SQL 보기 좋게
테스트 환경
# application-test.yml
spring:
datasource:
url: jdbc:h2:mem:testdb # 인메모리 DB
driver-class-name: org.h2.Driver
jpa:
hibernate:
ddl-auto: create-drop # 매번 새로 생성, 종료 시 삭제
프로덕션 환경
# application-prod.yml
spring:
datasource:
url: jdbc:mysql://${DB_HOST}:3306/${DB_NAME}
username: ${DB_USER}
password: ${DB_PASS}
jpa:
hibernate:
ddl-auto: none # 절대 자동 변경 금지!
flyway:
enabled: true
baseline-on-migrate: true
8. 정리
┌─────────────────────────────────────────────────────────────────┐
│ 요약 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ddl-auto란? │
│ └── Hibernate가 Java 엔티티 기준으로 DB 스키마 자동 관리 │
│ │
│ 등장 배경: │
│ ├── 테이블/클래스 중복 정의 문제 해결 │
│ ├── SQL 몰라도 개발 가능 │
│ └── 빠른 프로토타이핑 │
│ │
│ 옵션별 동작: │
│ ├── none : 아무것도 안 함 (프로덕션 권장) │
│ ├── validate : 검증만, 수정 안 함 │
│ ├── update : 추가만 가능, 삭제/변경 불가 (개발 편리) │
│ ├── create : 시작 시 DROP + CREATE (데이터 삭제!) │
│ └── create-drop: create + 종료 시 DROP │
│ │
│ 환경별 권장: │
│ ├── 로컬 개발 : update (편리함) │
│ ├── 테스트 : create-drop (깨끗한 상태) │
│ ├── 스테이징 : validate (검증) │
│ └── 프로덕션 : none + Flyway/Liquibase (안전) │
│ │
│ 핵심 원칙: │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ "개발할 땐 update로 편하게, │ │
│ │ 프로덕션은 none + 마이그레이션 도구로 안전하게" │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
관련 키워드
Hibernate, JPA, ddl-auto, update, validate, create, create-drop, Flyway, Liquibase, 스키마 마이그레이션, Spring Boot, ORM