-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
146 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
--- | ||
title: fastapi Swagger Response 분리하기 | ||
author: | ||
name: minseok | ||
link: https://github.com/kkminseok | ||
date: 2024-03-31 00:02:00 +0800 | ||
categories: [openAPI] | ||
tags: [openAPI] | ||
math: true | ||
mermaid: true | ||
image: | ||
path: | ||
comments: true | ||
--- | ||
|
||
|
||
## 개요 | ||
|
||
fastapi 를 사용하고 있는데, fastapi 공식문서를 따라가면서 swagger문서를 작성하다보니 불편함을 느꼈다. | ||
|
||
그것은 바로 | ||
|
||
```python | ||
@router.get("/ingredient", | ||
responses={ | ||
status.HTTP_200_OK: { | ||
"description": "재료 조회 성공", | ||
"content": { | ||
"application/json": { | ||
"example": { | ||
"code": 200, | ||
"message": "Success", | ||
"data": [ | ||
{ | ||
"ingredient_name": "당근", | ||
"ingredient_url": "https://default.image" | ||
}, | ||
{ | ||
"ingredient_name": "토마토", | ||
"ingredient_url": "https://default.image" | ||
}, | ||
{ | ||
"ingredient_name": "치즈", | ||
"ingredient_url": "https://default.image" | ||
} | ||
] | ||
} | ||
} | ||
} | ||
}, | ||
status.HTTP_401_UNAUTHORIZED: {"description": "토큰 에러", | ||
"content": { | ||
"application/json": { | ||
"example": { | ||
"code": 401, | ||
"message": "Parsing Error token", | ||
"data": None | ||
} | ||
} | ||
} | ||
} | ||
} | ||
) | ||
async def search_ingredient_history( | ||
db: Session = Depends(get_db), | ||
user: Optional[UserResponseByToken] = Depends(get_current_user_authorizer()) | ||
): | ||
return StandardizedResponse(code=200, data=await user_service.search_ingredient_history(db, user)) | ||
|
||
``` | ||
|
||
이런 코드들이 `Controller`에 너무 많이 있었다. | ||
|
||
뭐가 불편한가? | ||
|
||
service를 호출하는 코드는 `return StandardizedResponse(code=200, data=await user_service.search_ingredient_history(db, user))`로 **단 한 줄인데** swagger를 작성하는 코드는 약 40줄에 달한다. | ||
|
||
배보다 배꼽이 더 큰 상황인데, 이런 코드들이 점점 많아지니까 스웨거를 자세히 작성하는데에 불편함도 느끼고 함수를 찾아가는데에도 번거로움을 느꼈다. 무엇보다 정리되지 않은 느낌이 싫었다. | ||
|
||
|
||
## 해결방법 | ||
|
||
분리하기 위해서는 2가지 방법이 있었다. | ||
1. 직접 `.yml`파일을 작성하고, 해당 파일을 명시하는 방법 | ||
2. 함수형태로 호출 | ||
|
||
각각 장단점이 있는데, `.yml`파일을 작성하면 간결하게 읽을 수 있겠지만 모델이 변경하는 등의 수정작업이 있으면 `.yml`파일도 같이 수정해줘야한다. | ||
|
||
함수호출형태는 수정작업이 이루어져도 모델을 참조하고 있기에 관리가 용이하지만 파일이 늘어난다는 단점이 있다. | ||
|
||
나는 관리포인트를 최소화하고 싶어서 함수호출형태로 변경하였다. | ||
|
||
## 사전준비 | ||
|
||
```python | ||
@router.post( | ||
"/verification", | ||
description="사용자 토큰 검증 api", | ||
status_code=status.HTTP_200_OK, | ||
response_description="유저의 email과 이름 정보", | ||
responses=verification_user_response() | ||
) | ||
|
||
## 분리된 파일 | ||
def verification_user_response(): | ||
return { | ||
status.HTTP_200_OK: { | ||
"description": "Success", | ||
"content": { | ||
"application/json": MediaType( | ||
example=Example({ | ||
"code": 200, | ||
"message": "Success", | ||
"data": { | ||
"name": "username", | ||
"email": "[email protected]", | ||
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuaWNrbmFtZSI6IkppaG8gSmFuZyIsImV4cCI6MTcxMjIyODAzM30.RUl1PC3VuuQuldnl0IZMg4YEgSq-P1oZmKDrnjzt-uZ" | ||
} | ||
}) | ||
) | ||
} | ||
}, | ||
status.HTTP_401_UNAUTHORIZED: { | ||
"description": "Unauthorized", | ||
"content":{ | ||
"application/json": MediaType( | ||
example=Example({ | ||
"code": 401, | ||
"message": "Parsing Error token", | ||
"data": None | ||
}) | ||
) | ||
} | ||
} | ||
} | ||
``` | ||
|
||
이렇게 작성해주면 된다. | ||
|
||
이게 가능한 이유는 `@router`에 response가 스키마를 구성할때 데이터 타입은 `dictionary`로 데이터를 받고 있기에 위와같은 구조가 가능한 것이다. | ||
|
||
|
||
|
||
이렇게 한결 깔끔하게 문서관리를 할 수 있게 되었다. | ||
|
||
끝 |