OpenAPI로 인터랙티브 API 문서화를 쉽게 만드는 방법
Emily Parker
Product Engineer · Leapcell

소개
빠르게 변화하는 소프트웨어 개발 세계에서 강력하고 잘 문서화된 API를 구축하는 것은 매우 중요합니다. 아무리 강력한 API라도 개발자가 API와 상호 작용하는 방법을 쉽게 이해할 수 없다면 진정으로 유용하지 않습니다. 전통적으로 API 문서는 지루하고 종종 무시되는 작업으로, 업데이트되지 않거나 불완전한 가이드로 이어져 채택을 방해하고 개발 속도를 늦춥니다. API 문서가 항상 최신 상태이며 대화형이고 코드베이스에서 거의 쉽게 생성되는 세상을 상상해 보세요. 이것은 먼 꿈이 아닙니다. OpenAPI(이전 Swagger)와 Flask, FastAPI, Gin과 같은 인기 있는 백엔드 프레임워크와의 원활한 통합으로 가능해진 현실입니다. 이 글에서는 이 강력한 조합을 활용하여 대화형 문서를 자동으로 생성하는 방법을 살펴보고, 개발자 경험과 API 제공의 전반적인 품질을 크게 향상시킵니다.
핵심 개념 설명
실질적인 내용에 들어가기 전에 관련 핵심 개념에 대한 공통된 이해를 확립해 봅시다.
- OpenAPI 명세: RESTful API에 대한 언어에 구애받지 않고 사람이 읽을 수 있는 설명 형식입니다. 개발자가 API의 엔드포인트, 작업, 입력/출력 매개변수, 인증 방법 등을 표준화된 JSON 또는 YAML 형식으로 설명할 수 있습니다.
- Swagger UI: OpenAPI 명세를 중심으로 구축된 오픈 소스 도구 모음입니다. 가장 눈에 띄는 기능은 API를 시각화하고 브라우저에서 직접 요청을 보낼 수 있도록 하여 탐색 및 테스트를 매우 직관적으로 만드는 대화형 웹 기반 문서 인터페이스입니다.
- API 게이트웨이: 엄격하게 OpenAPI의 일부는 아니지만 API 게이트웨이는 종종 문서화된 API와 함께 작동합니다. 모든 클라이언트 요청에 대한 단일 진입점 역할을 하며, 적절한 백엔드 서비스로 요청을 라우팅하고 인증, 속도 제한, 트래픽 관리와 같은 작업을 처리합니다. 잘 문서화된 API는 API 게이트웨이와 통합하기가 더 쉽습니다.
- RESTful API: Representational State Transfer(REST)의 원칙을 준수하는 API 유형으로, 클라이언트와 서버 간의 상태 비저장 통신을 위해 표준 HTTP 메서드(GET, POST, PUT, DELETE)를 사용합니다. OpenAPI는 주로 RESTful API를 문서화하는 데 사용됩니다.
자동 대화형 문서 생성
대화형 문서를 자동으로 생성하는 기본 원리는 API의 구조와 동작을 설명하기 위해 코드 내에서 선언적 구문을 사용하는 것입니다. 프레임워크는 이 구문을 직접 해석하거나 데코레이터/주석을 사용하여 OpenAPI 명세 문서로 변환할 수 있는 내부 표현을 빌드합니다. 그런 다음 Swagger UI는 이 OpenAPI 문서를 가져와 아름답고 대화형 인터페이스로 렌더링합니다.
FastAPI
FastAPI는 자동 문서 생성에 대한 탁월한 지원으로 유명합니다. 데이터 유효성 검사 및 유형 힌트에 Pydantic을 사용하므로 OpenAPI 스키마를 매우 쉽게 생성할 수 있습니다.
# main.py from fastapi import FastAPI, Depends, HTTPException, status from pydantic import BaseModel from typing import List, Optional app = FastAPI( title="My Awesome API", description="A simple API for managing items.", version="1.0.0" ) # 시연을 위한 인메모리 데이터베이스 items_db = [] class Item(BaseModel): id: int name: str description: Optional[str] = None price: float tax: Optional[float] = None class ItemCreate(BaseModel): name: str description: Optional[str] = None price: float tax: Optional[float] = None @app.post("/items/", response_model=Item, status_code=status.HTTP_201_CREATED, summary="Create a new item") async def create_item(item: ItemCreate): """ Provides a new item with the provided details. - **name**: Name of the item - **description**: Optional description of the item - **price**: Price of the item - **tax**: Optional tax percentage """ new_id = len(items_db) + 1 new_item = Item(id=new_id, **item.dict()) items_db.append(new_item) return new_item @app.get("/items/", response_model=List[Item], summary="Get all items") async def read_items(skip: int = 0, limit: int = 10): """ Retrieve a list of all items, with optional pagination. - **skip**: Number of items to skip - **limit**: Maximum number of items to return """ return items_db[skip : skip + limit] @app.get("/items/{item_id}", response_model=Item, summary="Get a single item by ID") async def read_item(item_id: int): """ Retrieve a single item by its ID. - **item_id**: The ID of the item to retrieve """ for item in items_db: if item.id == item_id: return item raise HTTPException(status_code=404, detail="Item not found")
실행하려면 FastAPI와 Uvicorn을 설치하세요: pip install fastapi uvicorn pydantic
. 그런 다음 uvicorn main:app --reload
를 실행합니다.
FastAPI는 자동으로 /docs
에서 대화형 문서(Swagger UI)를, /redoc
에서 Redoc UI를 제공합니다.
여기서 마법은 FastAPI가 Pydantic 모델을 요청 및 응답 유효성 검사에 사용하고 이를 자동으로 OpenAPI 스키마로 변환한다는 것입니다. 함수의 Docstring은 경로 설명이 되고 데코레이터의 summary
매개변수는 간결한 제목을 제공합니다.
Flask with Flask-RESTx (or Flask-Marshmallow-Webargs + APISpec)
Flask는 마이크로프레임워크이므로 OpenAPI 통합을 위한 외부 라이브러리가 필요합니다. Flask-RESTx
(유지 관리되지 않는 Flask-RESTPlus
의 포크)는 훌륭한 Swagger UI 통합을 제공합니다. 더 간단한 경우 또는 더 많은 제어를 위해서는 Pydantic/Marshmallow와 APISpec
의 조합도 작동할 수 있습니다.
Flask-RESTx
를 사용한 예는 다음과 같습니다.
# app.py from flask import Flask from flask_restx import Api, Resource, fields app = Flask(__name__) api = Api(app, version='1.0', title='Item API', description='A simple Flask API for managing items.', doc='/docs/') # Swagger UI에 대한 사용자 지정 경로 # 요청/응답 직렬화를 위한 모델 정의 item_model = api.model('Item', { 'id': fields.Integer(readOnly=True, description='The unique identifier of an item'), 'name': fields.String(required=True, description='The item name'), 'description': fields.String(description='The item description'), 'price': fields.Float(required=True, description='The item price'), 'tax': fields.Float(description='The item tax') }) # 인메모리 데이터베이스 items = [] current_id = 1 @api.route('/items') class ItemList(Resource): @api.doc('list_items') @api.marshal_list_with(item_model) def get(self): """List all items""" return items @api.doc('create_item') @api.expect(item_model, validate=True) @api.marshal_with(item_model, code=201) def post(self): """Create a new item""" global current_id new_item = api.payload new_item['id'] = current_id items.append(new_item) current_id += 1 return new_item, 201 @api.route('/items/<int:id>') @api.param('id', 'The item identifier') class Item(Resource): @api.doc('get_item') @api.marshal_with(item_model) def get(self, id): """Retrieve a single item""" for item in items: if item['id'] == id: return item api.abort(404, "Item not found") if __name__ == '__main__': app.run(debug=True)
설치: pip install Flask Flask-RESTx
. python app.py
로 앱 실행. Swagger UI는 /docs/
에서 사용할 수 있습니다.
Flask-RESTx
는 api.model
을 사용하여 데이터 구조를 정의하고 api.expect
및 api.marshal_with
와 같은 데코레이터를 사용하여 이러한 구조를 API 작업에 연결하며, 이는 OpenAPI 사양을 구축하는 데 사용됩니다.
Swag와 Gin
Gin 프레임워크를 사용하는 Go(Golang) 애플리케이션의 경우 Swag
는 Go 주석에서 OpenAPI 문서를 생성하는 인기 있는 패키지입니다.
먼저 swag
명령줄 도구를 설치해야 합니다: go get -u github.com/swaggo/swag/cmd/swag
그런 다음 Go 프로젝트에서: go get -u github.com/gin-gonic/gin
및 go get -u github.com/swaggo/gin-swagger
및 go get -u github.com/swaggo/files
// main.go package main import ( "log" "net/http" "strconv" "github.com/gin-gonic/gin" _ "your-project-path/docs" // 생성된 문서 가져오기 sswaggerFiles "github.com/swaggo/files" ginSwagger "github.com/swaggo/gin-swagger" ) type Item struct { ID int `json:"id" example:"1"` Name string `json:"name" example:"Laptop"` Description *string `json:"description,omitempty" example:"Powerful workstation"` Price float64 `json:"price" example:"1200.00"` Tax *float64 `json:"tax,omitempty" example:"8.5"` } type CreateItemPayload struct { Name string `json:"name" example:"Laptop"` Description *string `json:"description,omitempty" example:"Powerful workstation"` Price float64 `json:"price" example:"1200.00"` Tax *float64 `json:"tax,omitempty" example:"8.5"` } var items []Item var currentID = 1 // @title Gin Item API // @version 1.0 // @description This is a simple Gin API for managing items. // @host localhost:8080 // @BasePath /api/v1 func main() { router := gin.Default() // Docs route router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) v1 := router.Group("/api/v1") { v1.GET("/items", getItems) v1.POST("/items", createItem) v1.GET("/items/:id", getItemByID) } log.Fatal(router.Run(":8080")) // listen and serve on 0.0.0.0:8080 } // @Summary Get all items // @Description Retrieve a list of all items, with optional pagination. // @Accept json // @Produce json // @Param skip query int false "Number of items to skip" default(0) // @Param limit query int false "Maximum number of items to return" default(10) // @Success 200 {array} Item // @Router /items [get] func getItems(c *gin.Context) { skipStr := c.DefaultQuery("skip", "0") limitStr := c.DefaultQuery("limit", "10") sskip, _ := strconv.Atoi(skipStr) limit, _ := strconv.Atoi(limitStr) if skip < 0 { skip = 0 } if limit <= 0 { limit = 10 } end := skip + limit if end > len(items) { end = len(items) } if skip > len(items) { skip = len(items) } c.JSON(http.StatusOK, items[skip:end]) } // @Summary Create a new item // @Description Creates a new item with the provided details. // @Accept json // @Produce json // @Param item body CreateItemPayload true "Item creation payload" // @Success 201 {object} Item // @Router /items [post] func createItem(c *gin.Context) { var payload CreateItemPayload if err := c.ShouldBindJSON(&payload); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } newItem := Item{ ID: currentID, Name: payload.Name, Description: payload.Description, Price: payload.Price, Tax: payload.Tax, } items = append(items, newItem) currentID++ c.JSON(http.StatusCreated, newItem) } // @Summary Get a single item by ID // @Description Retrieve a single item by its ID. // @Accept json // @Produce json // @Param id path int true "The ID of the item to retrieve" // @Success 200 {object} Item // @Failure 404 {object} gin.H "Item not found" // @Router /items/{id} [get] func getItemByID(c *gin.Context) { idStr := c.Param("id") id, err := strconv.Atoi(idStr) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid item ID"}) return } for _, item := range items { if item.ID == id { c.JSON(http.StatusOK, item) return } } c.JSON(http.StatusNotFound, gin.H{"message": "Item not found"}) }
Swag
를 사용하려면 주석이 있는 코드를 작성한 후 터미널에서 프로젝트 디렉토리로 이동하여 다음을 실행합니다: swag init
.
이 명령은 주석을 분석하고 swagger.json
(또는 swagger.yaml
) 및 swagger.go
파일을 포함하는 docs
폴더를 생성합니다.
swag init
을 실행하고 go run main.go
를 실행하면 Swagger UI는 http://localhost:8080/swagger/index.html
에서 사용할 수 있습니다.
Swag
는 함수 및 구조체 위의 특별히 형식화된 주석(주석)을 사용하여 API 엔드포인트, 매개변수, 응답 및 모델을 정의합니다. 그런 다음 swag init
명령은 이러한 주석을 처리하여 OpenAPI 사양을 생성합니다.
애플리케이션 시나리오 및 이점
자동으로 생성된 대화형 문서는 다양한 이점을 제공합니다.
- 개발자 경험 향상: 외부 및 내부 개발자는 광범위한 설정이나 사전 지식 없이 API를 신속하게 이해하고 상호 작용할 수 있습니다. Swagger UI의 "Try it out" 기능을 사용하면 직접 API 호출이 가능하여 신속한 프로토타이핑 및 테스트가 용이합니다.
- 문서 유지 관리 감소: API가 발전함에 따라 코드 수정 시(그리고 필요한 경우 생성기 재실행 시, Gin의 경우와 같이) 문서가 자동으로 업데이트됩니다. 이렇게 하면 오래되거나 일관성 없는 문서라는 일반적인 문제가 제거됩니다.
- API 검색 용이성 향상: 명확하고 대화형인 API 참조는 API를 잠재 사용자에게 더 매력적이고 통합하기 쉽게 만들어 채택률을 높입니다.
- 더 나은 API 디자인 일관성: 엔드포인트와 데이터 구조를 명시적으로 문서화하는 과정은 종종 개발 초기 단계에서 API 디자인의 불일치 또는 개선 영역을 드러냅니다.
- 도구 통합: OpenAPI 사양은 코드 생성, 테스트 및 보안 분석을 위한 광범위한 도구 생태계에서 널리 지원됩니다. 이렇게 하면 클라이언트 SDK, 서버 스텁 등을 생성할 수 있는 가능성이 열립니다.
- 협업: API 계약에 대한 단일 진실 공급원을 제공하여 프론트엔드, 백엔드 및 QA 팀 간의 협업을 더 효율적으로 만듭니다.
결론
OpenAPI를 사용하여 Flask, FastAPI 또는 Gin 애플리케이션에 대한 대화형 문서를 자동으로 생성하는 것은 고품질의 개발자 친화적인 API를 구축하는 중요한 단계입니다. 코드에 직접 설명 요소를 통합함으로써 번거로운 사후 작업에서 개발 프로세스의 필수적인 부분으로 문서를 변환합니다. 이 접근 방식은 API 문서를 항상 최신 상태로 유지할 뿐만 아니라 검색 용이성, 사용성 및 전반적인 API 성공을 크게 향상시켜 궁극적으로 관련된 모든 사람에게 더 효율적이고 즐거운 개발 경험을 조성합니다. OpenAPI를 활용하세요. 개발자들이 감사할 것입니다.