Jin
by Jin
R&M teamleader
다양한 산업에 AI 모델을 이식하기 위한 테스트 기획 및 수행을 담당하고 있습니다.
4 min read

Tags

완성된 사내 AI 모듈을 테스트 용도로 배포할 때, 현재는 코드 전달 등 다양한 방식과 여러 프레임워크가 무질서하게 사용되고 있는 실정입니다. 이에 따라 별도 요청이 없는 한, Python FastAPI 프레임워크를 기본으로 사용하기로 하였습니다. 이에 따라, http 프로토콜 메서드, 요청형태, 미들웨어, 도큐멘팅 4가지 요소에서 대한 최소한의 표준을 아래와 같이 권장합니다. 특히 요청형태는 별도의 요청이 없는 경우 반드시 준수해 주시기 바랍니다.

http 프로토콜 메서드

http 프로토콜 메서드는 웹 브라우저와 웹서버 간의 통신을 위해 표준화된 규약입니다. 대표적인 규약인 GET은 URL에 데이터가 노출되고, POST는 노출되지 않는 것이 주요 특징이며, 요청문이 길어지면 POST를 쓰는 경향이 있습니다.

fastapi를 통해 완성된 API는 주로 파이썬으로 테스트 되기도 하고, 굳이 URL에 데이터를 노출시킬 필요가 없으며, 텍스트 입력 시 최소 한 문장에서 최대 여러 문장의 입력값이 넘어가 긴 편에 속하기 때문에 별다른 요청이 없다면 POST 방식으로 코드를 구성합니다.

요청 형태

터미널에서 API를 요청하는 방식인 curl을 활용할 때, 매개변수를 작성하는 대표적인 포멧은 x-www-form-urlencoded(이하 폼)와 json이 있고, 그 예시는 아래와 같습니다.

curl -X POST http://0.0.0.0.:8080/item_post -H 'Content-Type: application/x-www-form-urlencoded' -d "name=string&description=example&price=0"
  • json
curl -X POST http://0.0.0.0.:8080/item_post -H 'Content-Type: application/json' -d '{"name":"string", "description":"example", "price":0}'

JSON 형식은 API 통신에서 널리 사용되는 표준 포맷으로, 다양한 언어 및 플랫폼과의 호환성이 뛰어나고 구조화된 데이터 전달에 유리합니다. 또한, curl 기능을 수행하는 Python의 requests 패키지에서도 JSON 형식은 dict 타입으로 간편하게 처리됩니다. 이에 따라, 별도의 요청이 없는 한 JSON 형식으로 데이터를 수신하도록 하고, FastAPI의 Request Body를 활용하여 코드를 작성합니다. JSON 형식을 처리하는 코드 예시는 아래와 같습니다.

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()  # 아래 '4.1 객체선언기능'을 통해 상세 정보 확인 가능

# JSON 형식의 요청 바디를 받을 모델 정의
class Item(BaseModel):
    name: str
    description: str
    price: Optional[float] = 0.7

@app.post("/items_post")
async def items_post(item: Item):
    return {
        "name": item.name,
        "description": item.description,
        "price": item.price
    }

[주의] 최근 JSON 방식에 대한 요청도 빈번히 일어나고 있으며 이에 따라, pydantic패키지에 BaseModel 메서드를 이용하여 json 포멧으로 요청을 할 수 있게 코드를 구성할 경우, request 메서드를 통해 요청을 보낸다면 json.dumps를 통해 dict 타입을 str타입으로 변경하여 요청해야 합니다.

미들웨어

1. 로깅

utils 디렉션에 로깅 모듈과 같이 logutils.py 파일을 작성한 후 로거를 불러와 사용합니다.

2. CORS처리 (Cross-Origin Resource Sharing, 고객사 요청 및 필요에 따라 작성)

접근 가능한 도메인, 프로토콜 메서드, 헤더를 정의하고 쿠키 및 인증 접근 허용하는 것을 설정하는 기능을 말합니다. 다른 요청이 없다면, 주로, 모든 도메인 허용, GET, POST 메서드 허용, 모든 헤더 허용, 자격증명 허용으로 설정하지만, 고객사와 커뮤니케이션 할때는 반드시 위 내용을 확인해 주세요. fastapi에서 CORS 처리하는 코드 예시는 아래와 같습니다.

from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],          # 모든 도메인에서 이 API로의 액세스 허용
    allow_credentials=True,       # 자격 증명(예: 쿠키, HTTP 인증)을 허용
    allow_methods=["GET", "POST"],# 모든 HTTP 메서드 (GET, POST, PUT, DELETE 등)를 허용
    allow_headers=["authorization"],          # 모든 헤더를 허용
)

3. 인증

인증은 여러 방식이 있지만, fastAPI를 활용한 시크릿키 적용 혹은 IP 허용만 설명합니다. 다른 방식으로 요청이 오면 개발팀과 논의하고 가능한 수준에서 작성합니다.

시크릿키 적용

헤더에 secret-key를 만들어 전달하는 방식은 아래 코드와 같습니다.

from fastapi import FastAPI, Form, Header, HTTPException

app = FastAPI() # 아래 '4.1 객체선언기능'을 통해 상세 정보 확인 가능

@app.post("/items_post")
async def items_post(
    name: str = Form(...), 
    description: str = Form(...), 
    price: float = Form(...).
    secret_key: str = Header(None, description="secret key")
    ):

    if secret_key !="{인증 키}":
        raise HTTPException(status_code=403, detail="Invalid secret-key")

    return {
        "name": name,
        "description": description,
        "price": price
        }

위 API를 요청할떄 header 에 시크릿 키를 추가 합니다.

curl -X POST http://0.0.0.0:8080/item_post \
     -H "Content-Type: application/json" \
     -H "secret-key: your_secret_key" \
     -d '{"name":"string", "description":"example", "price":0}'

IP 허용

ip 허용을 위한 미들웨어는 BaseHTTPMiddleware를 상속받아 아래와 같이 객체를 만들어 사용할 수 있습니다.

from starlette.middleware.base import BaseHTTPMiddleware

ALLOWED_IPS = {"0.0.0.0", "0.0.0.1"}  # 허용할 IP 주소를 여기에 추가
# IP 인증을위한 모듈
class IPFilterMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        client_ip = request.client.host
        if client_ip not in ALLOWED_IPS:
            raise HTTPException(status_code=403, detail="Access forbidden")

        response = await call_next(request)
        return response

app.add_middleware(IPFilterMiddleware)

4. 도큐멘팅

fastAPI는 아래 그림과 같이 “/redoc” url을 활용하면 내가 만든 API를 설명할 수 있는 웹페이지 생성 기능을 제공하고 있으며, 저희 팀은 이를 활용하고자 합니다. 본 기능은 fastapi에서 제공하는 객체선언기능과 함수별 도큐멘팅 기능을 활용하여 완성할 수 있으며, 마크다운 문법을 따릅니다. fastapi_redoc_sample

4.1 객체선언기능

FastAPI 매서드를 활용하여 app 객체를 선언할 때 title, version, description 옵션을 활용하면 API 전체를 설명할 수 있습니다. 활용 예시 코드는 아래와 같습니다.

from fastapi import FastAPI

app = FastAPI(
        title="hyungjin api",
        version="0.1.0",
        description= """마크다운 문법을 이용항여 필요시 내용을 추가합니다.
        """
        )

위 코드를 적용하였을 때 나타나는 웹페이지 일부는 아래 그림과 같습니다. fastapi_title

함수별 도큐멘팅 기능

python 코드 내에서 내가 작성한 함수를 설명하 듯 함수 선언문 아래 멀티라인 문자열(“”“ “”“)을 이용하고 안에 마크다운 문법을 사용하면 redoc 웹사이트 내 정리가 자동으로 완성 됩니다. 예시는 아래와 같습니다.

@app.post("/items_post")
async def items_post(
    name: str = Form(...), 
    description: str = Form(...), 
    price: float = Form(...)
    ):
    """
    ### header2
    - (필요시) 설명해주세요

    ### python code
    ```python
    import ast 
    import requests
    from pprint import pprint

    url = 'http://0.0.0.0:0000/items_post/'
    data = {
        "name": "name", 
        "description" : "example", # 설명
        "price": 0 
        }

    response = requests.post(url, data=data)
    response_dict = ast.literal_eval(response.text) # str to dict
    pprint(response_dict)
    ```

    ### output
    ```
    {'description': 'example', 'name': 'string', 'price': 0.0}
    ```
    """

위 코드를 적용하였을 때 나타나는 웹페이지 일부는 아래 그림과 같습니다. fastapi_def_explain