먹수의 개발일지

Soft Delete & Unique Constraint (PostgreSQL/Django) 본문

back-end/Database

Soft Delete & Unique Constraint (PostgreSQL/Django)

icandle 2024. 2. 9. 15:50

Soft Delete란?

DB 테이블에서 특정 row를 삭제 처리할 때, delete를 사용하지 않고 삭제 여부 혹은 삭제 시점 칼럼을 활용하여 flag 체크하는 방식이다. 반대되는 개념으로는 테이블에서 row를 delete하는 Hard Delete가 있다. Hard Delete의 경우 직접 삭제하기 때문에 별도의 audit table을 관리해줘야 한다. 

 

Soft Delete와 unique Constraint 로 인한 문제

회사에서 Soft Delete로 구현된 테이블을 작업하던 중 문제가 발생했다. 특정 시설물들을 관리하는 테이블에서 시설물을 Soft Delete한 후, 삭제된 것과 동일한 코드(unique 제약이 적용된 칼럼)를 가진 시설물 추가에 실패하게 되었다. 삭제 상태로 변경된 기존 시설물 코드값 칼럼과 새로운 등록하려는 코드값이 unique 조건에 충돌되기 때문이다.

 

DB unique 삭제 

API로직 상에서 중복을 막을 수는 있지만, DB에 직접 데이터 추가 혹은 수정시 중복된 시설물 코드값이 들어갈 수 있는 위험성이 있기 때문에 실무에서는 사용할 수 없다.

 

🔥 Partial Index

partial index란 조건에 만족한것만 indexing 하기 위해 사용하는 것이다. 공식문서 참고

 

여러 글을 찾아본 끝에, 현재 나의 DB와 구조에 적합한 partial index를 사용하기로 결정했다.

삭제되지 않은 row(is_delete=False)에 한정하여 unique 제약조건을 적용해주면, soft delete를 사용하여 삭제된 row(is_deleted=True)에는 인덱싱이 적용되지 않는다. 즉 삭제된 것들과의 unique 충돌 문제를 해결할 수 있게 되었다!

 

models.py (django)

class Product(TimeStampedModel):
    id = models.BigAutoField(primary_key=True)
    product_cd = models.CharField(max_length=30, help_text="표찰번호")
											...
    is_deleted = models.BooleanField(default=False, help_text="삭제여부")

    def __str__(self):
        return self.strlamp_cd

class Meta:
        constraints = [
            # 유니크 제약 조건을 부분 인덱스로 지정
            UniqueConstraint(
                name='unique_product_cd_constraint',
                fields=['product_cd'],
                condition=Q(is_deleted=False),
            ),
        ]

 

PostgreSQL의 경우 Partial Index가 가능하기 때문에 시설물 코드에 부분 인덱스를 걸어줄 수 있다.

 

다른 방법들도 고려해보자.

Virtual Columns

  • Mysql과 같이 partial index가 없는 DB의 경우, 가상 칼럼(virtual column)을 생성하여 복합 키(Composite Key)를 설정해주면 된다.
  • 사용하는 DB의 Unique constraint가 Null 값은 무시하는지 확인해야한다.
  • 다만 현재 내가 사용한 삭제여부 칼럼 구조 (is_deleted를 true, false 타입 둘중에 하나를 받는 구조)가 아닌 is_deleted 기본값(1), 삭제된 경우 Null 값을 갖는 구조로 변경해야 한다.

 

Hard Delete

  • audit table을 별로도 생성하여 테이블에서 수행되는 작업 이력을 쌓아가는 방식이다. 테이블의 변경 사항을 추적 하려는 경우에 사용된다.

→ Hard Delete 👍

  • active data를 불러올 때 실수를 줄일 수 있다.
    • soft delete의 경우 쿼리 작성시 is_deleted=False의 값을 누락하는 경우로 문제가 발생하기 쉽다.
  • 모든 쿼리에 삭제여부 조건을 추가하지 않아도 되기 때문에, 성능면에서 더 유리하다.
  • Soft delete의 경우 테이블에 soft delete된 데이터들과 active 데이터가 함께 있기 때문에 테이블의 크기가 상대적으로 더 커지고, 그로인한 쿼리 성능이 안좋아질 수 있다. 그에 비하여 Hard delete는 성능면에서 유리하다.

결론

현재 개발중인 프로젝트는, 시장에서의 반응을 빠르게 살피기 위함이므로 간단하게 soft delete를 활용하여 구현해도 괜찮다고 판단했다. 계속해서 변경되는 기획과 개발 요청사항에 빠르게 대처하기 위함이다. 다만, 실제 안정적인 운영으로 들어가고 데이터 이력의 중요성이 높아지는 경우 Hard delete를 활용하여 구현하면 좋을 것 같다. 다음번에는 개인적으로 Hard delete 환경을 구축하고 글을 작성해보고자 한다.

 

References 

https://oraange.tistory.com/26

https://gusiol.medium.com/soft-delete-and-unique-constraint-da94b41cff62

ttps://velog.io/@yhlee9753/soft-delete-와-hard-delete-비교

Comments