소프트 삭제의 어려움

2 weeks ago 6

  • 데이터 복구와 규제 준수를 위해 archived_at 열 기반의 소프트 삭제가 자주 사용되지만, 시간이 지나면 복잡성과 비효율이 커짐
  • 이 방식은 쿼리, 인덱스, 마이그레이션, 복원 로직을 복잡하게 만들고, 대부분의 보관 데이터가 다시 읽히지 않아 데이터베이스에 불필요한 부하를 초래함
  • 대안으로 애플리케이션 이벤트 기반 보관, 트리거 기반 보관, WAL(Change Data Capture) 기반 보관이 제시됨
  • 각 방식은 운영 복잡도, 인프라 요구, 복원 용이성에서 차이를 보이며, 특히 WAL 기반은 Kafka 등 외부 시스템과의 통합이 필요함
  • 새로운 프로젝트라면 트리거 기반 접근법단순성과 유지보수성 측면에서 가장 균형 잡힌 선택

소프트 삭제의 문제점

  • 일반적으로 deleted 불리언이나 archived_at 타임스탬프 열을 사용해 데이터를 논리적으로 삭제함
    • 고객이 데이터를 실수로 삭제했을 때 복구 가능
    • 규제나 감사 목적상 보관이 필요한 경우도 있음
  • 그러나 archived_at 열은 쿼리, 운영, 애플리케이션 코드 전반에 복잡성을 유발
    • 대부분의 보관 데이터는 다시 읽히지 않음
    • API 동작 문제나 자동화 도구(Terraform 등)로 인해 수백만 개의 불필요한 행이 누적될 수 있음
  • 보관 데이터 정리 작업이 설정되지 않으면 데이터베이스 백업 및 복원 시 성능 저하 발생
  • 쿼리와 인덱스에서 보관 데이터를 필터링해야 하며, 데이터 누출 위험 존재
  • 마이그레이션 시 오래된 데이터 처리나 기본값 수정이 어려움
  • 복원 로직이 복잡해지고, 외부 시스템 호출이 필요한 경우 버그 발생 가능
  • 결과적으로 archived_at 방식은 단순해 보이지만 장기적으로 유지보수 비용이 높음

애플리케이션 레벨 보관

  • 삭제 시 이벤트를 발행하고, 이를 SQS로 전송해 다른 서비스가 S3에 보관
  • 장점
    • 주요 데이터베이스와 애플리케이션 코드 단순화
    • 외부 리소스 정리를 비동기 처리하여 성능과 안정성 향상
    • JSON 형태로 직렬화해 애플리케이션 친화적 구조로 보관 가능
  • 단점
    • 애플리케이션 코드 버그로 인해 보관 데이터 손실 가능
    • 메시지 큐 등 운영 인프라 복잡성 증가
    • S3의 보관 데이터는 검색 및 복원 도구 필요

트리거 기반 보관

  • 삭제 전 트리거가 행을 별도의 archive 테이블에 JSON 형태로 복사
    • 예시 테이블: archive(id, table_name, record_id, data, archived_at, caused_by_table, caused_by_id)
  • 외래 키 삭제(cascade) 시 삭제 원인 추적을 위해 세션 변수(archive.cause_table, archive.cause_id) 사용
    • 어떤 상위 레코드가 하위 데이터를 삭제했는지 조회 가능
  • 장점
    • 라이브 테이블이 깨끗하게 유지, archived_at 열 불필요
    • 보관 테이블 정리(WHERE archived_at < NOW() - INTERVAL '90 days')가 간단
    • 쿼리와 인덱스 효율 유지, 마이그레이션 단순화
    • 백업 크기 감소
  • 보관 테이블은 별도 테이블스페이스시간 파티셔닝으로 관리 가능

WAL(Change Data Capture) 기반 보관

  • PostgreSQL의 WAL 로그를 읽어 삭제 이벤트를 외부 시스템으로 스트리밍
    • 대표 도구: Debezium (Kafka와 연동)
    • 경로 예시: PostgreSQL → Debezium → Kafka → Consumer → Archive Storage
  • 경량 대안
    • pgstream: WAL을 웹훅이나 메시지 큐로 직접 전송
    • wal2json: WAL을 JSON으로 출력
    • pg_recvlogical: PostgreSQL 내장 논리 복제 도구
  • 운영 복잡성
    • Kafka 기반은 모니터링·장애 대응·튜닝 필요
    • 소비자(consumer)가 지연되면 WAL 파일 누적 → 디스크 공간 부족 위험
    • PostgreSQL 13+의 max_slot_wal_keep_size 설정으로 제한 가능
    • 복제 슬롯 지연 모니터링 및 알림 필수
  • 장점
    • 애플리케이션 코드 수정 없이 모든 변경 캡처 가능
    • 다양한 목적지(S3, 데이터 웨어하우스, 검색 인덱스) 로 스트리밍 가능
    • 기본 데이터베이스에 추가 부하 없음
  • 단점
    • 운영 복잡도와 인프라 비용 높음
    • 소비자 지연 시 데이터 손실 또는 재동기화 필요
    • 스키마 변경 시 소스-소비자 간 조율 필요

삭제를 처리하지 않는 복제본 아이디어

  • DELETE 쿼리를 무시하는 PostgreSQL 복제본을 유지하는 아이디어 제시
    • 삭제되지 않은 모든 데이터를 누적 보관 가능
    • 보관 데이터 직접 쿼리 가능
  • 잠재적 문제
    • 삭제 정보 구분 불가 가능성
    • 마이그레이션 적용 시 충돌 위험
    • 저장 공간 및 운영 비용 증가

결론

  • 새로운 프로젝트에서는 트리거 기반 보관 방식이 가장 실용적 선택
    • 설정이 간단하고, 라이브 테이블을 깨끗하게 유지
    • 별도 인프라 없이도 보관 데이터 조회 및 관리 용이
  • 복잡한 인프라가 이미 존재하거나 다중 목적지 스트리밍이 필요한 경우에는 WAL 기반 접근이 적합

Read Entire Article