문제 상황
논리 삭제된 데이터가 조회되지 않음
Spring Boot와 JPA를 사용해 논리 삭제(Soft Delete) 기능을 구현하던 중 예상치 못한 이슈가 발생했습니다.
`isDeleted = true` 조건으로 논리 삭제된 데이터를 조회하려 했으나,
Hibernate의 `@Where` 애노테이션이 모든 조회 쿼리에 `isDeleted = false` 조건을 강제 적용하면서
원하는 데이터를 가져오지 못하는 상황이 발생했습니다.
SQL 로그 확인 결과
SELECT * FROM user u WHERE u.is_deleted = false;
쿼리를 `isDeleted = true` 조건으로 작성했음에도 `is_deleted = false` 조건이 추가되어 논리 삭제된 사용자를 조회할 수 없었습니다.
문제 원인 분석
이 문제는 Hibernate의 `@Where` 애노테이션과 JPQL의 기본 필터 조건 적용 방식의 충돌로 발생했습니다.
1️⃣ @Where 애노테이션
`@Where(clause = "is_deleted = false")`는 모든 JPQL 및 기본 조회 쿼리에 자동으로 적용됩니다.
`findAllByIsDeletedTrue()` 메서드에서도 `is_deleted = false` 조건이 우선 적용되어 원하는 데이터를 조회할 수 없습니다.
2️⃣ Boolean 필드 매핑 이슈
Boolean 타입 필드가 데이터베이스의 `TINYINT(0, 1)`로 매핑되며 필터링 조건이 부정확하게 적용될 수 있습니다.
해결 방법
1️⃣ Native Query 사용
`@Where` 애노테이션이 모든 JPQL 조회에 기본 필터를 적용하기 때문에,
특정 상황에서는 `isDeleted = true` 조건으로 논리 삭제된 데이터를 조회하는 데 어려움이 발생합니다.
이때 Native SQL Query를 사용하면 `@Where` 필터 조건을 우회하여 정확한 데이터를 조회할 수 있습니다.
(1) Repository 클래스 수정
// 삭제 요청된 사용자 조회
@Query(value = "SELECT * FROM user WHERE is_deleted = true", nativeQuery = true)
List<User> findAllByIsDeletedTrue();
2️⃣ UserService 코드 수정
UserService에서 findAllSoftDeletedUsers() 메서드를 호출해 DTO로 변환합니다.
(2) Service 클래스 수정
// 논리 삭제된 사용자 조회
public List<UserResponseDto> getDeletedUsers() {
return userRepository.findAllByIsDeletedTrue().stream()
.map(UserResponseDto::toDto)
.toList();
}
Spring Boot와 Hibernate의 논리 삭제 기능을 구현할 때,
`@Where` 애노테이션과 JPQL 필터 조건 충돌을 주의해야 합니다.
일반 조회에는 @Where로 필터링을 유지하고,
논리 삭제된 데이터를 조회할 때는 Native Query를 사용해 조건을 명확히 지정하는 것이 가장 안전합니다.
이러한 트러블슈팅 경험을 통해 데이터 무결성을 유지하면서도 효율적으로 데이터를 관리할 수 있는 시스템을 설계할 수 있습니다.