Post

FastAPI로 개발하기

FastAPI로 개발하기

🗂️ 목차

  1. intro
  2. FastAPI란
  3. 개발환경
  4. 가상환경 설정
  5. app 폴더 설정
  6. 서버 연동 확인
  7. 마무리

1. intro

2차 프로젝트로 금융 서비스에 LLM을 연동하기 위해서 파이썬 웹 프레임워크가 필요했다. DRF(Django Rest Framework)와 Flask로 프로젝트를 진행해 본 경험이 있었지만,
백엔드는 SpringBoot를 메인으로 가져가기 위해서 FastAPI를 사용해보고자 한다.

이번 글에서는 가상환경을 만들고, FastAPI 앱을 띄운 뒤, Swagger UI(/docs) 로 API가 정상 노출되는지 확인해 볼 예정이다.
(LLM 연동은 다음 글에서 다룰 예정 😉)


2. FastAPI란?

FastAPI는 파이썬 기반의 웹 프레임워크로 API를 만드는데 집중적으로 사용되며, 이름에 걸맞게 빠르다고 한다.

🔍 FastAPI 장점

  • 타입 안정성: typing + Pydantic으로 요청/응답 스키마 검증 자동
    • 함수에 타입 힌트를 달면, 그걸 근거로 요청 검증과 응답 직렬화를 자동으로 처리
    • 런타임시 Pydantic이 데이터 타입을 변환/검증해 버그를 초기에 차단해줌
  • 자동 문서화 : OpenAPI 스펙 → /docs 바로 생성
    • 자동으로 API 문서(웹)을 통해서 바로 테스트 할 수 있음 (Swagger)
  • 고성능/비동기: ASGI(Uvicorn)로 동시성 우수
    • FastAPI는 WSGI(동기) 대신 ASGI(비동기) 스택(Starlette + Uvicorn)을 사용
      I/O 바운드 작업(DB, 외부 API, 파일, 스트리밍)에 동시성 처리 강함
      ** ASGI : 파이썬 웹 서버, 프레임워크, 애플리케이션 간의 통신을 위한 표준 인터페이스
    • 엔드포인트를 async def로 작성하면 await 가능한 호출을 논블로킹으로 처리
    • 웹소켓/스트리밍에도 기본적으로 강함

3. 개발 환경

이 글은 Windows 유저가 쓴 글이기 때문에 Windows 기준으로 작성되었음을 미리 알립니다.

  • Shell: Git Bash(MINGW64) 또는 PowerShell
  • IDE: VS Code
  • Python: 3.13.7

4. 가상환경 설정

가상환경?

프로젝트의 패키지 버전 충돌을 막고 의존성 일치를 보장하기 위해 가상환경을 사용함!

  • 독립된 개발 환경 구성 가능
  • 협업시 버전 관리에 용이함

언어는 다르지만 Spring Boot에서 사용되는 일종의 의존성이나 버전관리 용도인 pom.xml이나 build.gradle과 유사하다고 할 수 있음.

가상환경 설정하기

  • 프로젝트 루트 폴더로 이동해 가상환경을 설정한다.

방법으로 가상환경 .venv를 설정해주면 구조는 다음과 같다.

1
2
3
4
5
6
venvproj/            # 루트폴더
├─ .venv/            # 가상환경
└─ app/
   ├─ __init__.py
   └─ main.py

루트 폴더에서 가상환경 설정해야 함!!
루트에서 가상환경을 설정해야 한다. 잘못된 경로에 하지 않도록 주의하자!

1
2
3
4
5
cd ../venvproj        # 루트 폴더로 이동  
python -m venv .venv  #.venv라는 가상환경 생성  
# (선택) 특정 버전으로 만들고 싶다면
# py -3.12 -m venv .venv  

.venv의 가상환경을 실행하기 위해서 venv의 Scripts를 활성화 해주면 된다.
활성화되면 프롬포트 앞에 (.venv)표시 생김

1
2
source .venv/Scripts/activate # 활성화
deactivate                    # 비활성화

정상 동작 확인

1
2
3
4
python -V
pip -V
python -c "import sys; print(sys.executable)"
# 출력 경로에 .../exproj/.venv/ 가 포함되어 있으면 OK

기본 패키지 설치
pip를 최신 버전으로 설치하고, FastAPI 서버를 위한 최소 패키지 설치

1
2
python -m pip install -U pip 
python -m pip install fastapi "uvicorn[standard]"
  • uvicorn[standard]: FastAPI 앱을 실행하는 ASGI 서버

5. app 폴더 설정

루트 경로 밑에 app 폴더를 생성한다. 여기서 파이썬 코드(엔드포인트), 라우터, 예제(JSON)를 관리한다.

디렉터리 구조

1
2
3
4
5
6
7
8
9
10
11
12
13
exproj/
├─ .venv/
└─ app/
   ├─ __init__.py
   ├─ main.py
   ├─ routers/
   │  ├─ __init__.py
   │  └─ example.py
   └─ examples/
      ├─ spending_series.json
      ├─ advice.json
      └─ index.html

파일 생성은 명령어를 사용해도 되고, 직접 생성해도 된다.

1
2
3
mkdir -p app/routers app/examples
:> app/__init__.py
:> app/routers/__init__.py

아래 코드를 추가한 뒤 서버를 실행해 잘 작동하는지 확인하자~!

app/main.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import os
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from app.routers import example ## 라우터 import

# 환경변수로 API prefix와 CORS 허용 오리진을 제어
API_PREFIX = os.getenv("API_AI_PREFIX", "/v1")

app = FastAPI(title="Ohgoodpay ML", version="0.1.0")

# CORS 설정
app.add_middleware(
    CORSMiddleware,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# 예시: example.py 파일에 작성한 라우터
app.include_router(example.router, prefix=API_PREFIX, tags=["example"])

# 간단 핑 엔드포인트 
@app.get(API_PREFIX + "/ping")
def ping():
    return {"service": "ohgoodpay-ai", "status": "ok"}

app/routers/example.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from fastapi import APIRouter, HTTPException
from fastapi.responses import HTMLResponse, JSONResponse
from pathlib import Path
import json

router = APIRouter()
EXAMPLES_DIR = Path(__file__).resolve().parents[1] / "examples"  # app/examples

def read_json(name: str):
    p = EXAMPLES_DIR / name
    if not p.exists():
        raise HTTPException(404, f"Not found: {p}")
    text = p.read_text(encoding="utf-8-sig").strip()  # BOM 허용 + 공백 제거
    if not text:
        raise HTTPException(500, f"{p.name} is empty")
    try:
        return JSONResponse(json.loads(text))
    except json.JSONDecodeError as e:
        raise HTTPException(500, f"JSON parse error in {p.name}: line {e.lineno}, col {e.colno} - {e.msg}")

@router.get("/example", response_class=HTMLResponse)
def example_page():
    idx = EXAMPLES_DIR / "index.html"
    return idx.read_text(encoding="utf-8") if idx.exists() else "<h1>example</h1>"

@router.get("/example/spending/series")
def example_spending_series():
    return read_json("spending_series.json")

@router.get("/example/advice")
def example_advice():
    return read_json("advice.json")

app/examples/spending_series.json

1
2
3
4
5
6
7
8
9
10
11
12
13
{
  "userId": "ex",
  "groupBy": "category",
  "labels": ["식비","교통","쇼핑","구독"],
  "values": [430000, 120000, 310000, 45000],
  "trend": {
    "x": ["2025-06","2025-07","2025-08"],
    "series": {
      "식비":  [120000,140000,170000],
      "쇼핑":  [80000,110000,120000]
    }
  }
}

app/examples/advice.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
  "summary": "쇼핑 카테고리 비중이 높습니다.",
  "insights": [
    {"title": "쇼핑 집중", "detail": "전체 BNPL의 52%가 쇼핑에서 발생", "impact": "down"},
    {"title": "구독 자동결제", "detail": "BNPL 구독 3건 → 월 3.2만원", "impact": "neutral"}
  ],
  "actions": [
    "월 상한 15만원 설정",
    "장바구니 10만원 초과시 알림",
    "구독 1건 해지로 월 1.1만원 절감"
  ]
}


화면을 위한 간단한 html 작성
app/examples/index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!doctype html>
<html lang="ko">
<head>
  <meta charset="utf-8" />
  <title>Ohgoodpay AI example</title>
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <style>
    body { font-family: system-ui, sans-serif; margin: 2rem; }
    .card { padding: 1rem; border: 1px solid #eee; border-radius: 12px; max-width: 720px; }
    ul { line-height: 1.9; }
    code { background:#f6f8fa; padding:.1rem .35rem; border-radius:6px; }
  </style>
</head>
<body>
  <h1>Ohgoodpay AI example</h1>
  <p class="card">
    다음 엔드포인트로 예시 데이터를 확인하세요.
    <ul>
      <li><a href="/docs">/docs</a> (OpenAPI)</li>
      <li><a href="/v1/example/advice/series">/v1/example/advice</a></li>
      <li><a href="/v1/example/spending/series">/v1/example/spending/series</a></li>
    </ul>
  </p>
</body>
</html>

6. 서버 연동 확인

위의 설정이 완료되었으면 서버 실행 후 아래 엔드포인트로 접근해보자.
http://localhost:8000/v1/example

서버 연결

이전에 설정한 html화면이 나오면
서버 연결이 잘 된 것을 확인할 수 있다.

각 라우터 경로 확인

우선 advice와 spending/series로 각각 진입해보면

advice.json 설정 화면이 나오고,
조언제이슨
spending_series.json 설정 화면이 나온다.
사용량제이슨

docs

마지막으로 /docs 엔드포인트로 접근하면,
Swagger ui가 나오면서 간단하게 API 테스트를 진행할 수 있다!
스웨거독스

하나만 예시로 확인해보자, Try it out 을 눌러, Execute해보면
아래와 같이 API 테스트가 진행됨을 확인할 수 있다!!
스웨거독스
스웨거독스

주의

uvicorn not found → venv 활성화 후 python -m uvicorn …
ModuleNotFoundError: app → 루트에서 실행 + app/init.py 확인


마무리

로컬에서 FastAPI + Swagger를 확인하는 최소 환경을 완성했다. 남은 건 비즈니스 로직과 LLM 연동🫡
다음 글에서 이어가도록 하겠다. 빨리 오겠음.

투비컨티뉴

This post is licensed under CC BY 4.0 by the author.