TypeORM 원하는 entity에만 withDeleted 적용하기

@ZungTa · 2022-07-18 월요일 · 2 min read

TypeOrm 에는 softDelete 기능이 있다.

Entity파일에서 deleted_at 을 추가해주면 된다.

@DeleteDateColumn({ type: 'timestamptz' })
deleted_at: Date;

데이터를 영구 삭제하고 싶지 않을 때 사용하는 방식이다.

데이터를 softDelete 하게 되면 deleted_atnull 이었다가, 삭제된 시점으로 Timestamp 값이 들어간다.

그럼 TypeORM에서 조회할 때 deleted_atnull 이 아닌 row 는 자동으로 반환하지 않는다.

(내부 select 쿼리 내용 확인해보면 deleted_at is null 옵션이 붙게된다.)

그런데 개발을 하다보면 삭제되어 있는 row 들도 같이 find 하고 싶은 경우가 있을 것이다.

그럴 경우 쓰는 옵션으로 withDeleted 라는 옵션이 있다. (참고 https://orkhan.gitbook.io/typeorm/docs/find-options)

find 할 때 저 옵션을 주면 삭제된 것들도 같이 찾아준다.

내부적으로는 SQL 쿼리에서 deleted_at is null 부분이 빠지게 된다.

근데 사용할 때 해당 테이블에 ManyToMany 연결이나 OneToMany 연결이 되어있는 테이블이 있다면 의도와 다르게 동작할 수 있다.

ManyToMany 연결일 경우 A 와 B 테이블을 연결해주는 JoinTable 이 생기게되는데 그 JoinTable 에 있는 값 중 연결된 row 가 softDelete 가 되어 있다면 그 값도 같이 find 된다.

예를들면 Product Table 과 RepairHistory Table 이 있을 때 Product와 RepairHistory 는 1:N 으로 연결될 것이다.

RepairHistory 가 변경되어 기존 것은 softDelete 처리한 후 새로운 RepairHistory 를 만들어서 연결시켜주었다.

그럼 Product 조회 시 withDeleted 옵션을 주게되면 softDelete 된 RepairHistory 도 같이 조회되게 된다.

물론 이런 동작을 원했을 경우도 있겠지만, 나는 Product 는 삭제된 것들도 같이 조회되고 RepairHistory 는 삭제되지 않은 것들만 조회하고 싶었다.

우선 withDeleted 옵션에는 boolean 값만 넣을 수 있어서 따로 세부적으로 조정할 수는 없었다.

(string[] 을 입력 받아서 세부 조정을 할 수 있게 하는 PR 을 하고 싶다.)

withDeleted 옵션을 빼고 따로 where 조건으로 deleted_at 을 일단 is not null 로 줘 보았다.

TypeORM 에서 만들어진 쿼리를 봤는데 deleted_at is not null and deleted_at is null 이런식으로 내가 정한 조건 뒤에 deleted_at is null 이 무조건 붙어버려서 이렇게는 할 수 없었다.

결국엔 해결 방법은 찾았는데 내가 보기엔 조금 조잡한 듯 하다.

  1. withDeleted 옵션을 적용한다. 적용하면 일단 deleted_at is null 부분은 빠진다.
  2. leftJoin 옵션에 deleted_at is null 을 수동으로 적용한다.

코드로 표현하면 아래와 같다.

const qb = this.productRepository.createQueryBuilder("product")
qb.withDeleted()
qb.leftJoinAndSelect(
  "product.repair_histories",
  "repair_histories",
  "repair_histories.deleted_at is null"
)
@ZungTa
I'm a backend developer
© ZungTa Devlog, Built with Gatsby