본문 바로가기
개발/개발

[스크랩 기능] 스크랩 삭제 API 설계 – scrapId만으로 충분할까?

by 글쓰는 개발자 2026. 2. 26.

배경

스크랩한 프로필을 삭제하는 기능을 구현하다가 이런 고민이 생겼다.

삭제할 때 scrapId(PK)만 넘기면 될까?
아니면 folderId와 scrapId를 둘 다 넘겨야 할까?

 

결론부터 말하면:

삭제 자체는 scrapId(PK) 하나로 충분하다.
하지만 “보안”과 “검증” 관점에서는 추가 고려가 필요하다.

 

삭제는 PK 하나로 충분하다

스크랩 테이블이 아래처럼 되어 있다고 가정해보자.

class Scrap(models.Model):
    folder = models.ForeignKey(ScrapFolder, on_delete=models.CASCADE)
    profile_num = models.BigIntegerField()
    profile_owner_user_id = models.CharField(max_length=100)
    created_at = models.DateTimeField(auto_now_add=True)

 

scrap.id는 PK이기 때문에 유일한 식별자다.

따라서 삭제 자체는 다음처럼 가능하다.

Scrap.objects.filter(id=scrap_id).delete()

 

그런데 왜 folderId를 같이 고민하게 될까?

보통 이런 구조로 요청을 보낸다.

$.ajax({
    url: `/scrap/${folderId}/remove/`,
    method: 'POST',
    data: {
        scrapId: scrapId
    }
});

 

이때 생기는 질문:

  • folderId는 정말 필요한가?
  • scrapId만으로 충분하지 않은가? scrapId가 PK니까!

핵심은 “삭제 가능 여부”가 아니라 “검증”이다.

folderId를 같이 받으면 서버에서 이런 검증이 가능하다.

이렇게 하면

  • 잘못된 폴더에서 삭제 요청하는 실수 방지
  • 프론트 버그 방어
  • API 의미 명확화

같은 장점이 있다.

하지만 이것보다 더 중요한 검증이 있다.

 


가장 중요한 것: 소유권 검증

삭제 API에서 진짜 핵심은 이것이다.

이 scrap이 “현재 로그인한 사용자”의 것이 맞는가?

예시:

Scrap.objects.filter(
    id=scrap_id,
    scrap_owner_user_id=request.user.username
).delete()

 

이 검증이 빠지면 심각한 보안 문제(예를 들어 IDOR 같은..) 가 발생한다.

IDOR 취약점이란?

IDOR는 다음의 약자다.

Insecure Direct Object Reference

직역하면
“안전하지 않은 직접 객체 참조”

쉽게 말하면

ID만 바꿔서 남의 데이터를 조작할 수 있는 취약점

예를 들어 이런 API가 있다고 가정해보자.

Scrap.objects.filter(id=scrap_id).delete()

 

만약 사용자가 브라우저 개발자도구에서

scrapId=15 → scrapId=16

 

으로 바꿔버리면?

→ 다른 사람의 스크랩이 삭제될 수도 있다.

이게 바로 IDOR다.

IDOR 발생 조건

  1. 클라이언트가 ID를 직접 전달
  2. 서버가 ID 기반 조회
  3. 소유자 검증을 하지 않음

이 세 가지가 모이면 100% 취약해진다.


그렇다면 어떻게 설계하는 것이 좋은가?

권장 설계

방법 1 — 가장 단순한 방식

DELETE /scraps/{scrapId}

 

서버 코드:

Scrap.objects.filter(
    id=scrap_id,
    profile_owner_user_id=request.user.username
).delete()

→ scrapId만 사용
→ 반드시 사용자 검증 포함


방법 2 — 폴더 스코프까지 포함

DELETE /folders/{folderId}/scraps/{scrapId}

 

서버 코드:

Scrap.objects.filter(
    id=scrap_id,
    folder_id=folder_id,
    scrap_owner_user_id=request.user.username
).delete()

 

→ 폴더까지 검증
→ API 의미 명확
→ 프론트 오류 방어 가능

 

결론

삭제 API의 본질은 “ID가 무엇인가?”가 아니라

이 리소스를 삭제할 권한이 있는가?

 

이다.

scrapId만으로 충분하다.
하지만 검증 없이 scrapId만 쓰는 건 매우 위험하다.

반응형