Spring Boot와 JPA를 사용하는 프로젝트에서 테이블 생성은 보통 두 가지 방식 중 하나로 이루어집니다.
@Entity 어노테이션을 통해 JPA가 테이블을 자동 생성data.sql 또는 schema.sql 파일을 통해 SQL 문으로 직접 테이블을 생성그런데 이 두 방식이 동시에 사용될 경우, 누가 테이블 생성을 먼저 담당하게 될까요?
만약 둘 다 같은 테이블을 정의하고 있다면 충돌이 발생하지 않을까요?
이번 글에서는 @Entity와 xxx.sql 간의 우선순위, 그리고 충돌 시 실제로 어떤 일이 벌어지는지를 정리해보겠습니다.
Spring Boot 기본 설정에서는 Hibernate(JPA)가 테이블 생성을 먼저 수행하고, 그 이후에 xxx.sql 파일이 실행됩니다.
즉, @Entity가 먼저 실행되고 xxx.sql은 그 다음입니다.
@Entity와 Hibernate의 테이블 생성JPA는 @Entity로 정의된 클래스를 기반으로 테이블을 자동으로 생성하거나 업데이트합니다.
Spring Boot에서는 아래 설정을 통해 이 동작을 제어합니다:
spring.jpa.hibernate.ddl-auto=create | update | none | validate | create-drop
create: 애플리케이션 시작 시 테이블을 새로 생성update: 기존 스키마를 유지하면서 엔티티 변경사항만 반영validate: 스키마를 검증만 하고 생성/수정은 하지 않음none: Hibernate는 아무런 스키마 작업을 하지 않음create-drop: 시작 시 생성, 종료 시 삭제xxx.sql (예: data.sql, schema.sql)Spring Boot는 애플리케이션 시작 시 SQL 스크립트를 자동으로 실행합니다.
기본적으로 data.sql은 Hibernate 이후에 실행됩니다.
즉, @Entity를 통해 Hibernate가 먼저 테이블을 만들고,
그 뒤에 data.sql이 실행되어 데이터를 넣거나, 추가적인 작업을 수행하는 구조입니다.
// Java
@Entity
public class Member {
;;
}
-- data.sql
CREATE TABLE member (...);
Hibernate가 먼저 member 테이블을 만들고 나면, data.sql의 CREATE TABLE 문은 이미 존재하는 테이블을 다시 만들려고 하기 때문에 오류가 발생합니다.
DB에 따라 "table already exists" 에러가 날 수 있습니다.
-- data.sql
INSERT INTO member (id, name) VALUES (1, '홍길동');
이 경우는 정상적으로 작동합니다. Hibernate가 먼저 테이블을 만들고, data.sql이 초기 데이터를 넣습니다.
spring.jpa.hibernate.ddl-auto=none
data.sql 또는 schema.sql에서 모든 테이블을 수동으로 작성해야 함xxx.sql 실행을 막고 싶다면spring.sql.init.mode=never
data.sql, schema.sql 실행을 막음spring.jpa.hibernate.ddl-auto=update
spring.sql.init.mode=always
data.sql은 초기 데이터만 삽입 (CREATE TABLE은 피해야 함)스키마 생성은 Hibernate(@Entity)에 맡기자
→ Java 코드와 DB 스키마가 일관성을 가질 수 있음
데이터 초기화는 data.sql에 맡기자
→ INSERT만 넣고, 테이블 생성은 피하자
DDL은 한 쪽에서만 관리하자
→ Hibernate와 SQL 스크립트가 동시에 CREATE TABLE 하면 충돌 발생 가능성 큼
Spring Boot에서는 보통 Hibernate가 테이블 생성을 먼저 하고, xxx.sql이 뒤따라 실행됩니다.
이 순서를 이해하지 못하면 table already exists 같은 에러를 만나게 됩니다.
정리하자면:
| 목적 | 권장 방식 |
|---|---|
| 테이블 생성 | @Entity + spring.jpa.hibernate.ddl-auto 설정 |
| 데이터 삽입 | data.sql 사용, 단 INSERT만 포함해야 함 |
| SQL로만 전체 관리 | ddl-auto=none, SQL 파일로 CREATE 포함 |
| SQL 완전 무시 | spring.sql.init.mode=never 설정 |
상황에 맞게 역할을 분리하고 충돌을 방지하는 설계를 해야할 것 같습니다.