개발환경
- 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
[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
'Spring' 카테고리의 다른 글
Redis를 이용한 QueryDSL 페이징 조회 성능 향상 (0) | 2024.05.26 |
---|---|
[트러블슈팅] Github Actions + submodule application.yml을 못 읽는 경우 (0) | 2024.04.13 |