[TIG] @Where 과 nativeQuery

JEONG KI MIN·2024년 7월 23일

TIG

목록 보기
1/12

Soft Delete 적용

우리팀에서는 논리적 삭제 적용을 위해 soft delete를 사용했다. 위시리스트를 예를들어 보겠다.

@Getter
@Setter
@Entity
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@Where(clause = "is_deleted = false")
@Table(name = "wishlist", uniqueConstraints = {@UniqueConstraint(columnNames = {"member_id", "club_id"})})
public class Wishlist extends BaseTimeEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "wishlist_id")
    private Long id;

    @Builder.Default
    private boolean isDeleted = Boolean.FALSE;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "member_id")
    private Member member;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "club_id")
    private Club club;
}

Wishlist 엔티티에 isDeleted 필드를 두어 DB에서 실제로 삭제하지 않고 해당 필드를 업데이트 하는 식으로 구현했다.

따라서 wishlist에 추가 후, 삭제 로직을 실행하면 isDeleted 필드가 true로 된다.

@Where(clause = "is_deleted = false") 코드에 의해 wishlist 관련 query 문법은 모두 is_deleted 가 false 인 row에 대해서만 수행이 된다.

삭제 후 다시 추가?

삭제 후 다시 추가 로직을 실행하면 중복을 검증하는 과정에서 에러가 발생한다.

@Table(name = "wishlist", uniqueConstraints = {@UniqueConstraint(columnNames = {"member_id", "club_id"})})

이 부분은 해당 테이블에 member_idclub_id의 조합이 중복되는 것을 막는 코드이다.

따라서 1번 사용자가 1번 클럽에 대해 위시리스트를 추가 했다가 삭제 후(soft delete 이므로 DB에는 여전히 남아있음) 다시 추가 하려고 할때 문제가 생긴다!.

리팩토링

@Transactional
public void addWishlist(Long clubId, Long memberId) {
    try {
        ClubResponse clubResponse = clubService.getClubById(clubId); // 있는 클럽인지 검사
        Club club = clubMapper.responseToEntity(clubResponse); // createdAt, updatedAt은 추가해야함.

        MemberResponse memberResponse = memberService.getMemberById(memberId); // 있는 멤버인지 검사
        Member member = memberMapper.responseToEntity(memberResponse); // createdAt, updatedAt은 추가해야함.

        // 삭제된 애들 & 삭제 안된 애들 둘다 조회됨
        Optional<Wishlist> existingWishlist = wishlistRepository.findAllByClubIdAndMemberId(clubId, memberId);

        if (existingWishlist.isEmpty()) {
            WishlistRequest request = WishlistRequest.builder()
                    .createdAt(LocalDateTime.now())
                    .club(club)
                    .member(member)
                    .build();

            Wishlist wishlist = wishlistMapper.requestToEntity(request);
            wishlistRepository.save(wishlist);
        } else {
            Wishlist wishlist = existingWishlist.get();
            if (wishlist.isDeleted()) {
                wishlistRepository.restoreWishlist(clubId, memberId);
            } else {
                throw new BusinessExceptionHandler("이미 위시리스트에 존재하는 항목입니다.", ErrorCode.BAD_REQUEST_ERROR);
            }
        }
    } catch (Exception e){
        throw new BusinessExceptionHandler("위시리스트에 추가 하는 과정에서 에러 : " + e.getMessage(), ErrorCode.IO_ERROR);
    }
}

위와 같이 코드를 수정하려 했다.
근데
wishlistRepository.findAllByClubIdAndMemberId(clubId, memberId);
이 로직에서 자동으로 @Where 이 적용되어 isDeleted가 false 인 row 에 대해서만 검색이 진행 되었다.

해결방법을 찾아본 결과...! nativeQuery를 사용하는 것이었다. JPQL이 아닌 순수 SQL을 사용하면 @Where 가 적용되지 않는다.

@Query(value = "SELECT * FROM wishlist WHERE club_id = :clubId AND member_id = :memberId", nativeQuery = true)
Optional<Wishlist> findAllByClubIdAndMemberId(@Param("clubId") Long clubId, @Param("memberId") Long memberId);

위와 같이 작성하면 isDeleted 에 상관없이 모든 row 에 대해 검색을 진행한다.

결론

찾아보니 실무에서는 soft delete 를 막 엄청 많이 사용하지는 않는 느낌이었다. 이러한 이슈가 있는 것도 한 몫을 하지 않을까 생각했다.
결론적으로는 @Where 를 적용하고 싶지 않은 쿼리에 대해서는 nativeQuery를 사용하자!

profile
열심히 해볼게요

0개의 댓글