모든 글

주문 스냅샷을 지키는 소프트 삭제와 도메인 경계

현재 상품과 주문 스냅샷의 경계를 나누고, 환불 이력을 보존하며 파괴적 삭제 전에 상태 전환과 보관을 검토합니다.

출처 및 AI 안내: 이 글은 제미니의 개발실무 유튜브를 기반으로 작성되었습니다. gpt-5.6-sol 모델을 사용해 생성·편집했습니다.

문제가 있는 상품을 내리고 이미 구매한 주문은 환불해야 한다. 직접 외래키 때문에 상품 행을 지울 수 없자 주문을 환불하고 주문 상품을 지운 뒤 마지막에 상품을 삭제하는 순서를 생각했다. 이 순서는 제약을 만족하려고 잘못된 개념을 지운다.

삭제 순서에서 무너진 경계가 드러난다

상품은 구매의 재료다. 주문이 성립한 뒤 주문 상품은 당시 상품명과 가격 같은 사실을 담은 스냅샷이다. 오늘 가격이 바뀌어도 어제 주문의 가격은 바뀌지 않는다. 현재 카탈로그 상품과 구매된 상품은 이미 서로 다른 생명주기로 들어갔다.

따라서 주문 상품은 현재 상품이 아니라 주문 경계 아래에 속한다. 카탈로그 행을 없애려고 주문 상품까지 지우면 그 경계를 무너뜨린다. 어느 주문에 그 상품이 있었고 왜 환불했는지도 사라진다. 데이터베이스 관계 때문에 구매 이후 이 데이터를 어느 개념이 소유하는지라는 더 중요한 질문을 놓친 셈이다.

이 예제에서는 스냅샷이 만들어진 뒤에도 실시간 관계가 있는 것처럼 보이게 하는 상품-주문 상품 외래키의 이점이 크지 않다. 다른 곳에서는 외래키가 필요한 무결성을 지킬 수 있지만, 생명주기를 만들어 내는 게 아니라 이미 정한 주기를 따라야 한다.

사건을 지우지 말고 상태 변화를 남긴다

환불은 주문이나 결제 상태를 바꾸지 구매 사실을 없던 일로 만들지 않는다. 현재 상품은 차단 또는 삭제 상태로 바꾸고, 영향을 받은 주문이나 주문 상품은 환불 상태로 바꾼다. 상태 이름은 서비스마다 다를 수 있지만 행을 없애는 대신 실제 전환을 보여 줘야 한다.

삭제된 상품도 무엇이었는지, 누가 등록했는지, 어느 구매에 포함됐는지, 어떤 환불로 이어졌는지를 알려 준다. 하드 삭제는 나중에 조사하고 분석할 데이터에 빈칸을 남긴다. 참조 행까지 없어지면 환불 테이블만으로 상품과 주문 맥락을 되살릴 수 없다.

이 상황에서는 소프트 삭제를 선호하지만 모든 데이터를 그렇게 다루라는 명령은 아니다. 실제 삭제가 필요하다면 필요한 이력이 사라지지 않도록 먼저 다른 테이블에 정보를 옮기고, 필요하다면 재처리할 수 있는 데이터까지 남긴다. 상품, 주문, 주문 상품, 결제, 환불의 경계부터 세운 뒤 각 경계 안에서 일어난 일을 보존하는 상태와 제약을 고른다.