운영이 커질수록 “삭제"는 단순 SQL 한 줄 문제가 아닙니다.DELETE FROM ... 로 끝내면 당장은 속이 시원하지만, 다음 주에 검색 인덱스 잔존 데이터, 분석 테이블 불일치, 백업 복구 시 재노출, 규제 감사 대응 실패가 한꺼번에 터집니다.
실무에서는 보통 삭제를 3단계로 다룹니다.
- 논리 삭제(Soft Delete): 사용자 관점에서 즉시 숨김
- 보관(Archive): 운영 DB 부담을 줄이면서 감사/분쟁 대응 데이터 유지
- 물리 삭제(Purge): 정책 만료 후 되돌릴 수 없게 완전 삭제
핵심은 “언제"보다 “무엇을 어떤 경로로” 지우느냐를 명확히 나누는 것입니다.
이 글에서 얻는 것
- 보존 정책을 서비스/테이블 단위로 나눌 때 쓰는 실무 분류 기준(민감도·법적 의무·복구 필요성) 을 잡을 수 있습니다.
- Soft Delete와 Hard Delete를 섞어 쓰면서도 정합성을 유지하는 삭제 파이프라인 설계 패턴을 이해할 수 있습니다.
- 배치 처리량, 지연 시간, 오류율 기반으로 purge 작업을 운영하는 의사결정 임계치(숫자 기준) 를 바로 적용할 수 있습니다.
핵심 개념/이슈
1) 보존 정책은 “테이블"이 아니라 “데이터 클래스” 기준으로 설계한다
같은 orders 테이블에도 성격이 다른 데이터가 섞여 있습니다.
- 결제 증빙(장기 보존 필요)
- 추천/실험 로그(단기 보존 가능)
- 개인정보(목적 달성 후 삭제 대상)
그래서 실무에서는 최소 3개 클래스로 나눕니다.
- Class A (법적/회계 필수 보존): 3~10년 보존, 접근 통제 강화
- Class B (운영 분석 데이터): 90~365일 보존, 요약본만 장기 유지
- Class C (개인식별/민감 데이터): 목적 달성 후 7~30일 내 삭제 큐 진입
이 분류를 먼저 고정해야 백업/DR 전략과 충돌이 줄어듭니다. 백업 보존 기간이 삭제 정책보다 길면 “삭제했는데 복구하면 다시 나타나는” 사고가 반복됩니다.
2) Soft Delete는 기능이 아니라 “전이 상태"다
Soft Delete(deleted_at 설정)는 최종 상태가 아니라 Purge 전 임시 상태입니다.
이 점을 놓치면 데이터가 영구히 쌓여 인덱스 비대화와 쿼리 성능 저하가 발생합니다.
운영 기준 예시:
deleted_at설정 후 30일 내 purge 큐 등록 완료율 99% 이상- soft-deleted 레코드 비율이 전체의 20% 초과 시 인덱스/쿼리 재설계 트리거
- purge 지연이 24시간 초과 시 운영 알림
Soft Delete 컬럼만 추가하고 끝내는 대신, Online DDL + Expand/Contract처럼 “추가 → 전환 → 정리"까지 한 세트로 설계해야 합니다.
3) 삭제는 동기 API가 아니라 비동기 파이프라인으로 분리한다
요청 경로에서 바로 cascade delete를 수행하면 타임아웃과 락 경합이 늘어납니다. 일반적으로 아래 구조가 안정적입니다.
- API: 삭제 요청 접수 + 상태 전환(
ACTIVE -> PENDING_DELETE) - Outbox 이벤트 기록
- 워커: 본 테이블/연관 테이블/검색 인덱스/캐시 순차 정리
- 완료 이벤트 발행 + 감사 로그 저장
이 흐름은 배치 멱등성과 재처리 전략과 결합해야 안전합니다. 삭제 워커는 재시도가 잦으므로 멱등 키 없이 운영하면 중복 삭제/누락 삭제가 동시에 생깁니다.
4) 삭제 성공률만 보지 말고 “재노출 위험” 지표를 따로 관리한다
삭제 시스템의 진짜 품질은 성공 건수가 아니라 삭제 후 재노출 가능성으로 측정해야 합니다.
- Purge Success Rate: 99.9%+
- Search Index Residual Rate(삭제 후 검색 노출): 0.1% 미만
- Backup Reappearance Incident: 월 0건
- DSAR(정보 삭제 요청) SLA: 7일 또는 내부 정책 기준 이내
실무 적용
1) 정책 매트릭스를 먼저 문서화하고 코드로 강제한다
문서만 두면 실제 운영에서 깨집니다. 최소한 아래 필드를 공통 메타데이터로 둡니다.
retention_class(A/B/C)delete_mode(soft,hard,archive_then_purge)purge_after_atlegal_hold(분쟁/감사 보류)
의사결정 우선순위:
- 법적 보존 의무 충족
- 개인정보 최소 보관 원칙
- 운영 비용/성능 최적화
즉, 성능이 좋아도 법적 보존을 깨면 안 되고, 법적 보존이 있다고 해서 불필요한 식별정보를 무기한 보관해도 안 됩니다.
2) 스키마 설계: 삭제 컬럼 1개가 아니라 삭제 수명주기 컬럼 3개를 둔다
실무에서 자주 쓰는 최소 세트:
deleted_at: 사용자 관점 비활성 시점purge_after_at: 물리 삭제 예정 시점delete_reason_code: 정책/사용자요청/운영정리 구분
그리고 인덱스는 WHERE deleted_at IS NULL 필터 패턴을 기본으로 잡습니다.
MySQL/PostgreSQL 모두 active 데이터 경로와 deleted 데이터 경로를 분리해야 읽기 성능이 유지됩니다.
3) Purge 워커 운영 기준(숫자)
권장 초기값(중간 규모 서비스 기준):
- 배치 청크: 500~2,000건
- 워커 동시성: DB primary 기준 2~6
- 단일 트랜잭션 최대 실행 시간: 2초 이하
- 재시도: 최대 5회, 지수 백오프 + jitter
- 일일 purge 한도: 전체 테이블의 3~5% 이내(급격한 락/복제 지연 방지)
중단 기준:
- replication lag 10초 초과 5분 지속
- lock wait timeout 비율 1% 초과
- API p95 지연이 기준 대비 20% 이상 상승
이 조건 중 하나라도 만족하면 purge 속도를 자동으로 절반으로 낮추는 식의 백프레셔를 걸어야 합니다.
4) 연관 시스템 정리 순서를 고정한다
삭제 순서가 매번 다르면 정합성 버그가 생깁니다. 권장 순서:
- 사용자 조회 경로 차단(soft delete)
- 캐시/서치 인덱스 비활성화
- 주 데이터 저장소 정리
- 파생 저장소(분석 mart, 데이터레이크) 정리
- 감사 로그에 완료 상태 기록
특히 데이터레이크는 지연 적재가 많아서 “OLTP에서 삭제됨"과 “분석에 남아 있음” 간 시간차를 허용하는 명시적 SLA(예: 24시간 이내 정리)를 두는 것이 현실적입니다.
5) 감사 대응을 위해 “무엇을 지웠는지"가 아니라 “어떤 정책으로 지웠는지"를 남긴다
개인정보 자체를 다시 저장하면 모순이 됩니다. 대신 아래 메타 로그를 남깁니다.
- 요청 ID / 주체(사용자, 관리자, 시스템)
- 정책 버전(retention policy vX)
- 삭제 대상 건수와 범위
- 실행 시각, 완료 시각, 실패 사유
이 정도면 외부 감사나 내부 보안 점검에서 설명 가능성이 높아집니다.
트레이드오프/주의점
Soft Delete 남용 시 스토리지/인덱스 비용이 급증
삭제 안전성은 좋아지지만 장기적으로 조회 성능이 나빠질 수 있습니다. 테이블별 허용 비율(예: 15~20%)을 넘기면 아카이브 분리나 파티셔닝을 검토해야 합니다.완전 삭제와 법적 보존 요구가 충돌할 수 있음
“삭제 요청 즉시 완전 삭제"는 분쟁 대응/회계 보존과 상충할 수 있습니다.legal_hold플래그, 보존 근거 문서, 승인 절차를 함께 설계해야 합니다.백업/복구 경로를 무시하면 삭제 신뢰성이 무너짐
운영 DB에서 삭제해도 스냅샷 복원 시 재등장할 수 있습니다. 백업 보존 정책과 purge 정책을 같은 문서에서 관리해야 합니다.연관 서비스 간 삭제 이벤트 계약이 약하면 누락 삭제가 누적
검색/추천/알림 시스템이 삭제 이벤트를 놓치면 데이터 그림자가 남습니다. 소비자 lag와 DLQ를 상시 모니터링해야 합니다.
체크리스트 또는 연습
실무 체크리스트
- 데이터 클래스를 A/B/C(법적 보존/운영 분석/민감 삭제)로 정의했다.
-
deleted_at,purge_after_at,legal_hold를 공통 규약으로 적용했다. - purge 워커의 처리량/동시성/중단 임계치를 숫자로 문서화했다.
- 검색 인덱스/캐시/데이터레이크 정리 순서와 SLA를 정의했다.
- 백업 보존 정책과 삭제 정책의 충돌 여부를 분기별로 점검한다.
연습 과제
- 현재 서비스에서 “사용자 탈퇴” 흐름을 그려서, 어느 저장소에 데이터가 남는지 전부 나열해보세요.
- 그중 민감 데이터 3개를 골라
soft -> archive -> purge수명주기를 날짜 기준으로 설계해보세요. - purge 지연이 48시간 발생했을 때, 서비스 성능을 지키면서 복구하는 런북(속도 제한/우선순위/중단 기준)을 작성해보세요.
삭제는 기능이 아니라 운영 체계입니다. 설계 초기에 조금 복잡하게 보이더라도, 정책·파이프라인·지표를 같이 묶어두면 장애와 감사 대응 비용을 훨씬 크게 줄일 수 있습니다.
💬 댓글