👥🚨 좋아요 상태 변경 후 본인 게시글 삭제 오류 해결 (FK 제약 & DB CASCADE)

2026. 2. 14. 20:15·프로젝트/스프링 부트 3 백엔드 개발자 되기 Blog + 선착순 강의 프로젝트

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
'프로젝트/스프링 부트 3 백엔드 개발자 되기 Blog + 선착순 강의 프로젝트' 카테고리의 다른 글
  • ⏰🚨 신청 버튼을 누르지도 않았는데 자동으로 신청되는 문제 (Redis 대기열 + localStorage 이슈)
  • ⏰ 선착순 강의 대규모 트래픽 처리 – Redis 1차 설계
  • 👥💡 좋아요(Like) 기능 설계: 상태, 집계, 동시성까지 고려한 구현 정리
  • 👥🚨 같은 유저가 좋아요를 동시에 연타할 때 동시성 테스트가 깨지는 이유
hak0622
hak0622
개발하면서 “이게 뭐지?”라는 순간마다 궁금한 점을 바탕으로 정리한 개발 블로그입니다.
  • hak0622
    궁금한 개발 이야기 Why?
    hak0622
  • 전체
    오늘
    어제
    • 분류 전체보기 (68)
      • 공부 (36)
        • 1. 자바 ORM 표준 JPA 프로그래밍 - 기본.. (35)
        • 시험 (1)
      • 프로젝트 (32)
        • 스프링 부트 3 백엔드 개발자 되기 Blog + .. (32)
  • 인기 글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
hak0622
👥🚨 좋아요 상태 변경 후 본인 게시글 삭제 오류 해결 (FK 제약 & DB CASCADE)
상단으로

티스토리툴바