이커머스 회원 탈퇴 데이터 보관 로직 설계

이커머스 프로젝트에서 회원 탈퇴 기능을 개발하던 중, 생각보다 오래 붙잡고 고민하게 된 지점이 하나 있었다.
회원 탈퇴라는 기능 자체는 흔하다. 버튼도 있고, 상태값만 바꾸면 되고, 일정 시간이 지나면 삭제 처리하면 될 것처럼 보인다. 그런데 막상 구현하려고 보면 생각보다 단순하지 않다.
탈퇴를 요청한 회원의 개인정보는 어디까지 지워야 하는지,
주문이나 결제 같은 거래 데이터는 어떻게 남겨야 하는지,
리뷰나 QnA, 1:1 문의 같은 사용자 작성 데이터는 익명화해야 하는지,
그리고 user 테이블을 참조하는 수많은 연관 테이블은 또 어떻게 처리해야 하는지.
처음에는 단순히 14일 유예 후 삭제 정도로 생각했는데, 조금만 더 구조를 들여다보니 이건 단순한 삭제 기능이 아니라 개인정보 정책, 데이터 정합성, 운영 편의성까지 함께 고려해야 하는 설계 문제에 더 가까웠다.
그래서 이 글은 회원 탈퇴 기능 구현 기록이라기보다, 회원 탈퇴를 설계하면서 어떤 고민과 의문을 가졌고 왜 지금의 방향으로 정리하게 되었는지를 담은 글이다.
처음 내가 정리했던 기준
당시 먼저 정리했던 기준은 아래와 같았다.
당초 계획
- 회원 탈퇴 후 14일 유예기간이 지나면 최종 삭제 처리
- 리뷰는 '탈퇴한 사용자' 또는 '회원' 등으로 표시
- 회원 복구는 고객센터 전화로만 가능
- 결제 내역은 3~5년간 보관
이 정도만 놓고 보면 얼핏 명확해 보인다. 탈퇴 요청을 받으면 유예기간을 두고, 14일이 지나면 유저 정보를 지우면 되는 것처럼 느껴진다. 그런데 실제 서비스 로직으로 가져오면 바로 복잡해진다.
탈퇴는 언제나 바로 가능한 게 아니었다
가장 먼저 부딪힌 건 탈퇴 자체가 불가능한 경우였다. 예를 들어 아래 상태가 하나라도 남아 있으면 회원 탈퇴를 허용하면 안 된다고 판단했다.
- 결제 완료, 배송 준비 중, 배송 중
- 교환/반품 진행 중, 환불 처리 중
이유는 단순하다. 주문 계약이 아직 종료되지 않았기 때문이다. 이 상태에서 회원이 탈퇴해 버리면 배송 책임이나 환불 책임이 불분명해질 수 있고, 운영 측면에서도 대응이 매우 어려워진다.
여기서 끝이 아니었다. 배송이 끝났다고 바로 탈퇴가 가능한 것도 아니었다. 배송 완료 후에도 반품 가능 기간(14일) 동안은 탈퇴를 제한해야 했다. 배송이 끝났더라도 거래 관계가 완전히 종료된 것은 아니라고 보는 쪽이 더 자연스러웠다.
이 지점에서 느꼈다.
회원 탈퇴는 사용자의 의사만으로 즉시 끝나는 기능이 아니라, 거래 상태와도 강하게 연결된 기능이라는 것을.
탈퇴가 진행된다면, 무엇을 지우고 무엇을 남겨야 할까?
탈퇴 제한 조건을 정리하고 나니 바로 다음 질문이 생겼다.
탈퇴가 실제로 진행되면 어떤 데이터는 삭제해야 하고, 어떤 데이터는 남겨야 할까?
당시 막연하게 구분했던 건 이 정도였다.
삭제되지 않을 것 같은 항목
주문 내역, 리뷰, 교환/반품/환불 내역, 결제 내역, 로그인 내역, 상품 QnA, 1:1 문의 등
삭제될 것 같은 항목
user 테이블의 개인정보, 배송지 정보
문제는 이 구분이 생각보다 단순하지 않았다는 점이다. 처음에는 민감한 정보니까 유저를 삭제하면 되겠다고 생각했는데, "user 테이블을 참조하던 테이블들은 어떻게 되는 거지?" 라는 의문이 따라왔다.
이커머스 서비스에서 user는 단순한 한 행(row)이 아니다. 주문, 결제, 환불 등 거의 모든 데이터가 user와 연결되어 있기 때문이다.
처음에는 Hard Delete를 떠올렸다
처음 생각은 꽤 단순했다. 고객 데이터는 민감하니까, 탈퇴 14일 유예가 끝나면 그때 고객 데이터를 물리적으로 삭제(Hard Delete) 하면 되는 것 아닌가?
그런데 그다음 생각은 그리 단순하지 않았다.
"그럼 user를 참조하는 자식 테이블들은 어떻게 하지? cascade로 같이 삭제하면 되나?"
이건 꽤 위험한 생각이었다. user 하나를 삭제하는 순간, 그 회원이 남긴 리뷰나 QnA, 1:1 문의 같은 연관 데이터까지 줄줄이 사라질 수 있기 때문이다.
이 시점부터는 삭제라는 행위 하나가 단순한 개인정보 처리 문제가 아니라, 관계형 데이터 전체를 흔드는 일처럼 느껴졌다.
그래서 처음 떠올린 방식은 이랬다
당시 가장 먼저 떠올린 구현 방식은 아래와 같았다.
- 회원 탈퇴 시 탈퇴 시각을 기록한다.
- 14일 뒤 스케줄러를 돌려 유저 정보를 삭제한다.
- 유저를 가지고 있어야 하는 테이블에서는 작성자 이름을 탈퇴한 회원으로 바꿔 관리한다.
처음엔 꽤 괜찮아 보였지만, 구조를 조금 더 생각해 보니 이 방식도 문제가 많았다. user row가 실제로 사라지면 기존에 user를 FK로 참조하던 테이블은 더 이상 관계를 유지할 수 없다. 그렇게 되면 결국 다음 일이 생긴다.
- FK를 끊고, 필요한 테이블마다 userName 컬럼을 따로 만들어야 한다.
- 탈퇴 시점에 일일이 값을 넣어 줘야 하고, 표시 정책도 파편화된다.
결국 user 한 줄을 삭제하기 위해 파생되는 설계 비용이 너무 커진다. 삭제가 오히려 구조를 더 복잡하게 만들 수도 있다는 걸 깨달은 순간이었다.
그러다 Soft Delete라는 방향을 보게 됐다
더 나은 방식이 없는지 찾아보다가 하나의 대안을 보게 됐다. user 테이블의 PK를 다른 테이블들이 참조하고 있으니, Hard Delete 대신 Soft Delete를 하고 개인정보 영역만 null 처리하는 방식이다.
처음엔 꽤 그럴듯하게 들렸다. 하지만 나는 GPT 같은 AI가 제안했다는 이유만으로 바로 설계를 채택하는 편은 아니다. 오히려 그때부터 더 많이 찾아본다.
- 이 방식이 실제로도 많이 쓰이는가?
- 다른 개발자들도 비슷하게 구현하는가?
- 예외 상황이나 사이드 이펙트는 없는가?
AI가 설계의 뼈대를 빠르게 잡아 주는 건 분명 큰 도움이 된다. 하지만 그게 정말 채택해도 되는 구조인지 검증하는 건 결국 개발자의 몫이다. 그래서 회원 탈퇴를 다룬 다양한 사례를 찾아보기 시작했다.
사례를 찾아보면서 생각이 정리되기 시작했다
여러 글을 보면서 느낀 건, 회원 탈퇴를 Hard Delete 중심으로 푸는 것보다 Soft Delete와 익명화 중심으로 푸는 방식이 훨씬 현실적이라는 점이었다.
이커머스에서는 더 그랬다. 이커머스의 핵심 데이터는 회원 정보만이 아니기 때문이다.
- 주문과 결제는 법적으로 보관해야 하고, 환불 기록도 남아야 한다.
- 리뷰와 문의 데이터도 서비스 운영 자산으로 기능한다.
- 무엇보다 대부분의 데이터가
USER_ID를 중심으로 연결되어 있다.
이 상황에서 유저를 물리적으로 삭제해 버리면 데이터 구조 전체가 흔들릴 가능성이 크다. 유저를 없애는 것보다 탈퇴 상태의 익명 데이터로 남기는 쪽이 훨씬 실무적이라는 결론에 도달했다.
그래서 내가 잡은 설계 방향
최종적으로 정리한 방향은 이랬다.
보관 기간과 익명화 전략
- 유예기간(14일) 동안은 탈퇴 대기 상태로 유지
- 14일 이후에는 user row를 삭제하지 않고 개인정보 전체를 익명화
- 법적 필수 정보 보관 기간(5년) 동안 Soft Delete로 관리
이 결론에 도달한 이유는 명확했다.
왜 Hard Delete가 아니라 Soft Delete를 선택했는가
1. user를 삭제하면 연결된 데이터가 너무 많다
리뷰, QnA, 1:1 문의 등은 사용자의 PK를 FK로 참조하고 있다. Hard Delete를 하면 이 모든 관계를 어떻게 처리할지가 곧바로 복잡한 문제가 된다.
2. 삭제 이후 파생되는 설계 비용이 더 크다
user row를 없애 버리면 참조하던 다른 테이블이 작성자 정보를 별도로 들고 있어야 한다. 구조는 더 복잡해지고 정합성 관리 포인트도 늘어난다.
3. 보관 기간이 끝난 뒤 삭제하는 흐름이 더 자연스럽다
주문/결제 데이터는 어차피 5년 단위로 보관해야 한다. 회원 row만 먼저 없애기보다, 법적 보관 기간이 끝나는 시점에 관련 데이터를 함께 최종 삭제하는 편이 더 안정적이다.
그런데 여기서 또 하나의 의문이 생겼다
Soft Delete 방향으로 정리하고 나니 이번에는 또 다른 질문이 생겼다.
"운영 중인 user 테이블에 활성 회원과 탈퇴 회원이 같이 존재하는 게 맞는 걸까?"
처음에는 솔직히 좀 어색하게 느껴졌다. 탈퇴 회원이 같은 테이블에 있다는 게 왠지 덜 정돈된 구조처럼 보였기 때문이다. 하지만 이 의문도 곱씹어 보면 결국 물리적으로 분리해야만 안전하다는 전제를 깔고 있었다.
내린 결론: 같이 존재하는 것이 오히려 정상이다
결론부터 말하면, 같이 존재하는 것이 오히려 정상이라는 쪽으로 생각이 정리됐다. 중요한 건 다음 두 가지였다.
공존을 위한 필수 조건
- 상태값(
USER_STATUS)으로 논리적으로 완벽히 분리되어야 한다.- 탈퇴 회원의 개인정보는 완전히 파기(익명화)되어 있어야 한다.
즉, 같은 테이블에 있더라도 활동 회원과 탈퇴 회원은 전혀 다른 상태의 데이터가 되어야 한다. 이커머스 운영 관점에서는 오히려 이 구조가 더 표준적이고 현실적이다.
왜 한 테이블에 같이 두는 게 맞는가?
1. 거래 데이터가 USER_ID를 참조하기 때문
user row가 사라지거나 다른 테이블로 옮겨지면 조인이 복잡해지고, 운영·정산·분쟁 대응이 번거로워지며, FK 무결성 관리도 힘들어진다.
2. 활동 회원과 탈퇴 회원은 상태로 구분하면 된다
이미 상태값이 있다면 굳이 물리적으로 분리할 이유가 크지 않다.
ACTIVE / WITHDRAW_PENDING / WITHDRAWN이렇게만 해도 논리적으로 충분히 다른 데이터로 구분할 수 있다. 결국 중요한 건 어떻게 구분하고 어떻게 통제하느냐였다.
함께 두면 생기는 문제와 해결 방법
| 문제 | 해결 |
|---|---|
| 조회 시 데이터가 섞인다 | 모든 조회 로직에 USER_STATUS = ACTIVE 필터를 기본 조건으로 강제한다 |
| UNIQUE 제약 때문에 재가입이 막힌다 | 익명화 시점에 LoginId, Email, Phone 등을 파기하거나 치환해 재가입을 허용한다 |
| 개인정보가 남아 있을 위험 | 14일 후 파기 배치를 반드시 운영하고, 로그·백업 정책까지 함께 정비한다 |
그래서 정리한 최종 운영 정책
여기까지 고민한 끝에 정리한 최종 결과물이다.
회원 탈퇴 프로세스
- 탈퇴 요청 시
DELETED_AT기록 및 즉시 서비스 차단- 14일 유예기간 — 계정 복구 가능 기간 (고객센터 대응)
- 14일 이후 — 개인정보 최종 익명화 (LoginId, Password, Email 등 파기)
- 보관 — 법정 데이터(결제 등)는 5년 보관 후 자동 삭제
리뷰나 QnA 같은 데이터도 익명화한 뒤 유지하는 것을 기본으로 잡았다. 리뷰는 법적으로 반드시 파기해야 하는 데이터가 아니고, 운영 정책에 따라 탈퇴 회원으로 남겨 두는 편이 서비스 자산 관리에 더 유리하기 때문이다.
지금 시점에서의 결론
회원 탈퇴 기능을 고민하면서 가장 크게 느낀 건, 이게 단순히 user를 삭제할지 말지의 문제가 아니라는 점이었다.
진짜 중요한 질문들
- 어떤 데이터는 왜 남겨야 하는가?
- 개인정보는 어디까지 파기해야 하는가?
- 구조를 어떻게 유지해야 운영이 편한가?
처음에는 '14일 뒤 삭제'라는 단순한 생각으로 시작했지만, 결국 Soft Delete + 익명화 + 생명주기 관리라는 흐름이 가장 안정적이라는 결론을 얻었다.
정말 중요한 건 어떻게 지우느냐보다 무엇을 왜 남겨야 하는지 정확히 구분하는 것이었다.