1️⃣ 문제 상황
본인 게시글을 삭제하려고 했는데 다음과 같은 오류가 발생했다.
delete from article where id=?
→ FK constraint violation
에러의 핵심은 이것이다.
article_like 테이블이 article을 FK로 참조 중인데
article을 먼저 삭제하려 해서 FK 위반이 발생했다.
즉,
- article_like.article_id → article.id
- 좋아요가 남아 있는 상태에서
- 게시글을 먼저 delete
→ DB가 무결성 위반으로 막음
좋아요 기능 구조 복습
좋아요는 이렇게 설계되어 있다.
- article_like (user_id, article_id)
- 유니크 제약으로 중복 좋아요 방지
- Article.likeCount는 캐싱 컬럼
문제는:
자식 테이블(article_like)을 정리하지 않고
부모 테이블(article)을 삭제하려 했기 때문
2️⃣ 해결 방법 1 (코드 최소 수정)
👉 삭제 시 “좋아요 먼저 삭제 → 게시글 삭제”
DB 스키마는 그대로 두고,
서비스 로직에서 삭제 순서만 바꾼다.
1) ArticleLikeRepository에 게시글 기준 삭제 추가
@Modifying
@Query("""
delete from ArticleLike al
where al.article.id = :articleId
""")
int deleteAllByArticleId(Long articleId);
2) BlogService.delete() 수정
@Transactional
public void delete(Long id) {
Article article = blogRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("not found : " + id));
authorizeArticleAuthor(article);
// 1️⃣ 좋아요 먼저 삭제 (FK 해제)
articleLikeRepository.deleteAllByArticleId(id);
// 2️⃣ 게시글 삭제
blogRepository.delete(article);
}
✅ 장점
- DB 스키마 수정 없음
- 수정 파일 2개 (Repository + Service)
- 빠르게 해결 가능
❗ 단점
- 삭제 쿼리 2번 실행
- 개발자가 자식 삭제를 항상 신경 써야 함
- 실수 가능성 존재
3️⃣ 해결 방법 2(실무적으로 더 권장) – DB CASCADE
👉 FK에 ON DELETE CASCADE 추가
이 방법은 DB가 자동으로 자식 데이터를 삭제하도록 만드는 방식이다.
1) 현재 FK 이름 확인
SELECT CONSTRAINT_NAME
FROM INFORMATION_SCHEMA.CONSTRAINTS
WHERE TABLE_NAME = 'ARTICLE_LIKE'
AND CONSTRAINT_TYPE = 'FOREIGN KEY';
2) 기존 FK 삭제
ALTER TABLE ARTICLE_LIKE
DROP CONSTRAINT FK_기존이름;
(데이터는 삭제되지 않음. FK 규칙만 제거)
3) CASCADE 옵션으로 FK 재생성
ALTER TABLE ARTICLE_LIKE
ADD CONSTRAINT FK_ARTICLE_LIKE_ARTICLE
FOREIGN KEY (ARTICLE_ID)
REFERENCES ARTICLE(ID)
ON DELETE CASCADE;
💡 의미
ARTICLE 삭제
→ DB가 자동으로 ARTICLE_LIKE 삭제
애플리케이션 코드에서 자식 삭제를 신경 쓸 필요가 없다.
4️⃣ 왜 실무에서는 CASCADE를 많이 쓸까?
1. 트랜잭션 로직 단순화
blogRepository.delete(article);
끝.
2. 실수 방지
개발자가 자식 삭제를 빼먹어도 DB가 무결성을 보장한다.
3. 동시성 상황에서도 안전
- 자식 row 정리는 DB가 직접 수행
- 애플리케이션 레벨 실수 없음
5️⃣ JPA Cascade vs DB Cascade 차이
| 구분 | JPA Cascade | DB Cascade |
| 동작 위치 | 애플리케이션 | 데이터베이스 |
| 트랜잭션 안전성 | 코드에 의존 | DB가 보장 |
| 실수 가능성 | 있음 | 거의 없음 |
| 실무 선호도 | 보조적 | 더 선호 |
실무에서는 보통:
DB CASCADE + 서비스 로직 단순화
이 조합을 가장 많이 사용한다.
개발 단계에서는?
- 로컬/테스트: ddl-auto=create-drop
- 스키마 변경 자유롭게 가능
하지만 운영 환경이라면:
- Flyway/Liquibase로 마이그레이션 관리
- FK 변경은 반드시 스크립트로 관리
6️⃣ 최종 정리
문제 원인
- article_like가 article을 참조 중
- 부모를 먼저 삭제해서 FK 위반
해결 방법
방법 A (빠른 해결)
- 서비스에서 좋아요 먼저 삭제
- 게시글 삭제
방법 B (실무 권장)
- FK에 ON DELETE CASCADE 적용
- DB가 자동 정리
결론
연관 엔티티 정리는
애플리케이션이 아니라
데이터베이스가 책임지는 것이 더 안전하다.
좋아요 + 게시글 삭제 문제를 통해
FK 제약과 CASCADE 전략의 중요성을 체감했다.
'프로젝트 > 스프링 부트 3 백엔드 개발자 되기 Blog + 선착순 강의 프로젝트' 카테고리의 다른 글
| ⏰🚨 신청 버튼을 누르지도 않았는데 자동으로 신청되는 문제 (Redis 대기열 + localStorage 이슈) (0) | 2026.02.14 |
|---|---|
| ⏰ 선착순 강의 대규모 트래픽 처리 – Redis 1차 설계 (0) | 2026.02.14 |
| 👥💡 좋아요(Like) 기능 설계: 상태, 집계, 동시성까지 고려한 구현 정리 (0) | 2026.02.14 |
| 👥🚨 같은 유저가 좋아요를 동시에 연타할 때 동시성 테스트가 깨지는 이유 (0) | 2026.02.14 |
| 👥🚨 좋아요 동시성 테스트에서 교착상태가 발생한 이유와 해결 방법 (0) | 2026.02.14 |