본문 바로가기

백엔드 지식 저장소

Soft Delete로 Delete 구현 후 테스트 케이스 실패

728x90

프로젝트를 기획하면서 사용자가 삭제 요청을 보냈을 때 DB에서 데이터 삭제가 아닌 deleted_at 값을 null 에서 현재 시간을 insert하여 해당 데이터는 나오지 않도록 하는 Soft Delete를 사용하기로 하였습니다.

 

게시글 Create, Read, Update를 구현한 후에 Delete를 구현 순서를 잡고 진행을 하였습니다. 

Soft Delete를 구현하기 위해 Service 계층에서 로직으로 deleted_at에 LocalDateTime.now()를 넣어주는 로직을 사용하는 대신에 저희는 Entity 클래스에서 @SQLDelete와 @SQLRestriction을 사용하여 Soft Delete를 구현했습니다.

@Getter
@SQLDelete(sql = "update board set deleted_at = now() where board_id = ?")
@SQLRestriction("deleted_at is null")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
public class Board extends BaseSoftDeleteEntity {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "board_id", nullable = false)
	private Long id;

	@ManyToOne(fetch = FetchType.LAZY)
	@JoinColumn(name = "crew_id", nullable = false)
	private Crew crew;

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

	@Column(name = "title", nullable = false)
	private String title;

	@Column(name = "content", columnDefinition = "TEXT")
	private String content;
    }

JPA에서 delete를 할 경우 deleted_at에 현재 시간을 넣어주었고 select 쿼리에서 deleted_at이 null이 아닌 데이터만 나오도록 하였습니다.

 

게시글 삭제 기능 구현 완료 후 테스트를 진행하였는데 기존 작성했던 테스트까지 실패가 되면서 오류 코드를 보여주었습니다.

JDBC exception executing SQL [delete from crew where (crew.deleted_at is null)] [Referential integrity constraint violation: "FKOUB5677298OUDW5XKHV1219EA: PUBLIC.BOARD FOREIGN KEY(CREW_ID) REFERENCES PUBLIC.CREW(CREW_ID) (CAST(1 AS BIGINT))"; SQL statement:
delete from crew where (crew.deleted_at is null) [23503-224]] [n/a]; SQL [n/a]; constraint ["FKOUB5677298OUDW5XKHV1219EA: PUBLIC.BOARD FOREIGN KEY(CREW_ID) REFERENCES PUBLIC.CREW(CREW_ID) (CAST(1 AS BIGINT))"; SQL statement:
delete from crew where (crew.deleted_at is null) [23503-224]]
org.springframework.dao.DataIntegrityViolationException: JDBC exception executing SQL [delete from crew where (crew.deleted_at is null)] [Referential integrity constraint violation: "FKOUB5677298OUDW5XKHV1219EA: PUBLIC.BOARD FOREIGN KEY(CREW_ID) REFERENCES PUBLIC.CREW(CREW_ID) (CAST(1 AS BIGINT))"; SQL statement:
delete from crew where (crew.deleted_at is null) [23503-224]] [n/a]; SQL [n/a]; constraint ["FKOUB5677298OUDW5XKHV1219EA: PUBLIC.BOARD FOREIGN KEY(CREW_ID) REFERENCES PUBLIC.CREW(CREW_ID) (CAST(1 AS BIGINT))"; SQL statement:
delete from crew where (crew.deleted_at is null) [23503-224]]

 

원인은 쿼리를 보고 쉽게 파악할 수 있었습니다.

Hibernate: 
    delete 
    from
        board 
    where
        (
            board.deleted_at is null
        )
Hibernate: 
    delete 
    from
        crew 
    where
        (
            crew.deleted_at is null
        )

@SQLRestriction에서 deleted_at이 null인 항목만 찾도록 하였고 JPA를 사용하여 repository를 삭제하는 코드를 작성하였기 때문에 deleted_at이 null인 항목들만 삭제가 되는 현상이 발생하였습니다.

JPA를 사용하여 작성한 tearDown 메서드 입니다.

@AfterEach
void tearDownJpa() {
	boardImageRepository.deleteAllInBatch();
	commentRepository.deleteAllInBatch();
	thumbsUpRepository.deleteAllInBatch();
	boardRepository.deleteAllInBatch();
	crewRepository.deleteAllInBatch();
	memberRepository.deleteAllInBatch();
}

테스트를 원할하게 수행하기 위해서는 Repository를 delete를 해줘야해서 JPA를 사용하지 않고 Delete를 수행할 수 있는 JdbcTemplate를 사용하여 tearDown 메서드를 작성하였습니다.

@AfterEach
void tearDown() {
	jdbcTemplate.execute("DELETE FROM board_image");
	jdbcTemplate.execute("DELETE FROM comment");
	jdbcTemplate.execute("DELETE FROM thumbs_up");
	jdbcTemplate.execute("DELETE FROM board");
	jdbcTemplate.execute("DELETE FROM crew");
	jdbcTemplate.execute("DELETE FROM member");
}

 

JdbcTemplate를 사용한 후에는 test가 통과할 수 있었고 Soft Delete기능 구현을 완료할 수 있었습니다.

 

728x90