Skip to content

Commit 280866b

Browse files
committed
Docs: swagger 모듈 분리
1 parent ec12766 commit 280866b

File tree

4 files changed

+116
-77
lines changed

4 files changed

+116
-77
lines changed

src/config/settings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555

5656
# rest_framework settings
5757
REST_FRAMEWORK = {
58-
"DEFAULT_SCHEMA_CLASS": "core.swagger.CustomAutoSchema",
58+
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
5959
"DEFAULT_PAGINATION_CLASS": "core.paginations.PageNumberPagination",
6060
"EXCEPTION_HANDLER": "rest_framework.views.exception_handler",
6161
"PAGE_SIZE_QUERY_PARAM": "page_size",

src/core/swagger.py

Lines changed: 6 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,9 @@
1-
import uritemplate
2-
from drf_spectacular.openapi import AutoSchema
3-
from drf_spectacular.plumbing import is_basic_type, is_list_serializer
4-
from drf_spectacular.utils import _SerializerType
5-
from rest_framework.generics import GenericAPIView
6-
from rest_framework.mixins import ListModelMixin
1+
from typing import Callable
72

3+
from drf_spectacular.utils import extend_schema
84

9-
class CustomAutoSchema(AutoSchema):
10-
def _is_list_view(self, serializer: _SerializerType | None = None) -> bool:
11-
"""
12-
partially heuristic approach to determine if a view yields an object or a
13-
list of objects. used for operationId naming, array building and pagination.
14-
defaults to False if all introspection fail.
15-
"""
16-
if serializer is None:
17-
serializer = self.get_response_serializers()
185

19-
if isinstance(serializer, dict) and serializer:
20-
# extract likely main serializer from @extend_schema override
21-
serializer = {str(code): s for code, s in serializer.items()}
22-
serializer = serializer[min(serializer)]
23-
24-
if is_list_serializer(serializer):
25-
return True
26-
if is_basic_type(serializer):
27-
return False
28-
# if hasattr(self.view, 'action'):
29-
# return self.view.action == 'list'
30-
# list responses are "usually" only returned by GET
31-
if self.method != "GET":
32-
return False
33-
if isinstance(self.view, ListModelMixin):
34-
return True
35-
# primary key/lookup variable in path is a strong indicator for retrieve
36-
if isinstance(self.view, GenericAPIView):
37-
lookup_url_kwarg = self.view.lookup_url_kwarg or self.view.lookup_field
38-
if lookup_url_kwarg in uritemplate.variables(self.path):
39-
return False
40-
41-
return False
6+
class SwaggerSchema:
7+
@classmethod
8+
def generate_schema(cls, *args, **kwargs) -> Callable:
9+
return extend_schema(*args, **kwargs)

src/faq/swagger.py

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
from drf_spectacular.utils import OpenApiExample, OpenApiResponse
2+
3+
from core.responses.serializer import (
4+
ErrorResponseSerializer,
5+
ListSuccessResponseSerializer,
6+
SuccessResponseSerializer,
7+
)
8+
from core.swagger import SwaggerSchema
9+
10+
11+
class FAQAPIDocs(SwaggerSchema):
12+
@classmethod
13+
def retrieve(cls):
14+
responses = {
15+
"성공": OpenApiResponse(
16+
response=SuccessResponseSerializer,
17+
description="단일 응답 성공",
18+
examples=[
19+
OpenApiExample(
20+
name="FAQ 조회",
21+
value={
22+
"status": "SUCCESS",
23+
"data": {
24+
"id": 1,
25+
"question": "질문",
26+
"answer": "답변",
27+
"created_at": "2021-01-01",
28+
},
29+
},
30+
),
31+
],
32+
),
33+
"에러": OpenApiResponse(
34+
response=ErrorResponseSerializer,
35+
description="응답 에러",
36+
examples=[
37+
OpenApiExample(
38+
name="데이터 없음",
39+
value={
40+
"status": "ERROR",
41+
"error": {"code": "NOT_EXIST", "message": "데이터가 없습니다."},
42+
},
43+
),
44+
],
45+
),
46+
}
47+
return cls.generate_schema(
48+
operation_id="faq_retrieve", description="FAQ 조회", responses=responses
49+
)
50+
51+
@classmethod
52+
def list(cls):
53+
responses = {
54+
"성공": OpenApiResponse(
55+
response=ListSuccessResponseSerializer,
56+
description="다중 응답 성공",
57+
examples=[
58+
OpenApiExample(
59+
name="FAQ 목록 조회 (페이지네이션 있음)",
60+
value={
61+
"status": "SUCCESS",
62+
"data": [
63+
{
64+
"id": 1,
65+
"question": "질문1",
66+
"answer": "답변1",
67+
},
68+
],
69+
"pagination": {
70+
"count": 20,
71+
"next": "http://localhost:8000/api/v1/faqs/?page=2",
72+
"previous": "http://localhost:8000/api/v1/faqs/?page=1",
73+
},
74+
},
75+
),
76+
OpenApiExample(
77+
name="FAQ 목록 조회 (데이터 있음)",
78+
value={
79+
"status": "SUCCESS",
80+
"data": [
81+
{
82+
"id": 1,
83+
"question": "질문1",
84+
"answer": "답변1",
85+
},
86+
],
87+
"pagination": {"count": 1, "next": None, "previous": None},
88+
},
89+
),
90+
OpenApiExample(
91+
name="FAQ 목록 조회 (데이터 없음)",
92+
value={
93+
"status": "SUCCESS",
94+
"data": [],
95+
"pagination": {"count": 0, "next": None, "previous": None},
96+
},
97+
),
98+
],
99+
),
100+
"에러": OpenApiResponse(response=ErrorResponseSerializer, description="응답 에러"),
101+
}
102+
return cls.generate_schema(
103+
operation_id="faq_list", description="모든 FAQ 목록 조회", responses=responses
104+
)

src/faq/views.py

Lines changed: 5 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,30 @@
1-
from drf_spectacular.utils import OpenApiResponse, extend_schema
1+
from drf_spectacular.utils import extend_schema_view
22
from rest_framework.viewsets import GenericViewSet
33

44
from core.errors import NotExistException
55
from core.responses.base import BaseResponse
6-
from core.responses.serializer import (
7-
ErrorResponseSerializer,
8-
ListSuccessResponseSerializer,
9-
SuccessResponseSerializer,
10-
)
116

127
from .models import FAQ
138
from .serializers import FAQSerializer
9+
from .swagger import FAQAPIDocs
1410

1511

12+
@extend_schema_view(list=FAQAPIDocs.list(), retrieve=FAQAPIDocs.retrieve())
1613
class FAQViewSet(GenericViewSet):
1714
serializer_class = FAQSerializer
1815

1916
def get_queryset(self):
20-
"""
21-
## queryset에서 is_deleted는 제외시켜주는 역할
22-
"""
2317
return FAQ.objects.filter(is_deleted=False)
2418

25-
@extend_schema(
26-
description="모든 FAQ 목록 조회",
27-
responses={
28-
"200/성공": ListSuccessResponseSerializer,
29-
"200/에러": ErrorResponseSerializer,
30-
},
31-
)
32-
def list(self, request, *args, **kwargs):
33-
"""
34-
## 모든 FAQ 목록 조회
35-
### 특징 : is_deleted는 제외
36-
"""
19+
def list(self, request, *args, **kwargs) -> BaseResponse:
3720
queryset = self.get_queryset()
3821
if not queryset.exists():
3922
raise NotExistException()
4023
page = self.paginate_queryset(queryset) # ✅ 페이지네이션 적용
4124
serializer = self.get_serializer(page or queryset, many=True)
4225
return self.get_paginated_response(serializer.data)
4326

44-
@extend_schema(
45-
description="모든 FAQ 목록 조회",
46-
responses={
47-
"200/성공": OpenApiResponse(
48-
response=SuccessResponseSerializer,
49-
description="응답 성공",
50-
),
51-
"200/에러": OpenApiResponse(
52-
response=ErrorResponseSerializer,
53-
description="응답 에러",
54-
),
55-
},
56-
)
57-
def retrieve(self, request, *args, **kwargs):
58-
"""
59-
## 특정 FAQ 조회
60-
"""
27+
def retrieve(self, request, *args, **kwargs) -> BaseResponse:
6128
instance = self.get_object()
6229
serializer = self.get_serializer(instance)
6330
return BaseResponse(serializer.data)

0 commit comments

Comments
 (0)