내가 일하는 은행 도메인에서는 오히려 soft delete가 유리하다고 느꼈음 deleted_at 컬럼이 있으면 쿼리 작성이 명확하고, 분석용 쿼리나 관리자 페이지에서도 동일한 데이터셋을 다룰 수 있음
삭제는 대부분의 경우 드물고, soft delete된 행이 성능 문제를 일으킨 적도 거의 없었음
또한 관계가 그대로 유지되므로 복구(undo) 도 간단함
나는 더 나아가 행을 완전히 불변(immutable) 하게 만들어 업데이트 시 새 행을 추가하는 방식을 선호함
로그를 남기려면 DB 트리거를 이용해 INSERT/UPDATE/DELETE 시 복제 테이블에 기록을 남기는 접근이 좋다고 생각함
네 말이 맞지만, 삭제가 드문 경우에만 해당된다고 봄
내가 본 테이블 중 50~70%가 soft delete된 경우에는 성능 저하가 확실히 있었음
결국 soft delete는 상황에 따라 다르고, 사전 분석이 필요함
Postgres에서는 soft delete되지 않은 데이터만 인덱싱하도록 설정할 수도 있음
대부분의 경우 필요 없지만, RAM 절약에는 도움이 될 수 있음
은행에서 soft delete는 감사 추적성(auditability) 부족을 임시로 가리는 해결책에 불과함
진짜 해법은 Event Sourcing으로, 모든 변경을 이벤트로 기록해야 함
성능은 떨어지지만, 스냅샷과 동기화(sync) 로 보완 가능함
DB를 불변 구조로 운영하려면 Datomic 같은 시스템을 고려할 만함
시간여행(time travel) 기능으로 과거 상태를 완전하게 조회할 수 있음
예전 보험사에서 근무할 때도 각 테이블을 append-only 로그로 운영했음
최신 상태는 가장 큰 타임스탬프의 행에 있고, 과거 상태는 필터로 조회 가능했음
이 방식은 강력한 이력 관리를 가능하게 함
soft delete의 가장 큰 함정은 쿼리 복잡도임
처음엔 WHERE deleted_at IS NULL만 추가하면 된다고 생각하지만, 몇 달 지나면 필터 누락으로 유령 데이터가 보고서에 등장함
View로 해결할 수 있지만, 결국 병렬 접근 패턴을 유지해야 하고, 삭제된 데이터 조회 시엔 추상화를 우회해야 함
Event sourcing이 더 깔끔하지만 운영 부담이 크기 때문에, 대부분은 하이브리드 접근을 택함
View는 충분히 강력한 도구임
문제는 많은 SWE와 BI 엔지니어가 SQL과 스키마 설계에 익숙하지 않다는 점임
soft delete보다 더 흔한 문제는 Type 2 Slowly Changing Dimension 처리임
대부분 불필요하게 audit table을 만들어 비효율적인 UPDATE/INSERT를 반복함
사실 DB는 정말 아름다운 시스템인데, 그만큼 존중받지 못함이 아쉬움
soft delete가 DB 내장 기능으로 제공되면 좋겠다고 생각함
테이블 단위로 활성화하고, 삭제 처리 전략을 선택할 수 있다면 이상적일 것 같음
실제로 Iceberg, Delta Lake, BigQuery 같은 시스템은 time travel 기능을 제공함
하지만 많은 팀이 커스텀 요구사항 때문에 결국 SCD(Slowly Changing Dimension) 방식으로 구현함
내 경험상 트리거 기반 접근이 가장 안정적이었음
아카이브 테이블은 append-only로 유지하고, 복구는 애플리케이션 레이어에서 처리해야 함
업데이트는 soft delete로 간주하고, 트리거가 이전 상태를 캡처하도록 함
트리거는 반드시 BEFORE 시점에 실행되어야 하며, 로직은 단순해야 함
파티션은 월 단위가 일반적이고, 쓰기 부하가 많으면 일 단위로 나누는 게 좋음
나는 DB가 stateful → stateless로 진화하길 바람
모든 변경을 append-only 이벤트로 기록하고, 필요한 데이터는 view로 표현하는 구조를 선호함
DB가 자동으로 materialized index를 관리해주면 이상적임
일부 최신 DB는 이런 기능을 제공하지만, 아직은 OLTP 중심의 발전이 부족함
예전에 다닌 회사에서는 모든 시스템에 soft delete를 적용했었음
교수님도 “비즈니스 세계에서는 데이터가 절대 삭제되지 않는다”고 하셨던 게 기억남
완전 삭제는 미래의 데이터 분석 능력을 스스로 제한하는 행위임
저장 공간은 싸니까, 데이터를 절대 지우지 말아야 함
하지만 수정에 대해서는 교수님이 아무 말도 안 하셨던 게 흥미로움
데이터베이스는 사실(fact) 을 저장하는 곳임
레코드 생성은 새로운 사실, 삭제는 또 다른 사실임
하지만 행을 물리적으로 지우면 사실이 사라짐
대부분의 경우 이런 삭제는 바람직하지 않음
하지만 데이터가 유출 위험을 가진 자산이라면, 오히려 대량 삭제가 필요할 수도 있음
유지 비용과 보안 리스크를 고려해야 함
DB가 불변이 아니라면, 수정 자체가 이미 사라진 사실을 만드는 셈임
데이터를 영구 보존하는 결정은 신중해야 함
개인적으로는 데이터 저장소가 조회와 삽입 두 가지 연산만 지원해야 한다고 생각함
이를 위해 데이터의 수명 주기를 이해하는 것이 중요함
Firezone에서는 처음에 soft delete를 감사 로그용으로 썼지만, 마이그레이션 문제로 포기했음
대신 Postgres CDC(Change Data Capture) 를 사용해 별도의 쓰기 최적화 테이블로 이벤트를 내보내는 방식으로 전환함
soft delete는 사용자 복구 기능에는 유용하지만, 감사나 규정 준수 용도로는 부적절하다고 생각함
단순한 프로젝트에서는 DB 변경 대신 API 호출 자체를 감사하는 게 더 효율적임
soft delete 필드를 가진 테이블 위에 View를 만들어, 삭제된 행을 숨기는 방식이 깔끔함
이렇게 하면 애플리케이션은 삭제 여부를 신경 쓸 필요가 없음
Postgres의 RLS(Row Level Security) 를 이용하면 soft delete된 행을 자동으로 숨길 수 있음
애플리케이션은 여전히 동일한 테이블에 읽기/쓰기/삭제를 수행함
스키마 드리프트(schema drift) 는 어떻게 처리하냐는 질문이 있음
삭제 당시의 스키마로 직렬화된 데이터를 나중에 복원하려면, 스키마 변경이 문제됨
내 경험상 아카이브된 객체는 거의 접근되지 않음
삭제 후 며칠 내 복원하는 경우가 대부분이라 스키마 변경 영향이 적음
오래된 아카이브를 새 모델로 마이그레이션하는 건 복잡하고 오류 가능성이 높은 작업이었음
결국 시스템의 사용 방식에 따라 접근 전략이 달라짐
Hacker News 의견들
내가 일하는 은행 도메인에서는 오히려 soft delete가 유리하다고 느꼈음
deleted_at컬럼이 있으면 쿼리 작성이 명확하고, 분석용 쿼리나 관리자 페이지에서도 동일한 데이터셋을 다룰 수 있음삭제는 대부분의 경우 드물고, soft delete된 행이 성능 문제를 일으킨 적도 거의 없었음
또한 관계가 그대로 유지되므로 복구(undo) 도 간단함
나는 더 나아가 행을 완전히 불변(immutable) 하게 만들어 업데이트 시 새 행을 추가하는 방식을 선호함
로그를 남기려면 DB 트리거를 이용해 INSERT/UPDATE/DELETE 시 복제 테이블에 기록을 남기는 접근이 좋다고 생각함
내가 본 테이블 중 50~70%가 soft delete된 경우에는 성능 저하가 확실히 있었음
결국 soft delete는 상황에 따라 다르고, 사전 분석이 필요함
대부분의 경우 필요 없지만, RAM 절약에는 도움이 될 수 있음
진짜 해법은 Event Sourcing으로, 모든 변경을 이벤트로 기록해야 함
성능은 떨어지지만, 스냅샷과 동기화(sync) 로 보완 가능함
시간여행(time travel) 기능으로 과거 상태를 완전하게 조회할 수 있음
최신 상태는 가장 큰 타임스탬프의 행에 있고, 과거 상태는 필터로 조회 가능했음
이 방식은 강력한 이력 관리를 가능하게 함
soft delete의 가장 큰 함정은 쿼리 복잡도임
처음엔
WHERE deleted_at IS NULL만 추가하면 된다고 생각하지만, 몇 달 지나면 필터 누락으로 유령 데이터가 보고서에 등장함View로 해결할 수 있지만, 결국 병렬 접근 패턴을 유지해야 하고, 삭제된 데이터 조회 시엔 추상화를 우회해야 함
Event sourcing이 더 깔끔하지만 운영 부담이 크기 때문에, 대부분은 하이브리드 접근을 택함
문제는 많은 SWE와 BI 엔지니어가 SQL과 스키마 설계에 익숙하지 않다는 점임
soft delete보다 더 흔한 문제는 Type 2 Slowly Changing Dimension 처리임
대부분 불필요하게 audit table을 만들어 비효율적인 UPDATE/INSERT를 반복함
사실 DB는 정말 아름다운 시스템인데, 그만큼 존중받지 못함이 아쉬움
soft delete가 DB 내장 기능으로 제공되면 좋겠다고 생각함
테이블 단위로 활성화하고, 삭제 처리 전략을 선택할 수 있다면 이상적일 것 같음
하지만 많은 팀이 커스텀 요구사항 때문에 결국 SCD(Slowly Changing Dimension) 방식으로 구현함
내 경험상 트리거 기반 접근이 가장 안정적이었음
아카이브 테이블은 append-only로 유지하고, 복구는 애플리케이션 레이어에서 처리해야 함
업데이트는 soft delete로 간주하고, 트리거가 이전 상태를 캡처하도록 함
트리거는 반드시 BEFORE 시점에 실행되어야 하며, 로직은 단순해야 함
파티션은 월 단위가 일반적이고, 쓰기 부하가 많으면 일 단위로 나누는 게 좋음
나는 DB가 stateful → stateless로 진화하길 바람
모든 변경을 append-only 이벤트로 기록하고, 필요한 데이터는 view로 표현하는 구조를 선호함
DB가 자동으로 materialized index를 관리해주면 이상적임
일부 최신 DB는 이런 기능을 제공하지만, 아직은 OLTP 중심의 발전이 부족함
Martin Fowler의 설명을 참고할 만함
예전에 다닌 회사에서는 모든 시스템에 soft delete를 적용했었음
교수님도 “비즈니스 세계에서는 데이터가 절대 삭제되지 않는다”고 하셨던 게 기억남
저장 공간은 싸니까, 데이터를 절대 지우지 말아야 함
데이터베이스는 사실(fact) 을 저장하는 곳임
레코드 생성은 새로운 사실, 삭제는 또 다른 사실임
하지만 행을 물리적으로 지우면 사실이 사라짐
대부분의 경우 이런 삭제는 바람직하지 않음
유지 비용과 보안 리스크를 고려해야 함
데이터를 영구 보존하는 결정은 신중해야 함
이를 위해 데이터의 수명 주기를 이해하는 것이 중요함
Firezone에서는 처음에 soft delete를 감사 로그용으로 썼지만, 마이그레이션 문제로 포기했음
대신 Postgres CDC(Change Data Capture) 를 사용해 별도의 쓰기 최적화 테이블로 이벤트를 내보내는 방식으로 전환함
soft delete는 사용자 복구 기능에는 유용하지만, 감사나 규정 준수 용도로는 부적절하다고 생각함
soft delete 필드를 가진 테이블 위에 View를 만들어, 삭제된 행을 숨기는 방식이 깔끔함
이렇게 하면 애플리케이션은 삭제 여부를 신경 쓸 필요가 없음
애플리케이션은 여전히 동일한 테이블에 읽기/쓰기/삭제를 수행함
스키마 드리프트(schema drift) 는 어떻게 처리하냐는 질문이 있음
삭제 당시의 스키마로 직렬화된 데이터를 나중에 복원하려면, 스키마 변경이 문제됨
삭제 후 며칠 내 복원하는 경우가 대부분이라 스키마 변경 영향이 적음
오래된 아카이브를 새 모델로 마이그레이션하는 건 복잡하고 오류 가능성이 높은 작업이었음
결국 시스템의 사용 방식에 따라 접근 전략이 달라짐