# Part 8: FastAPI를 이용한 모델 서빙 **⬅️ 이전 시간: [Part 7.5: LLM 애플리케이션 개발 맛보기](./part_7.5_llm_application_development_with_langchain.md)** **➡️ 다음 시간: [Part 9: 프로덕션 레벨 API와 Docker](./part_9_production_ready_api.md)** --- ## 1. 학습 목표 (Learning Objectives) 이번 파트가 끝나면, 여러분은 다음을 할 수 있게 됩니다. - API의 개념을 '레스토랑 주문'에 비유하여 설명할 수 있습니다. - FastAPI를 사용하여 기본적인 웹 서버를 구축하고 `uvicorn`으로 실행할 수 있습니다. - `lifespan` 이벤트를 사용하여 서버 시작 시 머신러닝 모델을 효율적으로 로드할 수 있습니다. - Pydantic을 사용하여 API의 요청/응답 데이터 형식을 정의하고 자동으로 검증할 수 있습니다. - FastAPI의 자동 생성 문서(Swagger UI)를 통해 브라우저에서 직접 API를 테스트하고 디버깅할 수 있습니다. ## 2. 핵심 키워드 (Keywords) `모델 서빙(Model Serving)`, `API`, `FastAPI`, `uvicorn`, `Pydantic`, `경로 매개변수(Path Parameter)`, `요청 본문(Request Body)`, `Swagger UI`, `lifespan` ## 3. 도입: 나만 알던 맛집 레시피, 세상에 공개하기 (Introduction) 지금까지 우리는 Scikit-learn과 PyTorch로 강력한 AI 모델, 즉 세상에 없는 '비법 레시피'를 개발했습니다. 하지만 이 레시피를 내 서랍 속에만 둔다면 아무도 그 맛을 볼 수 없습니다. > **API 서버는 이 비법 레시피로 요리를 만들어 손님에게 판매하는 '레스토랑'을 차리는 과정입니다.** > 손님(클라이언트)은 주방(모델 서버)이 어떻게 돌아가는지 몰라도, 메뉴판(API 문서)을 보고 주문하면 맛있는 요리(예측 결과)를 받을 수 있습니다. 이번 주차에는 파이썬의 최신 웹 프레임워크인 **FastAPI**를 이용해, 빠르고 세련된 'AI 레스토랑'을 여는 방법을 배워보겠습니다. > [!TIP] > 본 파트의 모든 예제 코드는 `source_code/part_8_model_serving_with_fastapi` 폴더에서 직접 실행하고 수정해볼 수 있습니다. --- ## 4. FastAPI로 우리의 AI 레스토랑 개점하기 > **🎯 1-2일차 목표:** FastAPI와 uvicorn으로 서버를 실행하고, Pydantic으로 데이터 형식을 정의합니다. ### 4-1. 왜 FastAPI일까? - **엄청난 속도**: 주문이 밀려들어도 막힘없이 처리하는 '베테랑 직원'처럼 매우 빠릅니다. - **쉬운 메뉴 관리**: '레시피(코드)'가 간결하고 명확해서, 실수를 줄이고 새로운 메뉴를 빠르게 추가하기 쉽습니다. - **스마트 메뉴판 자동 생성**: 코드를 짜면 손님들이 직접 보고 테스트할 수 있는 자동 대화형 문서(Swagger UI)를 공짜로 만들어줍니다. ### 4-2. 서버 실행과 데이터 형식 정의 먼저 FastAPI와 서버 매니저 `uvicorn`을 설치합니다. ```bash pip install fastapi "uvicorn[standard]" ``` 이제 `main.py` 파일에 레스토랑의 기본 틀을 잡고, 손님의 '주문서 양식'을 **Pydantic**으로 정의합니다. ```python # main.py from fastapi import FastAPI from pydantic import BaseModel from typing import List # 1. Pydantic으로 '주문서 양식' 정의 class IrisFeatures(BaseModel): features: List[float] class Config: # 스마트 메뉴판에 보여줄 주문 예시 json_schema_extra = {"example": {"features": [5.1, 3.5, 1.4, 0.2]}} # 2. FastAPI 인스턴스 생성 (레스토랑 개점) app = FastAPI() # 3. API 엔드포인트 정의 (메뉴판에 메뉴 추가) @app.get("/") def read_root(): return {"message": "어서오세요! 붓꽃 품종 예측 레스토랑입니다!"} # 주문(POST 요청)을 받는 엔드포인트 - 아직 내용은 비어있음 @app.post("/predict") def predict_iris(data: IrisFeatures): return {"주문 접수 완료": data.features} ``` - `@app.get("/")`: 손님이 가게 정문(`/`)으로 들어와 **"여기는 뭐하는 곳인가요?"(GET 요청)**라고 물어보면 응답합니다. - `@app.post("/predict")`: `/predict` 창구에서는 **"이 재료로 요리해주세요"(POST 요청)**라는 주문을 받습니다. - `data: IrisFeatures`: 손님의 주문(JSON)을 우리가 정의한 `IrisFeatures` 양식에 맞춰 자동으로 확인하고, 형식이 틀리면 FastAPI가 알아서 오류를 보냅니다. 이제 터미널에서 서버를 실행합니다. `--reload`는 코드가 바뀔 때마다 서버를 자동으로 재시작해주는 편리한 옵션입니다. ```bash uvicorn main:app --reload ``` 서버 실행 후, 웹 브라우저에서 `http://127.0.0.1:8000` 주소로 접속하여 환영 메시지를 확인하세요. --- ## 5. 예측 API 구현: 레스토랑의 시그니처 메뉴 만들기 > **🎯 3-4일차 목표:** 6주차에서 만든 붓꽃 예측 모델을 FastAPI에 탑재하여, 실제 예측 결과를 반환하는 API를 완성합니다. ### 5-1. 모델 로딩 최적화: `lifespan` 이벤트 무거운 주방기구(모델)를 손님이 올 때마다 켜는 것은 비효율적입니다. 서버가 시작될 때 **단 한번만** 모델을 메모리에 로드하도록 `lifespan` 이벤트를 사용합니다. ```python # main.py (수정 및 추가) from contextlib import asynccontextmanager import joblib import numpy as np # ... (IrisFeatures 정의는 그대로) ... MODELS = {} # 모델을 저장할 딕셔너리 @asynccontextmanager async def lifespan(app: FastAPI): # 서버 시작 시 (가게 오픈) print("레스토랑 오픈: 모델을 준비합니다.") MODELS["iris_model"] = joblib.load("iris_model.pkl") print("모델 준비 완료!") yield # 여기서 애플리케이션이 실행됨 # 서버 종료 시 (가게 마감) MODELS.clear() print("레스토랑 마감") app = FastAPI(lifespan=lifespan) # ... (@app.get("/") 정의는 그대로) ... ``` ### 5-2. 예측 엔드포인트 완성 이제 `/predict` 창구에서 실제 요리(예측)를 하고 결과를 반환하도록 코드를 완성합니다. ```python # main.py (@app.post("/predict") 수정) @app.post("/predict") def predict_iris(data: IrisFeatures): """ 붓꽃의 특징 4가지를 주문서로 받아, 예측된 품종과 신뢰도 점수를 반환합니다. """ model = MODELS["iris_model"] # 주방(모델)이 알아듣는 형태로 재료 손질 model_input = np.array(data.features).reshape(1, -1) # 요리(예측) 시작 prediction_idx = model.predict(model_input)[0] prediction_proba = model.predict_proba(model_input)[0] # 결과 플레이팅 class_names = ['setosa', 'versicolor', 'virginica'] class_name = class_names[prediction_idx] confidence = prediction_proba[prediction_idx] return { "predicted_class": class_name, "confidence": float(confidence) } ``` > **[필수]** 6주차 실습에서 저장한 `iris_model.pkl` 파일을 `main.py`와 같은 폴더에 준비하고, `scikit-learn`, `joblib`을 설치해주세요. > `pip install scikit-learn joblib numpy` --- ## 6. 직접 해보기 (Hands-on Lab): 스마트 메뉴판으로 시식하기 > **🎯 5일차 목표:** FastAPI의 자동 API 문서를 사용하여, 코드를 건드리지 않고 브라우저에서 직접 API를 테스트합니다. FastAPI의 가장 강력한 기능, **스마트 메뉴판(Swagger UI)**을 이용해봅시다. 1. `uvicorn main:app --reload` 명령으로 서버가 실행 중인지 확인합니다. 2. 웹 브라우저에서 **`http://127.0.0.1:8000/docs`** 로 접속합니다. > **💡 비유: '스마트 메뉴판' Swagger UI** > `/docs`는 단순한 메뉴판이 아닙니다. > - **모든 메뉴(엔드포인트)가 목록으로** 보입니다. > - 각 메뉴를 클릭하면 **필요한 재료(요청 형식)와 나오는 요리(응답 형식)**에 대한 상세한 설명이 나옵니다. > - 가장 멋진 기능! **`Try it out`** 버튼을 누르면 메뉴판에서 바로 **'맛보기 주문'**을 넣어볼 수 있습니다. 주방에 직접 가지 않아도 그 자리에서 바로 요리 결과를 확인할 수 있습니다. ### 문제: 1. `/docs` 페이지에서 `POST /predict` 메뉴를 클릭하여 펼치고 `Try it out` 버튼을 누르세요. 2. **Request body**에 다른 붓꽃 데이터를 넣어보세요. (예: `versicolor` 품종인 `[6.0, 2.2, 4.0, 1.0]` 또는 `virginica` 품종인 `[6.7, 3.0, 5.2, 2.3]`) 3. `Execute` 버튼을 눌러 예측 결과를 확인하고, 모델이 올바르게 예측하는지 검증해보세요. --- ## 7. 되짚어보기 (Summary) 이번 주에는 FastAPI를 이용하여 머신러닝 모델을 API로 만드는 첫걸음을 떼었습니다. - **AI 레스토랑**: 모델을 '레시피', API 서버를 '레스토랑'에 비유하여 모델 서빙의 개념을 이해했습니다. - **FastAPI와 Uvicorn**: 파이썬 웹 서버를 구축하고 실행하는 방법을 배웠습니다. - **Pydantic과 lifespan**: API의 데이터 형식을 강제하고, 서버 시작 시 모델을 효율적으로 로드하는 방법을 익혔습니다. - **Swagger UI**: `/docs`의 '스마트 메뉴판'을 통해 API를 쉽고 빠르게 테스트하는 강력한 기능을 체험했습니다. ## 8. 더 깊이 알아보기 (Further Reading) - [FastAPI 공식 튜토리얼](https://fastapi.tiangolo.com/tutorial/): FastAPI의 모든 것을 배울 수 있는 최고의 가이드 - [Pydantic 공식 문서](https://docs.pydantic.dev/latest/): 데이터 유효성 검사를 더 깊이 있게 활용하는 방법 - [Serving Machine Learning Models](https://www.manning.com/books/serving-machine-learning-models): 모델 서빙에 대한 전반적인 내용을 다루는 전문 서적 --- **➡️ 다음 시간: [Part 9: 프로덕션 레벨 API와 Docker](./part_9_production_ready_api.md)**