Logger 모듈을 사용한 FastAPI 기본 로깅

발행: (2026년 1월 4일 오후 09:09 GMT+9)
7 분 소요
원문: Dev.to

Source: Dev.to

When an Application Crashes in Production

프로덕션 환경에서 애플리케이션이 충돌하고 사용자는 다음에 무엇을 해야 할지 몰랐습니다.
작년에 제가 진행하던 프로젝트에서 이런 상황을 겪었습니다. 프로그래머인 저는 터미널로 달려가 오류를 확인하고 디버깅을 시작해야 했습니다.

즐거운 경험은 아니었지만, 선임 개발자가 catching and logging errors 를 소개해 주면서 상황이 달라졌습니다. 개념은 익숙했지만 실제로 적용해 본 적은 없었습니다. 적절한 오류 처리를 추가한 뒤로 제 프로그래밍 생활은 훨씬 매끄러워졌습니다.

Benefits of Logging and Catching Errors

  • Faster Debugging – 로그는 무엇이 일어났는지, 언제 일어났는지, 어디서 일어났는지, 그리고 누가 트리거했는지를 알려줍니다.
  • Better Error Visibility in Production – 프로덕션에서는 오류를 출력하거나 디버거를 붙일 수 없습니다. 로그는 원격 서버와 외부 클라이언트가 사용하는 API와 같이 중요한 프로덕션 환경을 들여다볼 수 있는 유일한 창입니다.
  • Graceful Failure (Better UX) – 오류를 잡아내면 의미 있는 HTTP 응답을 반환하고, 전체 애플리케이션이 충돌하는 것을 방지하며, 일부가 실패하더라도 서비스가 계속 실행될 수 있습니다.
  • Security and Auditing – 로그는 의심스러운 행동을 감지하고, 누가 무엇에 접근했는지 추적하며, 민감한 작업을 감사하는 데 도움이 됩니다.

Source:

실용적인 접근법: FastAPI에서 로깅 및 오류 처리

아래는 FastAPI 애플리케이션에서 로깅과 오류 처리를 설정하는 방법을 보여주는 최소한의 전자상거래 예시입니다.

폴더 구조

폴더 구조

schemas.py – Pydantic 모델 정의

from pydantic import BaseModel
from enum import Enum

class Status(str, Enum):
    AVAILABLE = "AVAILABLE"
    UNAVAILABLE = "UNAVAILABLE"

class ProductBase(BaseModel):
    name: str
    description: str
    price: float
    status: Status

class ProductCreate(ProductBase):
    pass

class ProductUpdate(BaseModel):
    name: str | None = None
    description: str | None = None
    price: float | None = None
    status: Status | None = None

class Product(ProductBase):
    id: int

main.py – FastAPI, 로깅 및 메모리 “데이터베이스” 설정

from fastapi import FastAPI, HTTPException, status, Response
import logging

from app.schemas import Product, ProductCreate, ProductUpdate

app = FastAPI()

# Configure logging – all logs go to app.log
logging.basicConfig(
    filename="app.log",
    level=logging.INFO,
    format="%(asctime)s - %(levelname)s - %(message)s",
)
logger = logging.getLogger(__name__)

# In‑memory list acting as a simple database
products: list[dict] = []

def fetch_product(product_id: int) -> dict | None:
    """Return a product dict matching the given id, or None if not found."""
    for product in products:
        if product["id"] == product_id:
            return product
    return None

엔드포인트 – 모든 제품 조회 및 단일 제품 조회

@app.get("/", response_model=list[Product])
def get_products():
    """Return the list of all products."""
    try:
        return products
    except Exception as e:
        logger.error(f"Unexpected error while getting products: {e}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="An unexpected error occurred while getting products",
        )

@app.get("/{product_id}", response_model=Product)
def get_product(product_id: int):
    """Return a single product by its id."""
    try:
        product = fetch_product(product_id)
        if not product:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND, detail="Product not found"
            )
        return product
    except Exception as e:
        logger.error(f"Unexpected error while getting product {product_id}: {e}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="An unexpected error occurred while getting the product",
        )

try/except 블록에 주목하세요:

  • 무언가 잘못될 경우(예: products가 정의되지 않은 경우) 예외가 로그에 기록되고 클라이언트는 원시 트레이스백 대신 일반적인 500 응답을 받게 됩니다.

예시: 제품 생성 (데모를 위한 의도적인 오류 포함)

@app.post("/", status_code=status.HTTP_201_CREATED, response_model=Product)
def create_product(product: ProductCreate):
    """Create a new product and add it to the in‑memory list."""
    try:
        # Uncomment the line below to simulate a coding error and see logging in action
        # kkk  # <-- intentional NameError for demonstration

        product_data = product.model_dump()
        product_data["id"] = len(products) + 1
        products.append(product_data)
        return product_data
    except Exception as e:
        logger.error(f"Unexpected error while creating product: {e}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="An unexpected error occurred while creating the product",
        )

구문 오류나 런타임 오류(예: kkk 라인)를 삽입하면,

예외가 포착되어 app.log에 기록되고, 클라이언트는 깔끔한 500 응답을 받게 됩니다.

애플리케이션 실행

  1. 가상 환경을 생성 (선택 사항이지만 권장됩니다).

  2. 의존성 설치:

    pip install fastapi uvicorn
  3. 서버 시작:

    uvicorn main:app --reload

    --reload 플래그는 개발 중 자동 재로드를 활성화합니다.

  4. 엔드포인트 테스트 (예: curl, HTTPie, Postman, 혹은 브라우저 사용).

  5. 로그 확인 – 예외가 발생할 때마다 기록된 메시지를 보려면 app.log 파일을 엽니다.

TL;DR

  • Log 오류에 대해 알아야 할 모든 것을 기록하세요.
  • Catch API 경계 수준에서 예외를 잡아 사용자 친화적인 응답을 반환하세요.
  • Never expose raw tracebacks 원시 트레이스백을 최종 사용자에게 절대 노출하지 마세요; 개발자를 위해 로그에 보관하세요.

이러한 관행을 구현하면 프로덕션 서비스가 더 신뢰성 있고, 디버깅이 쉬우며, 보안이 강화됩니다. 즐거운 코딩 되세요!

FastAPI에서 기본 로깅

제품을 생성하는 동안 오류가 발생하면 다음과 같은 메시지가 표시될 수 있습니다:

error occurred while creating the product')

로그 확인 방법

  1. create_product 엔드포인트를 실행합니다.
  2. 생성된 app.log 파일을 엽니다.
    파일은 발생하는 새로운 오류와 함께 자동으로 업데이트됩니다.

로그 파일

이것은 FastAPI에서의 기본 오류 로깅입니다.

프로젝트 저장소

기본 로깅 FastAPI Github

Back to Blog

관련 글

더 보기 »