본문으로 건너뛰기
개발 머꼬
개발 노트SQL
hohyeon.dev18

사용자를 지웠더니 게시글까지 사라진 이유 — ON DELETE 선택

  • #Engineering Note
  • #SQL

문제 발생

탈퇴 처리를 구현하고 테스트했더니 그 사용자가 쓴 게시글과 댓글이 전부 사라졌습니다. 커뮤니티에서 대화 맥락이 통째로 없어지는 결과였습니다.

반대로 다른 테이블에서는 사용자 삭제 자체가 실패했습니다.

ERROR 1451 (23000): Cannot delete or update a parent row:
a foreign key constraint fails

원인 분석

FK의 ON DELETE 동작을 정하지 않았거나, 기본값으로 정한 것입니다. MySQL 문서가 각각을 정의합니다.

  • CASCADE — 부모 행이 삭제되면 자식 테이블의 일치하는 행을 자동으로 삭제합니다.
  • SET NULL — 참조된 행이 삭제되면 자식 테이블의 외래 키 컬럼을 NULL로 설정합니다. 문서가 조건도 함께 적습니다 — 자식 테이블의 해당 컬럼을 NOT NULL로 선언하지 않았는지 확인해야 합니다.
  • RESTRICT — 자식 테이블에 일치하는 행이 있으면 부모의 삭제·수정을 거부합니다. 그리고 RESTRICT 지정은 ON DELETE 절을 아예 생략한 것과 같습니다.
  • NO ACTION — InnoDB에서는 RESTRICT와 동등합니다. **아무 절도 지정하지 않으면 기본 동작은 NO ACTION**입니다.
  • SET DEFAULT — 파서는 인식하지만 InnoDB와 NDB가 거부하므로 실제로는 쓸 수 없습니다.

즉 두 증상 모두 정상 동작이었습니다. 문제는 어떤 데이터가 어떤 운명을 따라야 하는지 정하지 않은 것이었습니다.

해결 방안

  1. 데이터 성격에 따라 나눕니다. 이 저장소의 실제 선택이 좋은 예입니다.
  • 공개 게시글·댓글의 author_idSET NULL (콘텐츠는 남고 화면에는 탈퇴한 사용자로 표시)
  • 좋아요·북마크·개인 대화 → CASCADE (그 사람의 개인 활동 데이터)
  • 회계·감사 기록 → RESTRICT (참조가 남아 있으면 삭제 자체를 막는다)
  1. SET NULL을 쓰려면 컬럼이 nullable이어야 합니다. 문서가 명시하는 조건이고, 이걸 어기면 제약 생성이나 삭제 시점에 실패합니다.

  2. 애플리케이션 로직과 이중으로 만들지 않습니다. DB에 CASCADE를 걸어두고 코드에서도 자식을 지우면, 나중에 한쪽만 바뀌었을 때 원인을 찾기 어렵습니다.

  3. CASCADE의 범위를 그려봅니다. 자식의 자식까지 연쇄될 수 있습니다 — 한 행을 지우는 트랜잭션이 예상보다 훨씬 커질 수 있고, 문서가 지적하듯 연쇄 동작은 트리거를 활성화하지 않습니다.

  4. 소프트 삭제와 조합을 정합니다. 콘텐츠를 deleted_at으로 보존하는 정책이라면, 계정 삭제는 hard delete여도 콘텐츠는 남아야 합니다 — 그 조합이 SET NULL입니다.

  5. 마이그레이션으로 남깁니다. 참조 동작 변경은 스키마 변경입니다. 운영 DB에서 직접 제약을 바꾸지 않습니다.

공식 문서

마지막 수정

좋아요북마크

댓글0

아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.