티스토리 뷰


안녕하세요! 오늘은 파이썬 백엔드 프레임워크의 혁명, FastAPI를 지탱하는 두 개의 기둥인 **어노테이션(Annotation)**과 **의존성 주입(Dependency Injection)**에 대해 알아보겠습니다.
이 두 기능을 제대로 이해하면 코드의 양은 줄어들고, 안정성은 비약적으로 상승합니다.
1. 어노테이션(Annotation): 단순한 주석 그 이상
파이썬의 타입 힌트(Type Hint)를 활용하는 어노테이션은 FastAPI에서 단순한 '표시'가 아닙니다. 이는 데이터 검증기이자 문서 생성기입니다.
✅ 주요 기능
• 데이터 검증: 설정한 타입과 다른 데이터가 들어오면 즉시 에러 응답을 보냅니다.
• 자동 변환: URL로 들어온 문자열 "10"을 함수 내부에서 정수 10으로 자동 변환합니다.
• Swagger 문서화: 코드를 작성함과 동시에 API 문서가 실시간으로 업데이트됩니다.

from pydantic import BaseModel

class Item(BaseModel):
    name: str
    price: float  # 실수형 검증

@app.post("/items/")
async def create_item(item: Item): # 'item'은 Item 모델의 형식을 따라야 함
    return item

의존성 주입(DI): 코드의 결합을 풀다
의존성 주입은 특정 함수가 실행될 때 필요한 기능을 외부에서 '주입'해주는 방식입니다.
🏗️ 왜 DI를 쓰나요?
• 중복 제거 (DRY): 인증, DB 연결 등 반복되는 로직을 한 번만 정의하고 재사용합니다.
• 테스트 용이성: 실제 DB 대신 테스트용 가짜(Mock) DB를 주입하기 쉽습니다.
• 관심사 분리: API 엔드포인트는 비즈니스 로직에만 집중하게 됩니다.

3. 현대적인 방식: Annotated의 등장
Python 3.9+부터 권장되는 Annotated는 타입 힌트와 FastAPI의 기능을 하나로 묶어주는 혁신적인 도구입니다.

from typing import Annotated
from fastapi import Depends, Header

# 1. 의존성 정의: 헤더에서 토큰을 검증하는 로직
async def verify_token(x_token: Annotated[str, Header()]):
    if x_token != "super-secret":
        raise HTTPException(status_code=400, detail="Invalid Token")
    return x_token

# 2. 의존성을 하나의 타입처럼 정의 (Alias)
ValidToken = Annotated[str, Depends(verify_token)]

# 3. 실제 API에서 사용: 매우 깔끔해집니다!
@app.get("/items/")
async def read_items(token: ValidToken):
    return {"message": "인증 성공!", "token": token}

Annotated는 단순히 FastAPI의 기능을 넘어, 현대 파이썬(3.9+)에서 타입 시스템을 확장하는 표준 방식입니다. 왜 이 방식이 "대세"가 되었는지, 그리고 실무에서 어떤 파워를 발휘하는지 심층적으로 파헤쳐 보겠습니다.
1. Annotated의 본질: "타입에 포스트잇 붙이기"
Annotated는 PEP 593에서 도입되었습니다. 기본 구조는 Annotated[T, x]인데, 여기서 T는 실제 데이터 타입이고, x는 그 타입에 추가하는 **메타데이터(설명서)**입니다.
• 파이썬 입장: "음, 이건 T 타입이군. 뒤에 붙은 x는 무시할게."
• FastAPI 입장: "오, T 타입이구나? 그런데 뒤에 x라는 설정이 있네? 이걸로 검증이나 의존성 주입을 처리해야지!"
이처럼 런타임 로직과 타입 선언을 분리할 수 있다는 점이 핵심입니다.
2. 왜 기존 방식보다 Annotated가 좋을까?
기존 방식은 함수 매개변수에 직접 Query(), Depends() 등을 할당했습니다. 하지만 Annotated를 쓰면 다음과 같은 이점이 생깁니다.
🛠️ 이점 1: 코드 재사용성 (DRY)
똑같은 검증 로직이나 의존성을 여러 곳에서 써야 할 때, 아예 새로운 타입처럼 정의할 수 있습니다.

from typing import Annotated
from fastapi import Query

# 반복되는 검색 쿼리 설정을 하나의 '타입'으로 정의
SearchQuery = Annotated[
    str,
    Query(min_length=2, max_length=20, description="검색어는 2~20자 사이")
]

@app.get("/items/")
async def read_items(q: SearchQuery = None): # 훨씬 간결해짐!
    return {"q": q}

@app.get("/users/")
async def read_users(q: SearchQuery = None): # 다른 곳에서도 그대로 사용
    return {"q": q}

🧪 이점 2: 테스트 및 정적 분석 (Linting)
기존 방식(q: str = Query(...))은 기본값 자리에 Query 객체가 들어가 있어서, 단위 테스트를 할 때 실제 문자열을 넘기기 까다로울 수 있습니다. Annotated를 쓰면 타입 힌트가 명확해져서 mypy 같은 도구가 에러를 더 잘 잡아냅니다.

결합
여러 의존성을 조합해서 사용할 때 Annotated는 빛을 발합니다.

from typing import Annotated
from fastapi import Depends, Header, HTTPException

# 1. 헤더에서 API 키 추출
APIKey = Annotated[str, Header(alias="X-API-KEY")]

# 2. 유저 권한 확인 의존성
def get_current_user(api_key: APIKey):
    if api_key != "secret":
        raise HTTPException(status_code=403)
    return {"user": "admin"}

# 3. 최종적으로 '인증된 유저' 타입을 정의
CurrentUser = Annotated[dict, Depends(get_current_user)]

@app.get("/admin")
async def admin_panel(user: CurrentUser): # 단 한 줄로 모든 검증 끝!
    return user

정리하자면: Annotated는 코드를 모듈화하고 문서화하는 데 있어 가장 강력한 도구입니다. 처음에는 조금 길어 보일 수 있지만, 프로젝트 규모가 커질수록 이 방식이 주는 안정성은 비교할 수 없습니다.

댓글
D-DAY
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2026/01   »
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
글 보관함