개발/Django, DRF

DRF-Spectacular을 활용한 Django Swagger 사용법과 예제 코드 가이드

심집사 2022. 11. 30. 23:05

더 정확한 문서화를 위해 DRF-spectacular관하여 찾아보고 쉽게 포스팅된 사용법이나 예시코드가 없어서

정리해서 올려본다.

 

api는 view_set을 기준으로 설명합니다

 

1. 먼저 drf-spectacular를 설치해준다

 

pip install drf-spectacular

2. settings.py 설정

settings.py

INSTALLED_APPS = [
	''''''~~~
    "drf_spectacular",
]


# docs
SPECTACULAR_SETTINGS = {
    # General schema metadata. Refer to spec for valid inputs
    # https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#openapi-object
    "TITLE": "drf-spectacular API Document",
    "DESCRIPTION": "drf-specatular 를 사용해서 만든 API 문서입니다.",
    # Optional: MAY contain "name", "url", "email"
    "CONTACT": {
        "name": "~~~~",
        "url": "https://.com",
        "email": "test@gmail.com",
    },
    # Swagger UI를 좀더 편리하게 사용하기위해 기본옵션들을 수정한 값들입니다.
    "SWAGGER_UI_SETTINGS": {
        # https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/  <- 여기 들어가면 어떤 옵션들이 더 있는지 알수있습니다.
        "dom_id": "#swagger-ui",  # required(default)
        "layout": "BaseLayout",  # required(default)
        "deepLinking": True,  # API를 클릭할때 마다 SwaggerUI의 url이 변경됩니다. (특정 API url 공유시 유용하기때문에 True설정을 사용합니다)
        "persistAuthorization": True,  # True 이면 SwaggerUI상 Authorize에 입력된 정보가 새로고침을 하더라도 초기화되지 않습니다.
        "displayOperationId": True,  # True이면 API의 urlId 값을 노출합니다. 대체로 DRF api name둘과 일치하기때문에 api를 찾을때 유용합니다.
        "filter": True,  # True 이면 Swagger UI에서 'Filter by Tag' 검색이 가능합니다
    },
    # Optional: MUST contain "name", MAY contain URL
    "VERSION": "1.0.0",
    "SERVE_INCLUDE_SCHEMA": False,  # OAS3 Meta정보 API를 비노출 처리합니다.
    # https://www.npmjs.com/package/swagger-ui-dist 해당 링크에서 최신버전을 확인후 취향에 따라 version을 수정해서 사용하세요.
    "SWAGGER_UI_DIST": "//unpkg.com/swagger-ui-dist@3.38.0",  # Swagger UI 버전을 조절할수 있습니다.
    "COMPONENT_SPLIT_REQUEST": True,
}

3. urls.py swagger url 추가 해주기

urls.py

urlpatterns += [
        # Open API 자체를 조회 : json, yaml,
        path("json/", SpectacularJSONAPIView.as_view(), name="schema-json"),
        path("yaml/", SpectacularYAMLAPIView.as_view(), name="swagger-yaml"),
        # Open API Document UI로 조회: Swagger, Redoc
        path(
            "swagger/",
            SpectacularSwaggerView.as_view(url_name="schema-json"),
            name="swagger-ui",
        ),
        path(
            "redoc/",
            SpectacularRedocView.as_view(url_name="schema-json"),
            name="redoc",
        ),
    ]

4. 간단한 게시판 CRUD를 이용한 예시코드

먼저 notice_board 앱을 생성해준다

model.py

class NoticeBoard(models.Model):
    title = models.CharField(max_length=100, null=True, blank=True)
    subheading = models.CharField(max_length=100, null=True, blank=True)
    content = models.TextField(null=True)
    created_date = models.DateTimeField(auto_now_add=True)
    updated_date = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = "notice_board"
        ordering = ["-id"]

urls.py

from django.urls import path
from .views import NoticeBoardViewSet

app_name = "notice_board"

urlpatterns = [
    path(
        "",
        NoticeBoardViewSet.as_view({"get": "list", "post": "create"}),
        name=app_name,
    ),
    path(
        "<int:id>",
        NoticeBoardViewSet.as_view({"get": "retrieve", "patch": "update", "delete": "destroy"}),
        name=app_name,
    ),
]

serializers.py

class NoticeBoardListSerializer(serializers.ModelSerializer):

    class Meta:
        model = NoticeBoard
        fields = ["id", "title", "subheading"]


class NoticeBoardCreateSerializer(serializers.ModelSerializer):

    class Meta:
        model = NoticeBoard
        fields = ["title", "subheading", "content"]


class NoticeBoardDetailSerializer(serializers.ModelSerializer):

    class Meta:
        model = NoticeBoard
        fields = ["id", "title", "subheading", "content"]

view.py

@extend_schema_view(
    list=extend_schema(summary="class레벨 데코레이터로 문서 커스터마이징.", tags=["notice_board"],
                       responses=NoticeBoardListSerializer),
    create=extend_schema(
        summary="@마찬가지로  데코레이터로 문서 커스터마이징 가능하다.",
        tags=["notice_board"],
        request=NoticeBoardCreateSerializer,
        responses={status.HTTP_200_OK: NoticeBoardCreateSerializer},
    ),
    retrieve=extend_schema(
            summary="@마찬가지로  데코레이터로 문서 커스터마이징 가능하다.",
            description="상세 조회 설명~~",
            tags=["notice_board"],
            responses={status.HTTP_200_OK: NoticeBoardDetailSerializer},
        ),
    update=extend_schema(
        summary="@마찬가지로  데코레이터로 문서 커스터마이징 가능하다.",
        description="update 설명~~",
        tags=["notice_board"],
        responses={status.HTTP_200_OK: NoticeBoardDetailSerializer},
    ),
    destroy=extend_schema(
            summary="@마찬가지로  데코레이터로 문서 커스터마이징 가능하다.",
            description="delete 설명~~",
            tags=["notice_board"],
            responses={status.HTTP_200_OK: NoticeBoardDetailSerializer},
        ),
)
class NoticeBoardViewSet(viewsets.ModelViewSet):
    permission_classes = [AllowAny]
    queryset = NoticeBoard.objects.all()
    serializer_class = NoticeBoardListSerializer
    lookup_field = "id"

    @transaction.atomic()
    def create(self, request, *args, **kwargs):
        """게시판 생성"""
        self.serializer_class = NoticeBoardCreateSerializer
        return super().create(request, *args, **kwargs)

    def retrieve(self, request, *args, **kwargs):
        """게시판 상세 조회"""
        self.serializer_class = NoticeBoardDetailSerializer
        return super().retrieve(request, *args, **kwargs)

    @transaction.atomic()
    def update(self, request, *args, **kwargs):
        """게시판 매거진 수정"""
        self.serializer_class = NoticeBoardUpdateSerializer
        return super().update(request, *args, **kwargs)

보통 이런식으로 많은 블로그에 포스팅이 되어있는데 이경우 view.py에서 문서화를 위한 코드라인이 너무 길어지게 되어서 가독성이 매우 떨어진다

view.py 코드 라인수를 줄일 방법을 찾아보다가 찾게된 방법을 사용해보자

 


5. schemas.py 생성하기

6. schemas.py에서 문서화 관련된 코드 작업

schemas.py

from drf_spectacular.utils import (
    OpenApiParameter,
    extend_schema,
    extend_schema_view,
    OpenApiExample,
)

from notice_board.serializers import NoticeBoardCreateSerializer, NoticeBoardDetailSerializer

TAG = "notice_board"


class NoticeBoardSchema:
    notice_board_list_schema = extend_schema(
        tags=[TAG],
        summary="notice_board 목록 조회 API_UPDATE : 2022-11-22",
        parameters=[
            OpenApiParameter(
                name="page_size",
                description=f"page_size 기본값 10.",
                required=False,
                type=int,
            ),
            OpenApiParameter(
                name="param_2",
                description="파라미터 설명 블라블라~",
                required=False,
                type=int,
                style="form",
            ),
        ],
    )

    notice_board_create_schema = extend_schema(
        tags=[TAG],
        summary="notice_board 생성 API_UPDATE : 2022-11-22",
        description="설명 블라블라~",
        parameters=[
            OpenApiParameter(
                name="page_size",
                description=f"page_size 기본값 10.",
                required=False,
                type=int,
                style="form",
            ),
        ],
        request=NoticeBoardCreateSerializer,
        responses=NoticeBoardCreateSerializer,
    )

    notice_board_detail_schema = extend_schema(
        tags=[TAG],
        summary="notice_board 상세 조회 API_UPDATE : 2022-11-22",
        responses=NoticeBoardDetailSerializer,
    )

    notice_board_update_schema = extend_schema(
        tags=[TAG],
        summary="notice_board 수정 API_UPDATE : 2022-11-22",
        description="설명 블라블라~~~",
        request=NoticeBoardCreateSerializer,
    )

    notice_board_delete_schema = extend_schema(
        tags=[TAG],
        summary="notice_board 삭제 API_UPDATE : 2022-11-22",
    )

    notice_board_schema_view = extend_schema_view(
        list=notice_board_list_schema,
        create=notice_board_create_schema,
        retrieve=notice_board_detail_schema,
        update=notice_board_update_schema,
        destroy=notice_board_delete_schema
    )

 

7. 완성된 코드

view.py

데코레이터 한줄로 코드 가독성이 매우 좋아졌다

@NoticeBoardSchema.notice_board_schema_view
class NoticeBoardViewSet(viewsets.ModelViewSet):
    permission_classes = [AllowAny]
    queryset = NoticeBoard.objects.all()
    serializer_class = NoticeBoardListSerializer
    lookup_field = "id"

    @transaction.atomic()
    def create(self, request, *args, **kwargs):
        """게시판 생성"""
        self.serializer_class = NoticeBoardCreateSerializer
        return super().create(request, *args, **kwargs)

    def retrieve(self, request, *args, **kwargs):
        """게시판 상세 조회"""
        self.serializer_class = NoticeBoardDetailSerializer
        return super().retrieve(request, *args, **kwargs)

    @transaction.atomic()
    def update(self, request, *args, **kwargs):
        """게시판 매거진 수정"""
        self.serializer_class = NoticeBoardUpdateSerializer
        return super().update(request, *args, **kwargs)

완성된 swagger-UI

schemas.py에서 작성한 파라미터 설명이나 respones도 잘나온다

 

잘 작성해두면 포스트맨 필요없이 api테스트를 할 수 있고 문서화 시간을 매우 절약 가능하며 협업하기도 훨씬 편해진다