django-rest-fremework으로 정의한 API를 자동으로 문화하 할 수 있는 패키지입니다. API 호출을 위한 메서드를 명시를 해줄 수도 있고 프로젝트 내 앱 별로 메서드가 정리가 되어 나타납니다. 직접 사용했을 때의 느낌은 postman보다 간결한 부분도 있었지만 postman에서는 없었던 문서화를 위한 설정도 존재하는 것이 사실입니다. 그럼에도 불구하고 요즘 많이 쓰이기도 하고 문서화 작업이 편한 것은 사실이기에, 공부를 해보았습니다.
Swagger는 어떤 것이고, drf-yasg는 뭐지? 하는 의문을 가지고 이리저리 검색을 하다 이 둘의 차이점에 대해 설명한 블로그를 보았습니다. 타운컴퍼니 기술 블로그인데요, 여기서 근무하시고 계신 직원분께서 문서화의 중요성에 대해 설명해 주셔서 인용을 해보겠습니다.
소프트웨어 프로젝트를 진행하면서 요구사항 명세서, 스토리 보드, 설계 문서, 일정관리 계획서, API 문서 등 프로젝트를 수행하는 규모와 방식에 따라 각기 다른 목적으로 다양한 문서를 작성하게 됩니다. 수 많은 프로젝트에서 그렇듯 이런 문서는 조금만 방심하면 유지보수 되지 않는 문서가 되곤 합니다.
프로젝트 일정이 급해서 / 문서의 중요도가 낮아져서 / 구두로 공유가 된 내용이라서 / 깜빡하고 문서 업데이트를 잊어버려서 / (귀찮아서)
여러가지 이유로 유지보수 되지 않는 문서로 인해 발생하는 유산(legacy)은 가끔(거의 항상) 프로젝트에 악영향을 주곤 합니다. 그 중에 API 문서에 대해 공유해보려 합니다.
출처 : 타운 컴퍼니 기술 블로그
아직 실무 경험이 없는 저에겐 먼 이야기지만 충분히 이해가 됐습니다. 위 블로그를 살펴보시면 이 둘의 차이점도 잘 설명을 해주셨습니다.
요약을 해보면 swagger는 자동으로 API 목록과 각 API의 파라미터, 헤더 등을 함께 문서화해주는 도구인데요, drf-yasg는 swagger의 부족한 부분을 채워줄 수 있는 django에서 제공하는 라이브러리라고 할 수 있을 거 같습니다.
먼저 swagger + drf-yasg를 사용하기 위한 셋팅 과정입니다. 기존 진행했었던 프로젝트를 기반으로 작성하였습니다.
pip install drf-yasg
pip install djangorestframework
만들어 놓은 Django 프로젝트의 settings.py의 INSTALLED_APPS에 설치한 라이브러리 앱을 추가합니다.
# settings.py
INSTALLED_APPS = [
....
"drf_yasg",
"rest_framework",
swagger API 문서 설정이 필요합니다. 이 과정을 통해 swagger 문서를 UI로 볼 수 있습니다. 루트 디렉토리, 최상단 urls.py에 작성합니다.
from django.urls import path, include
from django.conf.urls import url
from drf_yasg.views import get_schema_view
from drf_yasg import openapi
from rest_framework.permissions import AllowAny
schema_url_patterns = [
path("users", include("users.urls")),
path("postings", include("postings.urls")),
]
schema_view_v1 = get_schema_view(
openapi.Info(
title="AIMMO Assignment API",
default_version="v1",
description="에이모 기업과제 API Document",
terms_of_service="https://policies.google.com/terms",
),
validators=["flex"],
public=True,
permission_classes=(AllowAny,),
patterns=schema_url_patterns,
)
urlpatterns = [
path("users", include("users.urls")),
path("postings", include("postings.urls")),
url(r'^swagger(?P<format>\.json|\.yaml)$', schema_view_v1.without_ui(cache_timeout=0), name='schema-json'),
url(r'^swagger/$', schema_view_v1.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
url(r'^redoc/$', schema_view_v1.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
]
swagger 문서 자동화를 구현한 예제들을 보면 클래스형 View로 구현한다고 합니다. 아마 클래스 변수로서 serializer_class를 선언하여 시리얼라이저를 받아줘야 하기 때문인 거 같다고 합니다.
# 루트 디렉토리/urls.py
urlpatterns = [
path("users", include("users.urls")),
path("postings", include("postings.urls")),
url(r'^swagger(?P<format>\.json|\.yaml)$', schema_view_v1.without_ui(cache_timeout=0), name='schema-json'),
url(r'^swagger/$', schema_view_v1.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
url(r'^redoc/$', schema_view_v1.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
]
urlpatterns을 작성하는 부분은 크게 달라지지 않습니다. 앱과 연결되는 url를 작성합니다. 그리고 swagger 연결을 위한 url을 작성해줍니다.
http://127.0.0.1:8000/swagger/ 주소로 swagger UI에 접속할 수 있습니다.
http://127.0.0.1:8000/redoc/ 주소로 API 문서에 접속할 수 있습니다.
# urls.py
from postings.serializer import SearchSerializer
from django.urls import path
from . import views
urlpatterns = [
path("", views.PostingCreateView.as_view()),
path("/<int:posting_id>", views.PostingView.as_view()),
path("/list", views.PostingListView.as_view()),
path("/<int:posting_id>/comment", views.CommentView.as_view()),
path("/<int:posting_id>/commentlist", views.CommentListView.as_view()),
path("/search", views.SearchView.as_view())
]
ViewSet이나 다른 기능을 사용하지 않았기 때문에 특별한 부분은 없습니다.
# app/serializer.py
from .models import Comment, Posting
from rest_framework import serializers
class PostingSerializer(serializers.ModelSerializer):
class Meta:
model = Posting
fields = ["title", "text", "category"]
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = ["content"]
class SearchSerializer(serializers.ModelSerializer):
class Meta:
model = Posting
fields = ["author"]
시리얼라이저 파일을 작성합니다. 위에서 보이는 것처럼 class PostingSerializer 부분의 field에 title, text, category 등을 작성하였습니다. swagger 문서를 켜보면 다음과 같이 입력이 되는 걸 볼 수 있습니다.
대부분의 API는 request를 받을 때 데이터를 요구합니다. drf-yasg는 API 요청 시에 필요한 데이터를 swagger 문서에서 보여주고, 사용하게 해주는 기능을 제공합니다. 해당 기능은 라이브러리에서 제공하는 데코레이터에 시리얼라이저만 넘겨주면 구현됩니다.
커스텀이 필요한 메서드에 @swagger_auto_schema 데코레이터
를 추가합니다. 이 데코레이터는 파라미터로 request_body를 받는데, 해당 파라미터 값으로는 시리얼라이저 타입만 와야합니다.
# views.py
@swagger_auto_schema(request_body = PostingSerializer, manual_parameters = [parameter_token])
def ....
미리 작성해 놓은 시리얼라이저 클래스를 적용할 수 있습니다.
# serializers.py
class MusicBodySerializer(serializers.Serializer):
singer = serializers.CharField(help_text="가수명")
위 코드에 대한 출처는 아래에 적어두었습니다. 시리얼라이저를 작성할 때 이렇게 작성하는 방법이 더 일반적인 것 같습니다. 하지만 이번 프로젝트에서는 이렇게 사용하지 않았습니다. 위처럼 작성했을 때의 장점은 호출되는 API의 설명을 아주 자세하세 적어 놓을 수 있을 것 같다. 또한 파라미터 옵션을 지정해 줄 때 더 편리하게 할 수 있다는 장점이 있는 것 같습니다.
request에 body가 필요하지 않은 경우에는 body가 없도록 설정할 수 있습니다.
# views.py
@swagger_auto_schema(request_body = no_body)
def ....
헤더는 데코레이터에 manual_parameters 인자를 이용해 구현한다. 해당 인자는 list만 받는다. list값으로 openapi.Parameters()를 넣어주면 된다.
parameter_token = openapi.Parameter(
"Authorization",
openapi.IN_HEADER,
description = "access_token",
type = openapi.TYPE_STRING
)
@swagger_auto_schema(request_body = PostingSerializer, manual_parameters = [parameter_token])
Parameter()는 name, IN 인자는 필수값이라고 합니다. IN의 경우 매우 많은 종류가 있습니다. authorization 부분이니까 이번에는 INHEADER라고 지정을 해줍니다. description은 해당 파라미터에 대한 설명입니다. type은 필수 값은 아니지만 IN
description은 말그대로 해당 파라미터에 대한 설명을 보여준다.
type은 필수값은 아니지만, IN_을 지정했을 경우 필수가 된다고 합니다.
IN_QUERY 옵션에 대해 설명합니다. 이번 프로젝트 때 구현한 부분입니다.
query_parent_comment_id = openapi.Parameter(
"parent_comment_id",
openapi.IN_QUERY,
description = "parent_comment_id",
type = openapi.TYPE_INTEGER
)
query_limit = openapi.Parameter(
"limit",
openapi.IN_QUERY,
description = "limit",
type = openapi.TYPE_STRING
)
query_offset = openapi.Parameter(
"offset",
openapi.IN_QUERY,
description = "offset",
type = openapi.TYPE_STRING
)
@swagger_auto_schema(manual_parameters = [query_parent_comment_id, query_limit, query_offset])
위에서 설명한 것처럼 openapi.Parameters()를 이용해 파라미터를 커스터마이징이 가능합니다. 그리고 커스터메이징한 파라미터를 리스트로 넣어줍니다. 파라미터의 성격을 IN_QUERY로 지정했을 경우 문서에 자동으로 path라고 등록이 됩니다. (Authorization의 경우 header)
drf-ysag를 사용한다면 기존 swagger의 문제점 중 하나인 API response가 제한적인 점을 해결할 수 있습니다. 여러 방법이 있겠으나 비교적 간단한 방법으로 해결해 보도록 합니다. 추후 조금 더 관리가 편한 방식으로 고칠 것입니다.
parameter_token = openapi.Parameter(
"Authorization",
openapi.IN_HEADER,
description = "access_token",
type = openapi.TYPE_STRING
)
error_field = openapi.Schema(
'error',
description = '입력 부분을 수정해주세요',
type=openapi.TYPE_STRING
)
@swagger_auto_schema(request_body = CommentSerializer,
manual_parameters = [parameter_token],
responses = {
400 : error_field
}
)
이 부분에 대한 내용을 공식 문서를 통해 해결해 보고 싶었습니다. 하지만 찾지 못했습니다. 아래 기재된 블로그를 통해 해결할 수 있었습니다.
openapi의 Schema 어트리뷰트를 사용한 방식인 것 같습니다. 데코레이터의 responses에는 dict type을 받으며, key는 status code, value는 응답과 관련된 정보라고 합니다. value에는 일반 텍스트, Schema, Serializer가 들어갈 수 있습니다.
이런 식으로 swagger_auto_schema
데코레이터와 openapi.Parameter
, openapi.Schema
통해 커스텀하고 싶은 부분을 문서화할 수 있습니다.
이 글은 아래 기재된 블로그를 보며 공부한 내용입니다. 코드는 본인의 프로젝트 코드로 대체하였고 내용만 참고하였습니다!
https://hello-cruiser.tistory.com/entry/문서화를-위한-drf-yasg-적용하기