TypeOrm 에는 softDelete 기능이 있다.
Entity파일에서 deleted_at
을 추가해주면 된다.
@DeleteDateColumn({ type: 'timestamptz' })
deleted_at: Date;
데이터를 영구 삭제하고 싶지 않을 때 사용하는 방식이다.
데이터를 softDelete 하게 되면 deleted_at
이 null
이었다가, 삭제된 시점으로 Timestamp
값이 들어간다.
그럼 TypeORM에서 조회할 때 deleted_at
이 null
이 아닌 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 이 무조건 붙어버려서 이렇게는 할 수 없었다.
결국엔 해결 방법은 찾았는데 내가 보기엔 조금 조잡한 듯 하다.
- withDeleted 옵션을 적용한다. 적용하면 일단
deleted_at is null
부분은 빠진다. - 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"
)