내가 ddl-auto를 작성했던 이유

Kevin·2024년 2월 17일
0
post-thumbnail

JPA에는 ddl-auto라는 설정이 있다.

해당 설정은 JPA를 이용해서 데이터베이스를 Initialize 할 수 있게 도와주는 설정이다.

Spring 공식 문서에서는 ddl-auto를 아래와 같이 설명하고 있다.

spring.jpa.hibernate.ddl-auto (enum) is a Hibernate feature that controls the behavior in a more fine-grained way. See below for more detail

위 문장을 해석해보면, ddl-auto (enum)은 동작을 보다 세밀하게 제어하는 Hibernate 기능이라는 의미며 어떤 동작을 세밀하게 제어하는지
ddl-auto의 여러개의 속성들을 통해서, 함께 하나 하나 알아보자.


1. create

jpa:
    hibernate:
      ddl-auto: create
  • 애플리케이션 실행시점에 기존 테이블들을 모두 삭제하고, 다시 생성한다.

2. create-drop

jpa:
    hibernate:
      ddl-auto: create-drop
  • 애플리케이션 종료시점에 기존 테이블들을 모두 삭제하고, 다시 생성한다.

3. update

jpa:
    hibernate:
      ddl-auto: create
  • 애플리케이션 실행시점에 ‘JPA 엔티티 설계’와 ‘실제 테이블 상태’를 비교하여, 실제 테이블에서 변경된 부분만 반영한다.

4. validate

jpa:
    hibernate:
      ddl-auto: validates
  • 애플리케이션 실행시점에 엔티티와 테이블이 정상 매핑 되었는지만 확인한다.

5. none

jpa:
    hibernate:
      ddl-auto: none
  • 사실상 아무 설정도 하지 않은거지만 명시적으로 확인하기 위해서 사용한다.

궁금했던 점

해당 ddl-auto를 위 지식 없이 뇌 빼고 사용하던 중 아래와 같은 질문이 생겨서 카카오 테크 캠퍼스 멘토님께 여쭈어보게 되었다.

질문

현재 배포 profile에서는 ddl-auto를 update로 사용하고 있습니다. 제가 ddl-auto를 더 공부하기 위해서 레퍼런스들을 찾아보던 중에 아래와 같은 말씀을 들었습니다.

spring:
  config.activate.on-profile: prod
  jpa:
    hibernate:
      ddl-auto: update
    properties:
      hibernate:
        show_sql: true
        format_sql: true
  datasource:
    driver-class-name: org.mariadb.jdbc.Driver
    url: jdbc:mariadb://${DB_url}:${DB_port}/${DB_dataBaseName}?characterEncoding=UTF-8&serverTimezone=UTC
    username: ${DB_name}
    password: ${DB_password}

운영 서버에서는 최소한의 검증만을 위해서 ddl-auto를 사용하거나, 아예 사용하지 않아야 한다.

ddl-auto 속성들은 얼핏 보기엔 굉장히 편리해보이지만 validate와 none을 제외한 속성은 운영 DB에는 절대!!! 사용하면 안된다.

update라고 하더라도 문제가 될 수 있는 것이, 만약 update로 인해 새로 추가된 컬럼이 not null이라면 해당 변경사항이 반영되지 않은 버전을 배포하게 되었을 때 테이블에 데이터가 INSERT 되지 않을 수 있다. ← 이 구문이 정확히 어떤 상황을 이야기 하시는 건지가 이해가 안갑니다.

이 문장을 혹시 예시를 들어서 말씀해주실 수 있으실까요?

참고

[JPA] hibernate의 ddl-auto 속성의 종류와 주의해야할 점

멘토님의 답변

새로 추가된 Not null 컬럼에는 필수값이 있어야 하는데 Update시 새롭게 컨텍스트를 로드해서 Update 하려고하는데, 해당 필드에 넣을 데이터가 없어서 Insert가 되지 않는다는 의미입니다.

그렇다면 실제 코드를 통해서 예시를 들면서 위 말씀을 복기해보자.

아래와 같은 엔티티가 하나 있다.

/**
 * Walk(산책) 엔티티
 *
 * @author Kevin
 * @version 1.0
 */
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@Builder
public class Walk {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    @ManyToOne(fetch = FetchType.LAZY)
    private Member walker;

    @ManyToOne(fetch = FetchType.LAZY)
    private Member master;

    @Enumerated(value = EnumType.STRING)
    private WalkStatus walkStatus;

    private LocalDateTime startTime;

    private LocalDateTime endTime;
}

이 때 jpa.hibernate의 ddl-auto를 update로 지정한다고 하고,

  jpa:
    hibernate:
      ddl-auto: update

엔티티 코드를 아래와 같이 변경하고, 다시 애플리케이션을 실행한다고 하자.

/**
 * Walk(산책) 엔티티
 *
 * @author Kevin
 * @version 1.0
 */
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@Builder
public class Walk {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    @ManyToOne(fetch = FetchType.LAZY)
    private Member walker;

    @ManyToOne(fetch = FetchType.LAZY)
    private Member master;

    @Enumerated(value = EnumType.STRING)
    private WalkStatus walkStatus;

    private LocalDateTime startTime;

    private LocalDateTime endTime;

		/* 해당 부분 추가 */
		@Column(nullable = false)
		private String walkReview;
}

산책의 후기를 저장하는 walkReview 필드를 추가했다고 할 때, 기존에 테이블 설계와 JPA 엔티티 설계간 차이가 생겼기에 update 설정으로 인해서 다시 스키마에 데이터들을 insert 한다. 이 때 insert 할 때 walkReview 필드는 NonNull 설정이기 때문에 insert 할 수 없게 된다.

그래서 테이블에 데이터가 들어가지 않는 치명적인 문제가 발생하게 된다.

내가 ddl-auto를 update로 선언했던 이유

매번 애플리케이션을 재배포할 때마다 DB 서버에 존재하던 데이터들을 삭제하는 것이 불편하여서 Update를 사용하였었는데, 카카오 크램폴린으로 배포할 때는 validate로 변경을 해야겠다.

profile
Hello, World! \n

0개의 댓글