[Spring Boot] Soft Delete와 Hibernate 동적 필터링으로 삭제된 데이터 조회하기 (@Where 이슈 해결)

손은실·2024년 8월 13일
0

Spring Boot

목록 보기
4/13
post-thumbnail

Soft 와 Hard

데이터베이스에서 데이터를 삭제할 때는 논리 삭제물리 삭제가 있습니다.

항상 삭제 기능을 물리적으로 구현해왔으나, 이번에는 '최근에 삭제한 값 조회' 기능을 위해 사용자가 삭제한 데이터를 활용해야 했습니다.

그래서 이번 포스팅에서는 논리 삭제(Soft Delete)를 구현하고, 삭제된 데이터를 조회하기 위해 Hibernate의 필터링을 활용했습니다.



Soft Delete (논리적 삭제)

데이터베이스의 테이블에서 물리적으로 삭제하지 않고, 삭제된 것으로 표시하는 업데이트 프로세스를 수행한다.

  • 삭제 여부 칼럼을 생성하지만 실제로 삭제하지 않아 복구 가능
  • 별도의 컬럼을 사용해 삭제 여부 표시
UPDATE table 
SET is_deleted = 1 
WHERE condition;
  • DB에서는 해당 데이터가 삭제된 것으로 인식하지만, 실제 데이터는 보존됨
  • 데이터 복구와 이력 추적이 용이하다.

Hard Delete (물리적 삭제)

delete 쿼리를 사용해 테이블에서 영구적으로 제거하므로, 데이터가 완전히 삭제돼 복구가 불가능하다.

  • 삭제 대상인 데이터가 추후에 사용되지 않을 때


🟢 Soft Delete 구현

개발 환경

  • Spring Boot 3
  • Spring Data JPA
  • Hibernate 6
  • MySQL

해당 포스팅의 실제 코드는 EunsilSon - BookMarky에서 확인할 수 있습니다.



1. flag 전용 컬럼 생성

💡 삭제된 데이터임을 표시하는 flag 용도로 별도의 컬럼을 추가 생성해야 합니다.

  • isDeleted 이름의 Boolean 타입 변수 추가
  • @Builder.Default : Builder 패턴으로 해당 엔티티 생성을 하고 있어, isDeleted 의 값을 정해둔 값으로 초기화하기 위함


2. 삭제 명령 재정의

💡 Soft Delete를 위해서는 delete 메소드가 실행될 때 DELETE 쿼리 대신 UPDATE 쿼리를 발생시켜야 합니다.
💡 위에서 봤던 Passage 엔티티 클래스에 아래 어노테이션들을 주입시킵니다.

  • @SQLDelete : DELETE문을 UPDATE문으로 자동 변경해 처리
    • 해당 어노테이션이 주입된 엔티티에서 SQL문의 DELETE 쿼리가 발생했을 때, 미리 정의한 SQL문을 발생시킵니다.
  • @Where : 삭제 데이터의 flag에 대한 조건을 엔티티 자체에 붙여서 사용
    • 해당 어노테이션이 주입된 엔티티에서 SQL 쿼리가 발생될 때 해당 조건을 자동으로 걸 수 있습니다.
    • JPA를 통한 모든 select문에 조건이 붙어 삭제된 데이터를 조회할 수 없는 문제가 발생했습니다.

데이터베이스를 보면 사용자가 삭제한 데이터는 실제 삭제되지 않았으며, is_deleted 컬럼을 이용 삭제된 것은 1, 삭제 안 된 것은 0으로 구분합니다.



🚨 Issue: @Where로 인해 삭제된 데이터 조회 불가능

  • SELECT 쿼리가 발생했을 때 실제 SQL을 보면 Where 조건이 자동으로 붙어 삭제된 데이터는 제외되도록 한 것을 볼 수 있습니다.

이를 해결하기 위한 2가지 방법이 있습니다.

1. JPQL을 작성해 @Where 설정 무시
- 삭제된 데이터를 조회하는 메소드를 따로 생성합니다.

@Query(value = "Select * From table Where is_deleted = true", nativeQuery = true)

위 방법으로 간단하게 해결할 수 있지만, 해당 이슈에 대해 다른 방법이 있을 것 같아 Hibernate 공식 문서를 찾아보게 되었습니다.


2. Filter 적용

공식 문서의 Filter 설명란을 보면 "Soft Delete 된 데이터를 숨기는 필터" 라고 적혀있습니다.
이 내용을 반대로 하면, 필터를 이용해 Soft Delete 된 데이터를 꺼낼 수도 있습니다.



🟢 Hibernate 필터링

Hibernate 공식 문서8.1. Filters 부분을 참고했습니다.

1. 엔티티 필터 설정: @FilterDef, @Filter

💡 위에서 봤던 Passage 엔티티 클래스에 아래 어노테이션들을 주입시킵니다.

  • @Filter : 필터가 주어진 엔터티 또는 컬렉션에 적용되는 방식을 지정 (필터당 여러 개)
    • name : 적용할 필터 이름
    • condition : 필터의 조건
      • SQL 조건식으로 작성하며 필터가 적용될 때 해당 조건에 따라 엔티티가 필터링됨
  • @FliterDef : 필터를 정의하고 필터 이름을 선언 (필터당 하나씩)
    • name : 정의할 필터 이름
    • parameters : 필터에 사용될 파라미터


2. 필터 적용: FilterManager

Filter Manager

💡 필터 활성화와 비활성화를 위한 메소드를 생성합니다.
💡 필터 이름, 필터에 적용 시킬 파라미터 이름과 값을 받습니다.

  • Hibernate의 Session : Hibernate의 인터페이스 중 하나로, 데이터베이스 연결 관리 및 엔티티 저장, 수정, 삭제, 조회
  • entityManager.unwrap(Session.class) : JPA의 EntityManager를 Hibernate의 Session 객체로 변환
    • EntityManager를 통해 Hibernate 기능을 사용할 때 사용

PassageService

💡 삭제된 데이터를 조회하기 전에 필터를 활성화합니다.
💡 데이터 조회가 끝난 후 필터를 비활성화해 기본 상태로 돌려놓습니다.
💡 JPA 메서드 호출 시 isDeleted 컬럼 조건 없이 원하는 데이터를 구분할 수 있습니다.



테스트

  • 데이터베이스 상태

book_id = 1인 데이터가 4개가 있고, id = 2인 데이터가 삭제된 상태입니다.


  • 모든 데이터 조회 isDeleted = false

book_id = 1인 데이터를 모두 조회하면, 삭제된 데이터 1개를 제외한 3개의 데이터가 반환됩니다.


  • 삭제된 데이터 조회 isDeleted = false

삭제된 데이터는 book id에 상관 없이 모두 조회하도록 되어 있어, book_id = 15인 데이터도 같이 총 2개의 데이터가 출력됩니다.

0개의 댓글