Blog

데이터 삭제 요구사항 처리법: 삭제 플래그 vs 이관 테이블 vs 물리 삭제

2026-10-07

A minimalist photo of a card with 'Big Data' text inside a green envelope, showcasing modern concepts.

Photo by alleksana on Pexels

1. 데이터 삭제 요구사항을 전달받은 신입 개발자의 고민

기능 명세서에 "사용자가 작성한 게시글을 삭제할 수 있어야 한다" 또는 "탈퇴한 회원 정보를 삭제 처리한다"라는 한 줄짜리 요구사항이 들어오면, 업무를 막 맡게 된 신입 실무자는 단순하게 DELETE FROM table_name WHERE id = 10; 문을 떠올리기 쉽습니다. 하지만 실무 운영 환경에서 데이터베이스의 행을 직접 지우는 작업은 수많은 부작용을 낳습니다.

실제로 서비스를 운영하다 보면 결제 내역이나 통계 데이터가 기존 데이터와 연결되어 있어 물리 삭제 시 외래키 참조 에러가 발생하거나, 고객센터를 통해 "실수로 누른 삭제 버튼을 취소해 달라"는 요청이 빈번하게 인입됩니다. 또한 개인정보보호법 등 관련 법령에 따라 특정 기간 동안 반드시 보관해야 하는 데이터가 존재하는 반면, 사용자의 완전 파기 요청에 따라 지체 없이 삭제해야 하는 개인정보도 존재합니다.

이러한 비즈니스 요구사항과 운영상의 이점을 모두 충족하기 위해 백엔드 개발자는 애플리케이션 설계 단계에서 적절한 데이터 삭제 전략을 선택해야 합니다. 가장 대표적으로 쓰이는 세 가지 방식인 삭제 플래그 방식(Soft Delete), 별도 이관 테이블 분리 방식(Archive Table), 그리고 물리 삭제 및 변경 이력 결합 방식(Hard Delete + Audit Log)의 구조와 장단점, 그리고 적용 기준을 상세히 알아보겠습니다.

2. 방식 1: 삭제 플래그 컬럼 추가 (Soft Delete)

삭제 플래그 방식은 실제 DB에서 데이터를 삭제하지 않고, 해당 행이 삭제되었음을 나타내는 상태 값을 컬럼으로 관리하는 방법입니다. 가장 흔하게 사용되며 구현이 매우 직관적입니다.

구현 방식 및 예시

테이블에 is_deleted(Boolean) 컬럼을 추가하거나, 삭제된 시점을 기록하는 deleted_at(DateTime) 컬럼을 추가합니다. 데이터 삭제 요청이 들어오면 DELETE 쿼리 대신 UPDATE 쿼리를 실행합니다.

[데이터 삭제 요청 시 쿼리]

UPDATE orders SET deleted_at = '2025-02-15 14:30:00' WHERE id = 10050 AND user_id = 42;

[데이터 조회 요청 시 쿼리]

SELECT * FROM orders WHERE user_id = 42 AND deleted_at IS NULL;

장점

  • 구현의 용이성: 기존 스키마에 컬럼 하나만 추가하고 UPDATE 문을 호출하면 되므로 데이터베이스 구조 변경 폭이 작습니다.
  • 손쉬운 데이터 복구: 데이터 복구 요청이 들어왔을 때 deleted_at 컬럼을 다시 NULL로 변경하는 것만으로 즉시 복구할 수 있습니다.
  • 참조 무결성 유지: 타 테이블에서 해당 PK를 외래키로 참조하고 있더라도 제약조건 위반 에러가 발생하지 않습니다.

단점 및 주의점

  • 조회 성능 저하: 모든 SELECT 쿼리에 'WHERE deleted_at IS NULL' 조건이 붙어야 합니다. 삭제된 데이터가 누적될수록 인덱스 크기가 커지고 실제 조회 성능에 악영향을 미칩니다.
  • 유니크 제약조건(UNIQUE Index) 충돌: 예를 들어 회원 테이블의 email 컬럼에 UNIQUE 제약조건이 걸려있을 때, 탈퇴한 회원의 email이 삭제 플래그만 처리된 채 남아있다면 동일한 email로 재가입이 불가능해집니다. 이를 해결하려면 (email, deleted_at) 형태로 복합 유니크 인덱스를 생성하는 등 추가 조치가 필요합니다.
  • 개발자 실수 가능성: 새롭게 작성하는 조회 API나 복잡한 JOIN 쿼리에서 'deleted_at IS NULL' 조건을 누락할 경우, 삭제된 데이터가 사용자 화면에 노출되는 치명적인 버그가 발생합니다.

3. 방식 2: 별도 이관 테이블 분리 (Archive Table)

운영 테이블에는 현재 유효한 데이터만 남겨두고, 삭제 요청이 들어온 데이터는 동일하거나 유사한 구조를 가진 별도의 아카이브 테이블로 이동시키는 방식입니다.

구현 방식 및 예시

원본 테이블이 orders라면, 구조가 거의 동일한 orders_archive 테이블을 사전에 생성해 둡니다. 데이터 삭제 요청이 발생하면 단일 트랜잭션 내에서 이관 등록과 물리 삭제를 순차적으로 수행합니다.

[데이터 삭제 및 이관 트랜잭션 예시]

BEGIN TRANSACTION;

INSERT INTO orders_archive (id, user_id, amount, created_at, deleted_at) SELECT id, user_id, amount, created_at, NOW() FROM orders WHERE id = 10050;

DELETE FROM orders WHERE id = 10050;

COMMIT;

장점

  • 운영 테이블의 최적 성능 유지: 삭제된 데이터가 원본 테이블에 남지 않으므로, 데이터베이스 인덱스 크기가 작게 유지되고 SELECT 조회 쿼리 성능이 최상으로 유지됩니다.
  • 쿼리 작성의 간결함: 일반 조회 쿼리에서 'deleted_at IS NULL' 같은 조건절을 매번 넣을 필요가 없어 개발자의 비즈니스 로직 구현 실수를 줄일 수 있습니다.
  • 유니크 제약조건 청정 구역: 원본 테이블에서 물리적으로 행이 제거되므로 이메일, 아이디 등의 UNIQUE 인덱스 충돌 문제가 전혀 발생하지 않습니다.

단점 및 주의점

  • 스키마 변경(DDL) 시 공수 증가: 원본 테이블에 새로운 컬럼이 추가되거나 타입이 변경될 때마다 이관 테이블(orders_archive)에도 동일한 스키마 변경 작업을 동시 적용해야 합니다.
  • 트랜잭션 처리 비용: INSERT와 DELETE라는 두 번의 DML 작업이 하나의 트랜잭션으로 묶여야 하므로, 단일 UPDATE 처리보다 쓰기 작업에 대한 부하가 약간 높습니다.
  • 외래키 제약조건 처리 복잡성: 부모 테이블의 데이터를 아카이브 테이블로 이동시킬 때 자식 테이블의 데이터를 어떻게 처리할 것인지(함께 아카이브할 것인지, 외래키를 해제할 것인지)에 대한 명확한 설계가 필요합니다.

4. 방식 3: 물리 삭제와 변경 이력 테이블 결합 (Hard Delete + Audit Log)

원칙적으로 운영 테이블의 데이터는 완전히 지우는 물리 삭제(DELETE)를 수행하되, '누가, 언제, 어떤 이유로 삭제했는지'와 삭제 직전의 최소한의 Snapshot 데이터를 공통 이력 로그(Audit Log) 테이블에 JSON 형태 등으로 남기는 방식입니다.

구현 방식 및 예시

특정 비즈니스 테이블에 국한되지 않고 시스템 전체에서 공통으로 사용할 수 있는 audit_logs 테이블을 구축하여 활용합니다.

[데이터 삭제 및 이력 남기기 예시]

BEGIN TRANSACTION;

INSERT INTO audit_logs (target_table, target_id, action_type, payload, executed_by, created_at) VALUES ('orders', 10050, 'DELETE', '{"user_id": 42, "amount": 15000}', 'admin_user', NOW());

DELETE FROM orders WHERE id = 10050;

COMMIT;

장점

  • 법적 요구사항 및 개인정보 파기 준수: 개인정보보호 관련 법령이나 GDPR의 '잊힐 권리'에 맞춰 사용자의 개인식별정보를 완전히 파기해야 할 때 완벽한 물리 삭제를 보장합니다.
  • 공통화 및 확장성: 모든 테이블의 생성/수정/삭제 이력을 하나의 공통 audit_logs 구조 또는 로그 파이프라인으로 처리할 수 있어 테이블별 아카이브 구조를 별도로 만들지 않아도 됩니다.
  • 디스크 용량 절감: 전체 행 데이터를 복사해 보관하는 대신 감사(Audit)에 필요한 핵심 식별 데이터와 변경 사유만 저장하므로 저장소 효율이 높습니다.

단점 및 주의점

  • 복구의 어려움: 공통 이력 테이블에 JSON 상태로 저장된 데이터를 기반으로 복잡한 관계형 데이터를 원상 복구하려면 별도의 복구 스크립트를 작성해야 하며, 완벽한 복구가 불가능할 수 있습니다.
  • 관계형 조인 불가: JSON 형태나 텍스트로 보관된 이력 데이터는 표준 SQL JOIN 문을 통한 과거 데이터 조회가 어렵습니다.

5. 세 가지 데이터 삭제 방식 장단점 비교 표 및 상황별 선택 기준

업무 현장에서 상황에 알맞은 방식을 신속하게 선택할 수 있도록 주요 항목별로 비교한 내용입니다.

구분 삭제 플래그 (Soft Delete) 이관 테이블 분리 (Archive) 물리 삭제 + 이력 (Hard Delete)
구현 난이도 낮음 (컬럼 추가 및 UPDATE) 보통 (이관 테이블 관리 필요) 보통 (공통 로그 모듈 구축)
조회 성능 영향 데이터 누적 시 성능 저하 가능성 높음 원래 테이블 성능 영향 없음 (최상) 원래 테이블 성능 영향 없음 (최상)
데이터 복구 용이성 매우 쉬움 (플래그 원복) 쉬움 (역이관 쿼리 실행) 어려움 (JSON 파싱 및 수동 재등록)
유니크 제약조건 충돌 방지 대책 필수 (복합인덱스 등) 영향 없음 영향 없음
개인정보 파기 적합성 부적합 (DB에 데이터 잔재) 부적합 (이관 테이블에 잔재) 매우 적합 (완전 파기 달성)
추천 사용 상황 삭제 취소가 빈번한 단순 게시판/설정 대용량 거래/주문 데이터, 장기 보관용 개인정보 탈퇴, 법적 파기 대상 데이터

의사결정을 위한 상황별 선택 트리

  1. 질문 1. 해당 데이터가 개인정보보호법 등 법령에 의해 완전히 파기되어야 하는가?
    • Yes: '물리 삭제 + 변경 이력 결합' 방식을 선택하십시오. 이력 테이블에도 이름, 전화번호 등 개인식별정보가 마스킹 처리되거나 아예 남지 않도록 설계해야 합니다. (법령 관련 상세 세부 규정은 최신 개인정보보호 가이드라인 및 관련 법률 조항을 반드시 직접 확인하세요.)
  2. 질문 2. 일간/월간 데이터 생성량이 수십만 건 이상이며, 활성 데이터만 주로 조회하는가?
    • Yes: '별도 이관 테이블 분리' 방식을 선택하십시오. 메인 테이블의 크기를 일정 수준 이하로 유지해야 서비스 전반의 응답 속도를 확보할 수 있습니다.
  3. 질문 3. 사용자의 실수로 인한 복구 요청이 잦고, 전체 데이터 양이 많지 않은 관리자 CRUD 기능인가?
    • Yes: '삭제 플래그 추가(Soft Delete)' 방식을 선택하십시오. 빠른 개발 속도와 편리한 복구 기능을 제공할 수 있습니다.

6. 실무 적용 전 반드시 체크해야 할 5가지 점검 목록

신입 개발자가 데이터 삭제 기능을 구현하고 배포하기 전에 사고를 방지하기 위해 점검해야 할 핵심 체크리스트입니다.

1) UNIQUE 제약조건 재검증

Soft Delete 방식을 사용할 때, 삭제된 행의 중복 값 때문에 새로운 데이터가 INSERT되지 않는 현상이 발생하는지 테스트했는지 확인하세요. 예를 들어 재가입 시 동일한 이메일 등록이 막히는 현상이 대표적입니다.

2) 외래키(Foreign Key) 및 Cascade 파급 효과 확인

부모 테이블 데이터 삭제 시 자식 테이블의 FK 설정(ON DELETE CASCADE, ON DELETE SET NULL 등)이 어떻게 작동하는지 파악했는지 확인하세요. 물리 삭제 시 원치 않는 연관 데이터까지 한꺼번에 지워지는 사고를 방지해야 합니다.

3) 법령 및 내부 데이터 보관 주기 준수 여부

전자상거래법, 통신비밀보호법 등 서비스 도메인에 따라 거래 내역, 접속 로그 등의 법적 의무 보관 기간이 다릅니다. 파기 대상 데이터인지, 아니면 일정 기간 암호화하여 보관해야 하는 데이터인지 기획자 및 법무 담당자와 확인했는지 체크하세요.

4) 이관 테이블 스키마 동기화 절차

Archive Table 방식을 채택한 경우, 향후 원본 테이블에 ALTER TABLE 명령어로 컬럼을 추가할 때 아카이브 테이블도 함께 변경하는 Migration 스크립트가 준비되었는지 확인하세요.

5) 복합 인덱스(Index) 및 조회 조건 최적화

Soft Delete 방식 적용 시, 기존에 설정된 인덱스에 deleted_at 컬럼을 포함시켜야 하는지 execution plan(EXPLAIN)을 통해 확인했는지 점검하세요. 인덱스 타지 않는 Full Table Scan을 방지해야 합니다.

7. 신입 실무자를 위한 핵심 실행 요약

  1. 단순 DELETE 쿼리 금지: 요구사항에 '삭제'가 명시되어 있더라도 무조건적인 DELETE 문 실행은 지양하고, 비즈니스 요구사항과 데이터 보관 정책을 먼저 파악하세요.
  2. 데이터 특성에 따른 전략 이원화: 회원 탈퇴 및 개인정보는 '물리 삭제 + 최소 이력 로그', 대용량 거래 및 주문 내역은 '이관 테이블 분리', 단순 관리자 설정 데이터는 '삭제 플래그(Soft Delete)'를 우선 고려하세요.
  3. Soft Delete 사용 시 인덱스와 UNIQUE 주의: deleted_at 컬럼을 추가할 경우 모든 SELECT 조건절 수정과 복합 유니크 인덱스 설정을 잊지 마세요.
  4. 아카이브 작업 시 트랜잭션 보장: 이관 테이블 방식 적용 시 INSERT와 DELETE 작업은 반드시 하나의 DB 트랜잭션(Transaction) 내에서 안전하게 수행하세요.

인클루드웹 솔루션이 궁금하신가요?

건설관리, 프로젝트관리, 영업관리 솔루션 도입을 무료로 상담해 드립니다.