소프트 삭제 vs 하드 삭제: 올바른 데이터 수명주기 선택
소프트 삭제와 하드 삭제의 차이를 배우고, 이력을 보존하면서 참조를 깨뜨리지 않는 방법, 개인정보 삭제 요구를 충족하는 명확한 규칙을 세우세요.

소프트 삭제와 하드 삭제가 실제로 의미하는 것\n\n“삭제”는 두 가지 아주 다른 의미가 될 수 있습니다. 둘을 섞어 쓰면 팀이 이력을 잃거나 개인정보 삭제 요청을 이행하지 못하게 됩니다.\n\n하드 삭제는 대부분의 사람이 상상하는 바로 그것입니다: 데이터베이스에서 행이 제거됩니다. 나중에 쿼리하면 사라져 있습니다. 실제로 제거된 것이지만, 설계하지 않으면(예: 삭제된 고객을 가리키는 주문) 참조가 깨질 수 있습니다.\n\n소프트 삭제는 행을 유지하되 deleted_at이나 is_deleted 같은 필드로 삭제를 표시합니다. 애플리케이션에서는 사라진 것으로 취급하지만 보고, 지원, 감사 등을 위해 데이터는 남아 있습니다.\n\n소프트 삭제와 하드 삭제의 핵심 트레이드오프는 단순합니다: 이력 vs 완전 제거. 소프트 삭제는 이력을 보호하고 “되돌리기”가 가능하게 합니다. 하드 삭제는 저장량을 줄여 개인정보, 보안, 법적 요구를 충족시키는 데 유리합니다.\n\n삭제는 저장소 이상에 영향을 줍니다. 나중에 팀이 답할 수 있는 질문이 바뀝니다: 과거 불만을 이해하려는 지원 담당자, 결산을 하려는 재무, 누가 언제 무엇을 바꿨는지 확인하는 컴플라이언스 등. 데이터가 너무 빨리 사라지면 보고서가 달라지고 합계가 맞지 않으며 조사는 추측으로 전락합니다.\n\n유용한 사고 모델:\n\n- 소프트 삭제는 일상적인 뷰에서 레코드를 숨기되 추적 가능성을 유지합니다.\n- 하드 삭제는 레코드를 영구적으로 제거해 개인 데이터 저장을 최소화합니다.\n- 많은 실제 앱은 둘을 함께 씁니다: 비즈니스 기록은 보관하되, 필요 시 개인 식별자를 제거합니다.\n\n실무에서는 사용자 계정을 소프트 삭제해 로그인 못 하게 하고 주문 이력을 보존한 뒤, 보존 기간이 지나거나 검증된 GDPR 삭제 권리 요청이 오면 개인 필드를 하드 삭제하거나 익명화할 수 있습니다.\n\n도구가 결정을 대신해주지는 않습니다. AppMaster 같은 노코드 플랫폼으로 빌드하든 상관없이, 테이블별로 “삭제”가 무엇을 뜻하는지 결정하고 모든 화면, 보고서, API가 같은 규칙을 따르도록 하는 것이 실무 작업입니다.\n\n## 일상적인 앱에서 삭제가 실제로 일으키는 문제들\n\n대부분의 팀은 문제가 생겼을 때만 삭제를 인식합니다. “간단한” 삭제가 문맥, 이력, 설명 능력을 지워버릴 수 있습니다.\n\n하드 삭제는 되돌리기 어렵다는 점에서 위험합니다. 누군가 버튼을 잘못 클릭하거나 자동화 작업에 버그가 있거나 지원 담당자가 잘못된 절차를 따르면 복구 없는 영구 손실이 발생합니다. 깨끗한 백업과 명확한 복원 프로세스가 없다면 비즈니스 영향이 빠르게 드러납니다.\n\n다음은 의외의 문제입니다: 참조가 깨집니다. 고객을 삭제했는데 주문은 남아 있습니다. 이제 주문이 빈 참조를 가리키고, 송장에 청구 이름이 표시되지 않으며 포털이 관련 데이터를 불러오려다 오류가 납니다. 외래 키 제약이 있더라도 “해결”이 더 나쁠 수 있습니다: CASCADE로 설정된 삭제가 의도보다 훨씬 많은 것을 지워버릴 수 있습니다.\n\n분석과 보고도 엉망이 됩니다. 오래된 레코드가 사라지면 지표가 소급적으로 변합니다. 지난달 전환율이 바뀌고, 평생 가치가 떨어지며, 추세선에 설명할 수 없는 구멍이 생깁니다. 팀은 숫자를 두고 싸우게 되고 의사결정은 지연됩니다.\n\n지원과 컴플라이언스에서는 특히 타격이 큽니다. 고객이 “왜 청구됐나요?” 또는 “누가 내 플랜을 바꿨나요?”라고 물으면 레코드가 없으면 타임라인을 재구성할 수 없습니다. 누가, 언제, 무엇을 바꿨는지 답해줄 감사 추적을 잃게 됩니다.\n\n소프트 삭제 vs 하드 삭제 논쟁의 흔한 실패 모드:\n\n- 우발적 삭제나 자동화 버그로 인한 영구 손실\n- 부모 레코드가 없어서 자식(주문, 티켓)이 고아가 되는 경우\n- 과거 행이 사라져 보고서가 변화함\n- 이력 없이는 답하기 불가능한 지원 케이스\n\n## 언제 소프트 삭제를 기본으로 삼아야 하는가\n\n레코드가 장기적으로 가치가 있거나 다른 데이터와 연결되어 있을 때 소프트 삭제가 보통 더 안전한 선택입니다. 행을 제거하는 대신 deleted_at이나 is_deleted로 표시하고 일반 뷰에서 숨깁니다. 소프트 삭제를 기본으로 하면 이후의 놀라움을 줄일 수 있습니다.\n\n이 방식은 데이터베이스 감사 로그가 필요한 곳에서 특히 빛을 발합니다. 운영팀은 종종 “누가 이 주문을 바꿨나?” 또는 “왜 이 인보이스가 취소됐나?” 같은 간단한 질문에 답해야 합니다. 너무 일찍 하드 삭제하면 재무, 지원, 컴플라이언스 보고에 중요한 증거를 잃습니다.\n\n소프트 삭제는 또한 “되돌리기”를 가능하게 합니다. 관리자가 실수로 닫힌 티켓을 복원하거나 보관된 상품을 다시 활성화하거나 잘못된 스팸 신고로 삭제된 사용자 생성 콘텐츠를 복구할 수 있습니다. 데이터가 물리적으로 사라졌다면 이런 복원 흐름을 제공하기 어렵습니다.\n\n관계성도 큰 이유입니다. 부모 행을 하드 삭제하면 외래 키 제약이 깨지거나 보고서에 혼란스러운 구멍이 생깁니다. 소프트 삭제를 사용하면 조인이 안정적으로 유지되고(일일 매출, 이행된 주문, 응답 시간 통계 등) 과거 합계도 일관되게 유지됩니다.\n\n소프트 삭제는 지원 티켓, 메시지, 주문, 인보이스, 감사 로그, 활동 이력, 사용자 프로필 같은 비즈니스 기록에 대한 강력한 기본값입니다(최종 삭제가 확인될 때까지는).\n\n예: 지원 담당자가 실수로 작성한 주문 메모를 “삭제”하는 경우, 소프트 삭제라면 메모는 일반 UI에서 사라지지만 감독자는 불만 제기 시 이를 검토할 수 있고 재무 보고는 설명 가능하게 유지됩니다.\n\n## 언제 하드 삭제가 요구되는가\n\n소프트 삭제는 많은 앱에서 훌륭한 기본이지만, 데이터를 보관하는 것이 잘못된 선택인 경우가 있습니다. 하드 삭제는 레코드를 실제로 제거하며, 때로는 법적, 보안적, 비용적 요구 때문에 유일한 옵션입니다.\n\n가장 명확한 경우는 개인정보와 계약상 의무입니다. 개인이 GDPR 삭제 권리를 행사했거나 계약상 일정 기간 후 삭제를 약속했다면 “표시만 삭제”는 종종 충분하지 않습니다. 행, 관련 복사본, 그리고 해당 인물로 되돌릴 수 있는 식별자를 제거해야 할 수 있습니다.\n\n보안도 이유가 됩니다. 원시 액세스 토큰, 비밀번호 재설정 코드, 개인 키, 일회용 인증 코드, 또는 암호화되지 않은 시크릿 같은 데이터는 보관해서는 안 됩니다. 그런 것을 이력 때문에 보존하는 것은 거의 가치가 없습니다.\n\n스케일을 위해서도 하드 삭제가 적절할 수 있습니다. 오래된 이벤트, 로그, 텔레메트리의 대규모 테이블이 있다면 소프트 삭제는 데이터베이스를 조용히 키워 쿼리를 느리게 만듭니다. 계획된 퍼지 정책은 시스템을 응답성 있게 유지하고 비용을 예측 가능하게 합니다.\n\n하드 삭제는 캐시, 세션, 임시 임포트, 단명 보안 아티팩트(리셋 토큰, OTP, 초대 코드), 테스트/데모 계정, 집계 통계만 필요한 대규모 역사적 데이터에 적합합니다.\n\n실무적으로는 “비즈니스 이력”과 “개인 데이터”를 분리하는 것이 좋습니다. 예를 들어 회계용 인보이스는 보관하되 개인을 식별할 수 있는 사용자 프로필 필드는 하드 삭제하거나 익명화합니다.\n\n팀에서 소프트 삭제 vs 하드 삭제로 논쟁이 생긴다면 간단한 테스트를 사용하세요: 데이터를 보관하는 것이 법적 또는 보안적 위험을 만든다면 하드 삭제(또는 되돌릴 수 없는 익명화)를 선택해야 합니다.\n\n## 예측 가능한 소프트 삭제 모델링 방법\n\n소프트 삭제는 지루하고 예측 가능할 때 가장 잘 작동합니다. 목표는 단순합니다: 레코드는 DB에 남아 있지만 앱의 일반 부분은 마치 사라진 것처럼 동작하게 만드는 것.\n\n### 삭제 신호를 하나로 정하고 그 의미를 명확히 하세요\n\n세 가지 일반 패턴을 보게 됩니다: deleted_at 타임스탬프, is_deleted 플래그, 또는 상태 열거형(enum). 많은 팀이 deleted_at을 선호하는데, 삭제 여부와 시점을 한 번에 알려주기 때문입니다.\n\n이미 여러 수명 상태(active, pending, suspended)가 있다면 상태 enum도 괜찮지만 “deleted”는 “archived”나 “deactivated”와 분리하세요. 이들은 서로 다릅니다:\n\n- Deleted: 일반 목록에 나타나지 않으며 사용 불가능해야 합니다.\n- Archived: 이력 용도로 보관되며 “과거” 뷰에서 보일 수 있습니다.\n- Deactivated: 일시적으로 비활성화되어 사용자가 되돌릴 수 있는 경우가 많습니다.\n\n### 고유 필드는 미리 처리하세요\n\n소프트 삭제 vs 하드 삭제 논쟁은 종종 이메일, 사용자명, 주문 번호 같은 고유 필드에서 깨집니다. 사용자가 삭제된 상태인데 이메일이 여전히 저장되어 고유성을 차지하면 동일한 사람이 재가입할 수 없습니다.\n\n두 가지 일반적 해결책: 고유성 제약을 삭제되지 않은 행에만 적용하거나, 삭제 시 값을 재작성(예: 무작위 접미사 추가)합니다. 어느 쪽을 택할지는 개인정보와 감사 요구에 따라 달라집니다.\n\n### 필터 규칙을 명확히(그리고 일관되게) 만드세요\n\n누가 어떤 것을 볼 수 있는지 결정하세요. 흔한 규칙: 일반 사용자는 삭제된 레코드를 절대 보지 못하고, 지원/관리자는 명확한 라벨과 함께 볼 수 있으며, 내보내기/보고서는 요청이 있을 때만 포함합니다.\n\n"모두가 필터를 추가하는 것을 기억한다"에 의존하지 마세요. 규칙을 한 곳에 두세요: 뷰, 기본 쿼리, 또는 데이터 접근 계층에 넣으세요. AppMaster로 빌드하는 경우에는 엔드포인트와 비즈니스 프로세스에서 이 필터를 내장해 삭제된 행이 실수로 새 화면에 나타나지 않도록 해야 합니다.\n\n의미를 짧은 내부 문서(또는 스키마 주석)에 적어두세요. 나중에 "deleted", "archived", "deactivated"가 같은 회의에 올라왔을 때 고맙게 여길 것입니다.\n\n## 참조를 온전하게 유지하기: 부모, 자식, 조인\n\n삭제는 관계를 통해 앱을 가장 자주 망가뜨립니다. 레코드는 혼자가 아닙니다: 사용자는 주문을 가지고, 티켓은 코멘트를 가지고, 프로젝트는 파일을 가집니다. 소프트 삭제 vs 하드 삭제에서 까다로운 부분은 참조를 일관되게 유지하면서도 제품이 항목을 "사라진" 것처럼 동작하게 하는 것입니다.\n\n### 외래 키: 실패 모드를 의도적으로 선택하세요\n\n외래 키는 깨진 참조로부터 보호하지만 각 옵션은 다른 의미를 가집니다:\n\n- RESTRICT는 자식이 존재하면 삭제를 차단합니다.\n- SET NULL은 삭제를 허용하되 자식을 분리합니다.\n- CASCADE는 자식을 자동으로 삭제합니다.\n- NO ACTION은 많은 DB에서 RESTRICT와 유사하지만 시점이 다를 수 있습니다.\n\n소프트 삭제를 사용한다면 RESTRICT가 종종 가장 안전한 기본값입니다. 행을 유지하므로 키가 유효하고 자식 레코드가 아무것도 가리키지 않는 상황을 피할 수 있습니다.\n\n### 관계에서의 소프트 삭제: 고아 없이 숨기기\n\n소프트 삭제는 보통 외래 키를 변경하지 않습니다. 대신 앱과 보고서에서 삭제된 부모를 필터링합니다. 고객이 소프트 삭제되면 인보이스는 여전히 조인되어야 하지만 화면에서는 드롭다운에 고객이 나타나지 않아야 합니다.\n\n첨부 파일, 코멘트, 활동 로그에 대해서는 “삭제”가 사용자에게 어떤 의미인지 결정하세요. 일부 팀은 쉘을 유지하되 위험한 부분만 제거합니다: 개인정보 보호가 필요하면 첨부 내용을 플레이스홀더로 교체하고, 코멘트 작성자를 익명화하거나 삭제된 사용자로 표시하며, 활동 로그는 불변으로 유지합니다.\n\n조인과 보고서는 삭제된 행을 포함할지에 대한 명확한 규칙이 필요합니다. 많은 팀은 두 가지 표준 쿼리를 유지합니다: 하나는 "활성만", 다른 하나는 "삭제 포함". 지원과 보고서가 중요한 이력을 실수로 숨기지 않도록 하기 위함입니다.\n\n## 단계별: 둘 다 사용하는 데이터 수명주기 설계\n\n실용적인 정책은 흔히 실무에서 소프트 삭제를 실수와 일상 복구용으로 쓰고, 법적/개인정보 필요에 따라 하드 삭제를 하는 방식을 사용합니다. 소프트 삭제 vs 하드 삭제라는 하나의 결정으로만 보면 중간 지대를 놓치게 됩니다: 이력을 잠시 보관한 뒤 반드시 제거해야 할 것을 정기적으로 퍼지하는 접근이 필요합니다.\n\n### 간단한 5단계 계획\n\n데이터를 몇 가지 버킷으로 분류하세요. “사용자 프로필”은 개인적, “거래”는 재무 기록, “로그”는 시스템 이력처럼 각 버킷은 다른 규칙이 필요합니다.\n\n대부분 팀에 효과적인 짧은 계획:\n\n- 데이터 그룹과 담당자를 정의하고 누가 삭제를 승인하는지 명시하세요.\n- 보존 및 복원 규칙을 정하세요.\n- 제거 대신 익명화할 항목을 결정하세요.\n- 타임드 퍼지 단계(지금 소프트 삭제, 나중에 하드 삭제)를 추가하세요.\n- 삭제, 복원, 퍼지마다 누가 언제 왜 했는지 감사 이벤트를 기록하세요.\n\n### 하나의 시나리오로 실천해보기\n\n고객이 계정 해지를 요청했다고 합시다. 즉시 사용자 레코드를 소프트 삭제해 로그인하지 못하게 하고 참조가 깨지지 않게 합니다. 그다음 남아 있어서는 안 되는 개인 필드를 익명화(이름, 이메일, 전화)하고, 회계에 필요한 비개인 거래 정보는 유지합니다. 마지막으로 예약된 퍼지 작업이 대기 기간 후에도 남아 있는 개인 정보를 제거합니다.\n\n## 흔한 실수와 함정 피하기\n\n팀이 틀리는 이유는 종종 잘못된 접근 때문이 아니라 일관성 없이 적용하기 때문입니다. 흔한 패턴은 문서상으로는 “소프트 삭제 vs 하드 삭제”인데 실제로는 "한 화면에서만 숨기고 나머지는 잊음"입니다.\n\n쉬운 실수 하나: UI에서 삭제된 레코드를 숨기지만 API, CSV 내보내기, 관리자 도구, 데이터 동기 작업에서는 여전히 표시되는 경우입니다. 사용자는 삭제된 고객이 이메일 목록이나 모바일 검색에 다시 나타나면 곧바로 알아차립니다.\n\n보고서와 검색도 함정입니다. 보고서 쿼리가 삭제된 행을 일관되게 필터링하지 않으면 합계가 흔들리고 대시보드 신뢰도가 떨어집니다. 최악의 사례는 백그라운드 작업이 같은 규칙을 적용하지 않아 삭제된 항목을 재색인하거나 재발송하는 경우입니다.\n\n하드 삭제도 과도하게 될 수 있습니다. 단일 연쇄 삭제가 실제로 감사에 필요했던 주문, 인보이스, 메시지, 로그를 전부 지워버릴 수 있습니다. 하드 삭제가 꼭 필요하다면 무엇을 사라지게 허용할지, 무엇을 보관하거나 익명화할지 명시적으로 정하세요.\n\n소프트 삭제에서 고유 제약은 미묘한 고통을 유발합니다. 사용자가 계정을 삭제한 뒤 같은 이메일로 재가입하려 하면 이전 행이 여전히 고유 이메일을 가지고 있다면 가입이 실패할 수 있습니다. 이 점을 초기에 계획하세요.\n\n컴플라이언스 팀은 묻습니다: 삭제가 언제 어떻게 이루어졌는지 증명할 수 있나? “삭제된 것 같다”는 많은 데이터 보존 정책 검토에서 통과하지 못합니다. 삭제 타임스탬프, 누가/무엇이 트리거했는지, 불변 로그 항목을 보관하세요.\n\n출시 전에 전체 표면을 점검하세요: API, 내보내기, 검색, 보고서, 백그라운드 작업. 테이블별 연쇄 삭제를 검토하고, 제품 약속의 일부라면 사용자가 이메일이나 사용자명 같은 고유 데이터를 재생성할 수 있는지도 확인하세요.\n\n## 출시 전 빠른 체크리스트\n\nsoft delete vs hard delete를 결정하기 전에 스키마가 아닌 앱의 실제 동작을 검증하세요.\n\n- 복원은 안전하고 예측 가능한가. 관리자가 "undelete"하면 필요한 상태로 정확히 돌아오며, 제거돼야 할 민감 항목(예: 해지된 액세스 토큰)은 되살아나지 않나요?\n- 쿼리는 기본적으로 삭제된 데이터를 숨기는가. 새 화면, 내보내기, API가 실수로 삭제된 행을 포함하지 않도록 하나의 규칙을 정하고 모든 곳에 적용하세요.\n- 참조가 깨지지 않는가. 외래 키와 조인이 고아 레코드나 빈 화면을 만들지 않도록 확인하세요.\n- 퍼지 일정과 담당자가 있는가. 소프트 삭제는 계획의 절반에 불과합니다. 데이터가 언제 영구 제거될지, 누가 실행할지, 어떤 항목을 제외할지 정의하세요(예: 진행 중인 분쟁).\n- 삭제는 다른 민감 작업처럼 로깅되는가. 누가 언제 왜 삭제했는지 기록하세요.\n\n그런 다음 개인정보 경로를 엔드투엔드로 테스트하세요. GDPR 삭제 권리 요청을 실제로 이행할 수 있나요? 메인 DB뿐 아니라 복사본, 내보내기, 검색 인덱스, 분석 테이블, 통합까지 모두 확인해야 합니다.\n\n실용적인 검증 방법은 스테이징에서 하나의 "사용자 삭제" 드라이런을 실행하고 데이터 추적을 따라가는 것입니다.\n\n## 예시: 결제 이력은 유지하면서 사용자 삭제 처리하기\n\n고객이 “계정 삭제해 주세요”라고 요청했습니다. 회계상 남겨둬야 하는 인보이스가 있습니다. 이럴 때 소프트 삭제 vs 하드 삭제는 실무적 문제가 됩니다: 접근을 제거하고 개인 정보를 제거하면서 비즈니스에 필요한 재무 기록은 보존할 수 있습니다.\n\n“계정”과 “결제 기록”을 분리하세요. 계정은 로그인과 식별에 관한 것이고, 결제 기록은 이미 발생한 거래에 관한 것입니다.\n\n깔끔한 접근:\n\n- 사용자 계정은 소프트 삭제해 로그인 불가 상태로 만들고 일반 뷰에서 보이지 않게 합니다.\n- 인보이스와 결제는 기록으로 유지하되 개인 식별 필드는 더 이상 연결하지 않습니다.\n- 개인 데이터(이름, 이메일, 전화, 주소)는 “삭제된 사용자” 같은 중립 값이나 비식별 내부 참조로 익명화합니다.\n- API 토큰, 비밀번호 해시, 세션, 리프레시 토큰, 등록된 장치 등 민감 항목은 하드 삭제합니다.\n- 규정 준수와 지원에 진짜로 필요한 것만 유지하고 그 이유를 문서화하세요.\n\n지원 티켓과 메시지는 중간지대에 있습니다. 메시지 내용에 개인 데이터가 포함되어 있으면 텍스트 일부를 편집하거나 첨부를 제거해야 할 수 있고, 티켓의 쉘(타임스탬프, 카테고리, 해결 여부)은 품질 추적을 위해 유지할 수 있습니다. 제품이 이메일/SMS, Telegram 등을 전송했다면 발송 대상 식별자도 제거해 더 이상 연락되지 않도록 하세요.\n\n지원팀이 볼 수 있는 것은 보통 인보이스 번호, 날짜, 금액, 상태, 사용자가 삭제되었고 언제 삭제되었는지에 대한 노트 정도입니다. 볼 수 없는 것은 로그인 이메일, 전체 이름, 주소, 저장된 결제 수단 상세, 활성 세션 같은 식별 정보입니다.\n\n## 다음 단계: 규칙을 정하고 일관되게 구현하세요\n\n삭제 결정은 문서로 남기고 제품 전반에 일관되게 구현될 때만 지속됩니다. soft delete vs hard delete는 코딩 트릭이 아니라 정책 문제로 먼저 다루세요.\n\n간단한 데이터 보존 정책을 만들어 팀 누구나 읽을 수 있게 하세요. 무엇을 보관하고, 얼마나 오래 보관하며, 그 이유를 명시하세요. "왜"가 중요합니다. 이유는 서로 충돌하는 목표(예: 지원 이력 vs 개인정보 요청)에서 어느 쪽이 우선인지 알려줍니다.\n\n좋은 기본값은 종종: 일상 비즈니스 기록(주문, 티켓, 프로젝트)은 소프트 삭제, 진짜 민감한 데이터(토큰, 시크릿)는 하드 삭제로 하는 것입니다.\n\n정책이 명확해지면 이를 강제하는 흐름을 구축하세요: 복원을 위한 "휴지통" 뷰, 확인 절차가 있는 "퍼지 큐", 누가 언제 무엇을 했는지 보여주는 감사 뷰. "퍼지"는 "삭제"보다 더 어렵게 만들어 실수로 사용되지 않게 하세요.\n\nAppMaster(appmaster.io)에서 구현한다면 Data Designer에 소프트 삭제 필드를 모델링하고 삭제/복원/퍼지 로직을 하나의 비즈니스 프로세스에 중앙화해 화면과 API 전반에 동일한 규칙이 적용되도록 하는 것이 도움이 됩니다.
자주 묻는 질문
하드 삭제는 데이터베이스에서 행을 물리적으로 제거하므로 이후 쿼리로 찾을 수 없습니다. 소프트 삭제는 행을 보관하되 deleted_at 같은 표시로 삭제된 것으로 마크해, 일반 화면에서는 숨기면서도 지원, 감사, 보고를 위해 이력을 보존합니다.
주로 주문, 인보이스, 티켓, 메시지, 계정 활동처럼 나중에 설명이 필요할 수 있는 비즈니스 기록에는 기본적으로 소프트 삭제를 사용하는 것이 좋습니다. 우발적 데이터 손실을 줄이고 관계를 유지하며 백업 복원 없이도 안전한 "되돌리기"를 가능하게 합니다.
개인정보나 보안 위험을 초래하거나 보존 규정상 진정한 제거가 필요한 경우 하드 삭제가 적합합니다. 예시로는 비밀번호 재설정 토큰, 일회용 코드, 세션, API 토큰, 검증된 삭제 요청에 따라 지워져야 하는 개인 데이터 등이 있습니다.
deleted_at 타임스탬프는 레코드가 삭제되었는지 그리고 언제 삭제되었는지를 동시에 알려주기 때문에 실무에서 자주 쓰입니다. 이를 통해 보존 기간(예: 30일 후 퍼지) 관리와 감사 질문(“언제 삭제됐나?”)을 별도 로그 없이 해결할 수 있습니다.
이메일이나 사용자명 같은 고유 필드는 삭제된 행이 여전히 값을 가지고 있으면 재가입을 막을 수 있습니다. 일반적 해결책은 고유성을 deleted가 아닌 행에만 적용하거나, 삭제 시 해당 값을 수정(예: 무작위 접미사 추가)해 충돌을 피하는 것입니다. 선택은 개인정보와 감사 요구에 따라 달라집니다.
부모를 하드 삭제하면 자식 레코드가 고아가 되거나 의도치 않은 연쇄 삭제가 발생할 수 있습니다. 소프트 삭제는 외래 키를 유지하므로 고아 문제를 피하지만, 삭제된 부모가 드롭다운이나 사용자용 조인에 표시되지 않도록 일관된 필터링이 필요합니다.
과거 행을 하드 삭제하면 이전 합계가 바뀌고 추세에 구멍이 생기며 재무 수치가 일치하지 않는 등 보고서 문제가 생깁니다. 소프트 삭제는 이력을 보존하지만, 보고와 분석 쿼리가 삭제된 행을 포함할지 여부를 명확히 정의하고 일관되게 적용해야 합니다.
“소프트 삭제”만으로는 GDPR 삭제 권리 요구를 충족하지 못할 수 있습니다. 실무 패턴은 접근을 즉시 차단한 뒤 식별 가능한 개인 식별자를 영구 익명화하거나 하드 삭제하고, 회계나 분쟁 처리에 필요한 비식별 거래 사실은 보관하는 방식입니다.
복원은 레코드를 안전하고 올바른 상태로 되돌려야 하며, 유지해서는 안 되는 민감 항목(세션, 리셋 토큰 등)을 불필요하게 되살려서는 안 됩니다. 또한 관련 데이터에 대한 명확한 규칙이 있어 계정만 복원되고 필수 관계나 권한이 누락되는 일이 없어야 합니다.
삭제, 복원, 퍼지 동작을 중앙화해 모든 API, 화면, 내보내기, 백그라운드 작업이 같은 필터 규칙을 적용하도록 하세요. AppMaster(appmaster.io)를 쓴다면 Data Designer에 소프트 삭제 필드를 모델링하고 비즈니스 프로세스 하나에 로직을 넣어 새로운 엔드포인트가 삭제 데이터를 노출하지 않도록 만드는 것이 일반적입니다.


