@action 데코레이터로 특정 로직을 위한 깔끔한 엔드포인트 추가하기

ViewSet에서 기본 CRUD 외의 추가 기능을 구현할 때 @action 데코레이터를 사용하여 관련 로직을 깔끔하게 추가하고 URL을 자동 생성

@action이 필요한 이유

기본 CRUD 외에 필요한 추가 기능들:

  • 최신 글 목록만 조회

  • 게시글에 '좋아요', '신고하기' 기능

  • 공유하기 등

기존 해결 방법의 문제점:

  • 별도 APIView 생성: 파일과 클래스 증가, URL 설정 복잡화

  • update 로직에 억지로 추가: 메서드 비대화, API 직관성 저하

@action 기본 사용법

핵심 인자

  • detail=True: 특정 객체에 대한 액션 (/posts/{pk}/publish/)

  • detail=False: 목록 전체에 대한 액션 (/posts/recent/)

  • methods: 허용할 HTTP 메서드 지정

  • serializer_class: 사용할 시리얼라이저 지정

  • permission_classes: 사용할 권한 클래스 지정

특정 게시글 공개 (detail=True)

@action(detail=True, methods=['post'])
def publish(self, request, pk=None):
    post = self.get_object()
    if post.is_published:
        return Response({'detail': '이미 공개된 게시글입니다.'})
    
    post.is_published = True
    post.save(update_fields=['is_published'])
    
    serializer = self.get_serializer(post)
    return Response(serializer.data)

최신 글 목록 조회 (detail=False)

@action(detail=False, methods=['get'])
def recent(self, request):
    recent_posts = self.get_queryset().order_by('-created_at')[:5]
    serializer = self.get_serializer(recent_posts, many=True)
    return Response(serializer.data)

실무 활용 팁

URL 경로 커스텀

@action(detail=True, methods=['post'], url_path='change-password')
def set_password(self, request, pk=None):
    # URL: /users/{pk}/change-password/

액션별 권한과 시리얼라이저 적용

@action(detail=True, methods=['post'], 
        permission_classes=[IsAdminUser],
        serializer_class=PasswordSerializer)
def set_password(self, request, pk=None):
    # 해당 액션에만 특별한 권한과 시리얼라이저 적용

주의사항

@action 사용 vs 별도 APIView

  • @action 적합: 리소스와 강하게 결합된 행위, 단순한 로직

  • 별도 APIView 적합: 독립적인 로직, 복잡한 다중 모델 처리

Last updated