Spring

[트러블슈팅] @ColumnDefault() 사용 시 DB에 null로 저장되는 이유와 해결방법

11dy 2024. 2. 8. 21:52

개발환경

- java17

- spring boot 3.2.0

- spring data JPA

- Gradle 8.5

- MySQL

 

진행 중인 프로젝트에서는 회원의 탈퇴 여부를 soft delete로 구현했습니다. 서버가 클라이언트로부터 회원탈퇴 요청을 전달받으면, 실제로 테이블에서 해당 회원을 삭제하지 않고 boolean 타입의 deleted 칼럼으로 삭제 여부를 저장했습니다.

@Column(name = "deleted")
@ColumnDefault("false")
private Boolean deleted;

 

또한 회원과 관련된 sql 생성 시 where절을 통해 deleted 칼럼이 0(=false)인 경우만 조회할 수 있도록 select 조건을 제한했습니다. 

@SQLDelete(sql = "UPDATE member SET deleted = true, deleted_at = CURRENT_TIMESTAMP WHERE MEMBER_ID = ?")
@Where(clause = "deleted = false")

 

문제

@ColumnDefault에 문제가 있다는 것은 로그인 과정에서 알 수 있었습니다. 아래 메서드는 UserDetailsService의 loaduserByUsername을 재정의 한 메서드입니다. 로그인 요청으로 전달받은 dto에서 username (=email) 값을 이용해 회원 정보를 조회합니다. 문제는 이 부분에서 발생했습니다.  

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException{
    Optional<Member> optionalMember = memberRepository.findByEmail(username);
    Member findMember = optionalMember.orElseThrow(() -> new BusinessLogicException(ExceptionCode.MEMBER_NOT_FOUND));

    return new MemberDetails(findMember);// db에서 조회한 객체를 리턴해 인증절차 수행
}
  • 이메일을 통해 정상적인 조회가 불가능했습니다. DB에 해당 회원 데이터가 존재했지만, 디버깅 시 optionalMember에 null 값이 담겼습니다. 
  • DB를 확인해보니 deleted 칼럼이 null로 저장되어 있었습니다. 회원가입 시 @ColumnDefault을 사용했기 때문에 deleted 칼럼이 false로 초기화될 것이라 생각했는데, 자동으로 값이 설정되지 않았습니다. 

 

원인은 이렇습니다. 

  • hibernate 특성 상, 테이블 속성이 create-drop인 경우만 default 쿼리가 날아갑니다. 저는 create를 통해서 테이블을 최신화했기 때문에, deleted의 초기값이 null로 설정되었습니다.
  • MySQL에서 null은 '값'으로 인식됩니다. default값은 아무 값도 insert 되지 않은 칼럼에 대해 적용이 됩니다. 때문에 null이 들어온다면 'null' 값 자체를 저장합니다. (https://dev.mysql.com/doc/refman/8.0/en/null-values.html)
  • 이런 이유로 deleted 컬럼에  null 값이 들어갔습니다. 

 

시도 및 해결 

다행히 잘 정리된 레퍼런스를 찾아 문제를 빠르게 해결할 수 있었습니다. https://sienna1022.tistory.com/entry/%ED%94%8C%EB%A1%9C%EB%8B%88-default-%EC%84%A4%EC%A0%95%EC%9D%84-%ED%96%88%EB%8A%94%EB%8D%B0-null%EC%9D%B4-%EB%82%98%EC%99%80%EC%9A%94

 

[플로니] default 설정을 했는데 null이 나와요

최근 시작한 프로젝트에서, Entity의 column값을 default값으로 설정하고픈 일이 굉장히 많아졌다. 예를 들면, 논리적 삭제를 구현할 때에 status를 boolean값으로 default는 True를 지정해주어야 했다. 그런

sienna1022.tistory.com

 

저는 위 레퍼런스에서 제안한 두 번째 방법인 @DynamicInsert 어노테이션을 사용해 문제를 해결했습니다. Dynamic Insert는 쿼리가 날아갈 때 null인 구문을 제외하고 쿼리를 날립니다. 단순히 엔티티 클래스에 해당 어노테이션을 추가해서 문제를 해결할 수 있었습니다. 

create table member (
        deleted bit default false,
        created_date datetime(6),
        deleted_at datetime(6),

정상적으로 초기값이 false로 설정되는 것을 확인했습니다. 

 

 

 

참고 

https://eocoding.tistory.com/71

 

JPA @ColumnDefault에 대한 오해, 컬럼 default 적용하기, @ColumnDefault not working 해결하기, @DynamicInsert

회원가입 API를 개발하던 중, @ColumnDefault가 작동되지 않는 문제를 직면했었다. 어제 내 시간을 뺏어가버린...... 현재 상황 : 회원가입 API에서 request DTO에서는 email, password만 받고, User의 또다른 컬

eocoding.tistory.com

https://giron.tistory.com/128 

 

[JPA] hibernate.ddl-auto 설정

hibernate.ddl-auto 옵션은 Entity객체를 참고하여 애플리케이션 실행 시점에 하이버네이트에서 자동으로 DDL을 만들어주는 옵션이다. JPA 테스트 도중 fk 제약조건이 걸린 엔티티를 생성할 때, 아래 설

giron.tistory.com

https://colabear754.tistory.com/136

 

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

목차 개요 JPA의 구현체 중 하나인 하이버네이트는 다양한 기능을 제공하는데 그 중 엔티티만 등록해놓으면 DDL(Data Definition Language)을 자동으로 작성하여 테이블을 생성하거나 수정해주는 ddl-auto

colabear754.tistory.com