더 큰 애플리케이션 - 여러 파일

더 큰 애플리케이션 - 여러 파일 (Bigger Applications - Multiple Files)

애플리케이션이나 웹 API를 만들 때, 모든 걸 한 파일에 넣을 수 있는 경우는 거의 없어요.

FastAPI는 유연성을 모두 유지하면서 애플리케이션을 구조화할 수 있는 편리한 도구를 제공해요.

참고 — Flask에서 왔다면, 이건 Flask의 Blueprints에 해당하는 거예요.

출처: 공식문서

예시 파일 구조

이런 파일 구조를 갖고 있다고 해 볼게요:

.
├── app
│   ├── __init__.py
│   ├── main.py
│   ├── dependencies.py
│   └── routers
│   │   ├── __init__.py
│   │   ├── items.py
│   │   └── users.py
│   └── internal
│       ├── __init__.py
│       └── admin.py

__init__.py 파일이 여러 개 있어요. 각 디렉터리나 하위 디렉터리에 하나씩이죠. 이것이 한 파일에서 다른 파일로 코드를 임포트할 수 있게 해주는 거예요.

예를 들어 app/main.py에서 이런 줄을 쓸 수 있어요:

from app.routers import items
  • app 디렉터리는 모든 걸 담아요. 그리고 빈 파일 app/__init__.py가 있어서 "Python 패키지"(Python 모듈들의 모음)가 돼요: app.
  • app/main.py 파일이 있어요. Python 패키지(__init__.py 파일이 있는 디렉터리) 안에 있으므로 그 패키지의 "모듈"이에요: app.main.
  • app/dependencies.py 파일도 있어요. app/main.py처럼 이것도 "모듈"이에요: app.dependencies.
  • 하위 디렉터리 app/routers/에 또 다른 __init__.py 파일이 있어서, "Python 서브패키지"예요: app.routers.
  • 파일 app/routers/items.py는 패키지 app/routers/ 안에 있으므로 서브모듈이에요: app.routers.items.
  • app/routers/users.py도 마찬가지로 또 다른 서브모듈이에요: app.routers.users.
  • 하위 디렉터리 app/internal/에도 또 다른 __init__.py 파일이 있어서, 또 다른 "Python 서브패키지"예요: app.internal.
  • 파일 app/internal/admin.py는 또 다른 서브모듈이에요: app.internal.admin.

같은 파일 구조를 주석과 함께 보면:

.
├── app                  # "app" is a Python package
│   ├── __init__.py      # this file makes "app" a "Python package"
│   ├── main.py          # "main" module, e.g. import app.main
│   ├── dependencies.py  # "dependencies" module, e.g. import app.dependencies
│   └── routers          # "routers" is a "Python subpackage"
│   │   ├── __init__.py  # makes "routers" a "Python subpackage"
│   │   ├── items.py     # "items" submodule, e.g. import app.routers.items
│   │   └── users.py     # "users" submodule, e.g. import app.routers.users
│   └── internal         # "internal" is a "Python subpackage"
│       ├── __init__.py  # makes "internal" a "Python subpackage"
│       └── admin.py     # "admin" submodule, e.g. import app.internal.admin

APIRouter

사용자만 처리하는 전용 파일이 /app/routers/users.py 서브모듈이라고 해 볼게요.

사용자와 관련된 경로 연산을 나머지 코드와 분리해서, 정리된 상태를 유지하고 싶어요.

하지만 여전히 같은 FastAPI 애플리케이션/웹 API의 일부예요(같은 "Python 패키지"의 일부죠).

APIRouter를 써서 그 모듈의 경로 연산을 만들 수 있어요.

APIRouter 임포트하기

FastAPI 클래스를 쓸 때와 같은 방식으로 임포트하고 "인스턴스"를 만들어요:

app/routers/users.py:

from fastapi import APIRouter

router = APIRouter()

@router.get("/users/", tags=["users"])
async def read_users():
    return [{"username": "Rick"}, {"username": "Morty"}]

@router.get("/users/me", tags=["users"])
async def read_user_me():
    return {"username": "fakecurrentuser"}

@router.get("/users/{username}", tags=["users"])
async def read_user(username: str):
    return {"username": username}

APIRouter경로 연산 만들기

그리고 이걸 써서 경로 연산을 선언해요. FastAPI 클래스를 쓰는 것과 같은 방식으로 쓰면 돼요:

app/routers/users.py:

from fastapi import APIRouter

router = APIRouter()

@router.get("/users/", tags=["users"])
async def read_users():
    return [{"username": "Rick"}, {"username": "Morty"}]

@router.get("/users/me", tags=["users"])
async def read_user_me():
    return {"username": "fakecurrentuser"}

@router.get("/users/{username}", tags=["users"])
async def read_user(username: str):
    return {"username": username}

APIRouter를 "미니 FastAPI" 클래스라고 생각할 수 있어요.

모든 같은 옵션을 지원해요. 같은 parameters, responses, dependencies, tags 등 모두요.

— 이 예시에서는 변수 이름을 router로 했지만, 원하는 대로 이름을 지을 수 있어요.

APIRouter를 메인 FastAPI 앱에 포함시킬 거예요. 하지만 먼저 의존성과 또 다른 APIRouter를 확인해 볼게요.

의존성 (Dependencies)

애플리케이션의 여러 곳에서 쓰일 의존성들이 필요하다는 걸 알 수 있어요.

그래서 그걸 자체 dependencies 모듈(app/dependencies.py)에 넣어요.

이제 커스텀 X-Token 헤더를 읽는 간단한 의존성을 써 볼게요:

app/dependencies.py:

from typing import Annotated

from fastapi import Header, HTTPException

async def get_token_header(x_token: Annotated[str, Header()]):
    if x_token != "fake-super-secret-token":
        raise HTTPException(status_code=400, detail="X-Token header invalid")

async def get_query_token(token: str):
    if token != "jessica":
        raise HTTPException(status_code=400, detail="No Jessica token provided")

— 예시를 단순화하기 위해 지어낸 헤더를 쓰고 있어요. 실제 상황에서는 내장된 보안 유틸리티를 쓰면 더 나은 결과를 얻어요.

APIRouter가 있는 또 다른 모듈

애플리케이션의 "items"를 처리하는 엔드포인트 전용 모듈도 app/routers/items.py에 있다고 해 볼게요.

다음을 위한 경로 연산이 있어요:

  • /items/
  • /items/{item_id}

app/routers/users.py와 같은 구조예요.

하지만 좀 더 똑똑하게 코드를 단순화하고 싶어요.

이 모듈의 모든 경로 연산이 같은 것들을 공유한다는 걸 알고 있어요:

  • 경로 prefix: /items.
  • tags: (태그 하나: items).
  • 추가 responses.
  • dependencies: 모두 우리가 만든 그 X-Token 의존성이 필요해요.

그래서 모든 경로 연산에 그걸 추가하는 대신 APIRouter에 추가할 수 있어요.

app/routers/items.py:

from fastapi import APIRouter, Depends, HTTPException

from ..dependencies import get_token_header

router = APIRouter(
    prefix="/items",
    tags=["items"],
    dependencies=[Depends(get_token_header)],
    responses={404: {"description": "Not found"}},
)

fake_items_db = {"plumbus": {"name": "Plumbus"}, "gun": {"name": "Portal Gun"}}

@router.get("/")
async def read_items():
    return fake_items_db

@router.get("/{item_id}")
async def read_item(item_id: str):
    if item_id not in fake_items_db:
        raise HTTPException(status_code=404, detail="Item not found")
    return {"name": fake_items_db[item_id]["name"], "item_id": item_id}

@router.put(
    "/{item_id}",
    tags=["custom"],
    responses={403: {"description": "Operation forbidden"}},
)
async def update_item(item_id: str):
    if item_id != "plumbus":
        raise HTTPException(
            status_code=403, detail="You can only update the item: plumbus"
        )
    return {"item_id": item_id, "name": "The great Plumbus"}

경로 연산의 경로가 /로 시작해야 하므로, 이렇게요:

@router.get("/{item_id}")
async def read_item(item_id: str):
    ...

...prefix는 마지막에 /를 포함하면 안 돼요.

그래서 이 경우 prefix는 /items예요.

이 라우터에 포함된 모든 경로 연산에 적용될 tags 목록과 추가 responses도 더할 수 있어요.

그리고 라우터의 모든 경로 연산에 추가되어, 각 요청마다 실행/해결될 dependencies 목록도 추가할 수 있어요.

경로 연산 데코레이터의 의존성과 비슷하게, 경로 연산 함수에 값이 전달되지 않는다는 점을 주목하세요.

최종 결과로 item 경로들은 이렇게 돼요:

  • /items/
  • /items/{item_id}

...우리가 의도한 대로예요.

  • 문자열 하나 "items"를 포함한 태그 목록으로 표시돼요.
    • 이 "tags"는 (OpenAPI를 쓰는) 자동 대화형 문서 시스템에 특히 유용해요.
  • 모두 미리 정의된 responses를 포함해요.
  • 이 모든 경로 연산은 그것들 앞에 dependencies 목록이 평가/실행돼요.

APIRouterdependencies를 두는 건, 예를 들어 경로 연산 전체 그룹에 인증을 요구하는 데 쓸 수 있어요. 각각에 개별적으로 추가하지 않아도요.

prefix, tags, responses, dependencies 파라미터는 (다른 많은 경우처럼) 코드 중복을 피하도록 도와주는 FastAPI의 기능일 뿐이에요.

의존성 임포트하기

이 코드는 모듈 app.routers.items, 파일 app/routers/items.py에 있어요.

그리고 의존성 함수는 모듈 app.dependencies, 파일 app/dependencies.py에서 가져와야 해요.

그래서 의존성에 ..를 쓰는 상대 임포트(relative import)를 사용해요:

app/routers/items.py:

from fastapi import APIRouter, Depends, HTTPException

from ..dependencies import get_token_header

router = APIRouter(
    prefix="/items",
    tags=["items"],
    dependencies=[Depends(get_token_header)],
    responses={404: {"description": "Not found"}},
)

fake_items_db = {"plumbus": {"name": "Plumbus"}, "gun": {"name": "Portal Gun"}}

@router.get("/")
async def read_items():
    return fake_items_db

@router.get("/{item_id}")
async def read_item(item_id: str):
    if item_id not in fake_items_db:
        raise HTTPException(status_code=404, detail="Item not found")
    return {"name": fake_items_db[item_id]["name"], "item_id": item_id}

@router.put(
    "/{item_id}",
    tags=["custom"],
    responses={403: {"description": "Operation forbidden"}},
)
async def update_item(item_id: str):
    if item_id != "plumbus":
        raise HTTPException(
            status_code=403, detail="You can only update the item: plumbus"
        )
    return {"item_id": item_id, "name": "The great Plumbus"}

상대 임포트가 어떻게 동작하는지

— 임포트가 어떻게 동작하는지 완벽히 안다면 아래 섹션으로 넘어가세요.

점 하나 .은 이렇게요:

from .dependencies import get_token_header

이걸 의미해요:

  • 이 모듈(파일 app/routers/items.py)이 살고 있는 같은 패키지(디렉터리 app/routers/)에서 시작해서...
  • 모듈 dependencies(가상의 파일 app/routers/dependencies.py)를 찾고...
  • 그 모듈에서 함수 get_token_header를 임포트해요.

하지만 그 파일은 없어요. 우리 의존성은 app/dependencies.py에 있어요.

우리의 앱/파일 구조가 어떻게 생겼는지 기억하세요.


점 두 개 ..은 이렇게요:

from ..dependencies import get_token_header

이걸 의미해요:

  • 이 모듈(파일 app/routers/items.py)이 살고 있는 같은 패키지(디렉터리 app/routers/)에서 시작해서...
  • 부모 패키지(디렉터리 app/)로 가고...
  • 거기서 모듈 dependencies(파일 app/dependencies.py)를 찾고...
  • 그 모듈에서 함수 get_token_header를 임포트해요.

그게 올바르게 동작해요! 🎉


같은 방식으로, 점 세 개 ...을 썼다면 이렇게요:

from ...dependencies import get_token_header

이걸 의미해요:

  • 이 모듈(파일 app/routers/items.py)이 살고 있는 같은 패키지(디렉터리 app/routers/)에서 시작해서...
  • 부모 패키지(디렉터리 app/)로 가고...
  • 그 패키지의 부모로 갑니다(부모 패키지가 없어요. app이 최상위입니다 😱)...
  • 그리고 거기서 모듈 dependencies(파일 app/dependencies.py)를 찾고...
  • 그 모듈에서 함수 get_token_header를 임포트해요.

그건 app/ 위의 어떤 패키지(자체 __init__.py 파일을 가진)를 가리킬 거예요. 하지만 우리는 그런 게 없어요. 그래서 이 예시에서는 에러가 날 거예요. 🚨

하지만 이제 어떻게 동작하는지 알게 됐으니, 아무리 복잡한 앱이라도 상대 임포트를 쓸 수 있어요. 🤓

커스텀 tags, responses, dependencies 추가하기

경로 연산에 prefix /itemstags=["items"]를 추가하지 않았어요. APIRouter에 그걸 추가했으니까요.

하지만 특정 경로 연산에 적용될 tags 추가할 수도 있고, 그 경로 연산에 특화된 responses도 추가할 수 있어요:

app/routers/items.py:

from fastapi import APIRouter, Depends, HTTPException

from ..dependencies import get_token_header

router = APIRouter(
    prefix="/items",
    tags=["items"],
    dependencies=[Depends(get_token_header)],
    responses={404: {"description": "Not found"}},
)

fake_items_db = {"plumbus": {"name": "Plumbus"}, "gun": {"name": "Portal Gun"}}

@router.get("/")
async def read_items():
    return fake_items_db

@router.get("/{item_id}")
async def read_item(item_id: str):
    if item_id not in fake_items_db:
        raise HTTPException(status_code=404, detail="Item not found")
    return {"name": fake_items_db[item_id]["name"], "item_id": item_id}

@router.put(
    "/{item_id}",
    tags=["custom"],
    responses={403: {"description": "Operation forbidden"}},
)
async def update_item(item_id: str):
    if item_id != "plumbus":
        raise HTTPException(
            status_code=403, detail="You can only update the item: plumbus"
        )
    return {"item_id": item_id, "name": "The great Plumbus"}

— 이 마지막 경로 연산은 태그 조합 ["items", "custom"]을 갖게 돼요.

그리고 문서에 404용 responses와 403용 responses가 둘 다 생겨요.

메인 FastAPI

이제 app/main.py의 모듈을 볼게요.

여기가 FastAPI 클래스를 임포트하고 쓰는 곳이에요.

이건 여러분 애플리케이션에서 모든 걸 묶어 주는 메인 파일이 될 거예요.

그리고 대부분의 로직이 이제 각자의 전용 모듈에 살게 되므로, 메인 파일은 꽤 단순해질 거예요.

FastAPI 임포트하기

평소처럼 FastAPI 클래스를 임포트하고 만들어요.

그리고 각 APIRouter의 의존성과 결합될 전역 의존성을 선언할 수도 있어요:

app/main.py:

from fastapi import Depends, FastAPI

from .dependencies import get_query_token, get_token_header
from .internal import admin
from .routers import items, users

app = FastAPI(dependencies=[Depends(get_query_token)])

app.include_router(users.router)
app.include_router(items.router)
app.include_router(
    admin.router,
    prefix="/admin",
    tags=["admin"],
    dependencies=[Depends(get_token_header)],
    responses={418: {"description": "I'm a teapot"}},
)

@app.get("/")
async def root():
    return {"message": "Hello Bigger Applications!"}

APIRouter 임포트하기

이제 APIRouter를 가진 다른 서브모듈들을 임포트해요:

app/main.py:

from fastapi import Depends, FastAPI

from .dependencies import get_query_token, get_token_header
from .internal import admin
from .routers import items, users

app = FastAPI(dependencies=[Depends(get_query_token)])

app.include_router(users.router)
app.include_router(items.router)
app.include_router(
    admin.router,
    prefix="/admin",
    tags=["admin"],
    dependencies=[Depends(get_token_header)],
    responses={418: {"description": "I'm a teapot"}},
)

@app.get("/")
async def root():
    return {"message": "Hello Bigger Applications!"}

파일 app/routers/users.pyapp/routers/items.py가 같은 Python 패키지 app의 일부인 서브모듈이므로, 단일 점 .을 써서 "상대 임포트"로 임포트할 수 있어요.

임포트가 어떻게 동작하는지

이 섹션:

from .routers import items, users

이걸 의미해요:

  • 이 모듈(파일 app/main.py)이 살고 있는 같은 패키지(디렉터리 app/)에서 시작해서...
  • 서브패키지 routers(디렉터리 app/routers/)를 찾고...
  • 그 패키지에서 서브모듈 items(파일 app/routers/items.py)와 users(파일 app/routers/users.py)를 임포트해요...

모듈 items는 변수 router(items.router)를 가질 거예요. 이건 파일 app/routers/items.py에서 만든 그거예요. APIRouter 객체죠.

그리고 모듈 users에 대해서도 똑같이 해요.

이렇게도 임포트할 수 있어요:

from app.routers import items, users

참고 — 첫 번째 버전은 "상대 임포트"예요:

from .routers import items, users

두 번째 버전은 "절대 임포트"예요:

from app.routers import items, users

Python 패키지와 모듈에 대해 더 배우려면 Python 공식 문서의 Modules를 읽어 보세요.

이름 충돌 피하기

서브모듈 items를, 단지 그 변수 router만 임포트하는 대신 직접 임포트하고 있어요.

이건 서브모듈 users에도 router라는 또 다른 변수가 있기 때문이에요.

하나씩 순서대로 임포트했다면 이렇게요:

from .routers.items import router
from .routers.users import router

usersrouteritems의 것을 덮어쓰고, 동시에 둘을 쓸 수 없게 돼요.

그래서 같은 파일에서 둘 다 쓰려면 서브모듈을 직접 임포트해요:

app/main.py:

from fastapi import Depends, FastAPI

from .dependencies import get_query_token, get_token_header
from .internal import admin
from .routers import items, users

app = FastAPI(dependencies=[Depends(get_query_token)])

app.include_router(users.router)
app.include_router(items.router)
app.include_router(
    admin.router,
    prefix="/admin",
    tags=["admin"],
    dependencies=[Depends(get_token_header)],
    responses={418: {"description": "I'm a teapot"}},
)

@app.get("/")
async def root():
    return {"message": "Hello Bigger Applications!"}

usersitemsAPIRouter 포함하기

이제 서브모듈 usersitemsrouter들을 포함해요:

app/main.py:

from fastapi import Depends, FastAPI

from .dependencies import get_query_token, get_token_header
from .internal import admin
from .routers import items, users

app = FastAPI(dependencies=[Depends(get_query_token)])

app.include_router(users.router)
app.include_router(items.router)
app.include_router(
    admin.router,
    prefix="/admin",
    tags=["admin"],
    dependencies=[Depends(get_token_header)],
    responses={418: {"description": "I'm a teapot"}},
)

@app.get("/")
async def root():
    return {"message": "Hello Bigger Applications!"}

참고users.router는 파일 app/routers/users.py 안의 APIRouter를 담고 있어요.

그리고 items.router는 파일 app/routers/items.py 안의 APIRouter를 담고 있어요.

app.include_router()로 각 APIRouter를 메인 FastAPI 애플리케이션에 추가할 수 있어요. 그 라우터의 모든 라우트를 그것의 일부로 포함해요.

기술적 세부사항 — 라우터가 메인 애플리케이션에 포함될 때 FastAPI는 원래 APIRouter와 그 APIRoute들을 활성 상태로 유지해요. 즉 커스텀 APIRouterAPIRoute 서브클래스가 라우터 포함 후에도 여전히 참여할 수 있다는 뜻이에요.

— 라우터를 포함할 때 성능을 걱정할 필요 없어요. 이건 가볍게 설계됐고, 각 요청에 오버헤드를 추가하지 않도록 설계됐어요. 그래서 성능에 영향을 주지 않아요. ⚡

커스텀 prefix, tags, responses, dependenciesAPIRouter 포함하기

이제 조직이 여러분에게 app/internal/admin.py 파일을 줬다고 상상해 볼게요.

조직이 여러 프로젝트에서 공유하는 몇몇 admin 경로 연산을 가진 APIRouter를 담고 있어요.

이 예시에서는 매우 단순할 거예요. 하지만 조직의 다른 프로젝트와 공유되기 때문에, 그 APIRouter에 직접 prefix, dependencies, tags 등을 추가해서 수정할 수 없다고 해 볼게요:

app/internal/admin.py:

from fastapi import APIRouter

router = APIRouter()

@router.post("/")
async def update_admin():
    return {"message": "Admin getting schwifty"}

하지만 여전히 APIRouter를 포함할 때 커스텀 prefix를 설정해서 모든 경로 연산/admin으로 시작하게 하고 싶고, 이 프로젝트를 위해 이미 갖고 있는 dependencies로 보호하고 싶으며, tagsresponses를 포함하고 싶어요.

원래 APIRouter를 수정하지 않고도 이 모든 걸 선언할 수 있어요. 그 파라미터들을 app.include_router()에 넘기면 되죠:

app/main.py:

from fastapi import Depends, FastAPI

from .dependencies import get_query_token, get_token_header
from .internal import admin
from .routers import items, users

app = FastAPI(dependencies=[Depends(get_query_token)])

app.include_router(users.router)
app.include_router(items.router)
app.include_router(
    admin.router,
    prefix="/admin",
    tags=["admin"],
    dependencies=[Depends(get_token_header)],
    responses={418: {"description": "I'm a teapot"}},
)

@app.get("/")
async def root():
    return {"message": "Hello Bigger Applications!"}

그렇게 하면 원래 APIRouter는 수정되지 않은 채 남아서, 같은 app/internal/admin.py 파일을 조직의 다른 프로젝트와 계속 공유할 수 있어요.

그 결과 우리 앱에서 admin 모듈의 각 경로 연산은 이걸 갖게 돼요:

  • prefix /admin.
  • 태그 admin.
  • 의존성 get_token_header.
  • 응답 418. 🍵

하지만 그건 우리 앱의 그 APIRouter에만 영향을 주고, 그걸 쓰는 다른 코드에는 영향을 주지 않아요.

예를 들어 다른 프로젝트는 다른 인증 방법으로 같은 APIRouter를 쓸 수 있어요.

경로 연산 포함하기

FastAPI 앱에 경로 연산을 직접 추가할 수도 있어요.

여기서는... 할 수 있다는 걸 보여주려고 해요 🤷:

app/main.py:

from fastapi import Depends, FastAPI

from .dependencies import get_query_token, get_token_header
from .internal import admin
from .routers import items, users

app = FastAPI(dependencies=[Depends(get_query_token)])

app.include_router(users.router)
app.include_router(items.router)
app.include_router(
    admin.router,
    prefix="/admin",
    tags=["admin"],
    dependencies=[Depends(get_token_header)],
    responses={418: {"description": "I'm a teapot"}},
)

@app.get("/")
async def root():
    return {"message": "Hello Bigger Applications!"}

그리고 app.include_router()로 추가된 다른 모든 경로 연산과 함께 올바르게 동작해요.

매우 기술적인 세부사항 — 참고: 이것은 아마 건너뛸 수 있는 매우 기술적인 세부사항이에요.


APIRouter들은 "마운트"되지 않아요. 애플리케이션의 나머지와 격리되지 않죠.

왜냐하면 우리는 그 경로 연산을 OpenAPI 스키마와 사용자 인터페이스에 포함시키고 싶기 때문이에요.

FastAPI는 원래 라우터와 경로 연산을 활성 상태로 유지하고, 요청을 처리하고 OpenAPI를 생성할 때 라우터의 prefix, 의존성, 태그, 응답 및 기타 메타데이터를 결합해요.

pyproject.toml에서 entrypoint 설정하기

여러분의 FastAPI app 객체가 app/main.py에 있으므로, pyproject.toml 파일에서 entrypoint를 이렇게 설정할 수 있어요:

[tool.fastapi]
entrypoint = "app.main:app"

이건 다음과 같이 임포트하는 것과 같아요:

from app.main import app

그렇게 하면 fastapi 명령이 여러분 앱을 어디서 찾을지 알 수 있어요.

참고 — 명령에 경로를 넘길 수도 있어요, 이렇게요:

$ uv run fastapi dev app/main.py

하지만 fastapi 명령을 호출할 때마다 올바른 경로를 기억해서 넘겨야 해요.

게다가 VS Code 확장이나 FastAPI Cloud 같은 다른 도구는 그것을 찾지 못할 수도 있으므로, pyproject.tomlentrypoint를 쓰는 걸 권장해요.

자동 API 문서 확인하기

이제 앱을 실행해요:

uv run fastapi dev

그리고 http://127.0.0.1:8000/docs에서 문서를 열어요.

모든 서브모듈의 경로를 올바른 경로(와 prefix)와 올바른 태그로 포함한 자동 API 문서를 볼 수 있어요.

같은 라우터를 다른 prefix로 여러 번 포함하기

.include_router()같은 라우터로 다른 prefix를 쓰며 여러 번 쓸 수도 있어요.

예를 들어 같은 API를 다른 prefix로 노출하는 데 유용해요. /api/v1/api/latest처럼요.

정말 필요하지 않을 수도 있는 고급 사용법이지만, 필요할 때를 대비해 있어요.

APIRouter를 다른 것 안에 포함하기

FastAPI 애플리케이션에 APIRouter를 포함할 수 있는 것과 같은 방식으로, 이렇게 써서 다른 APIRouterAPIRouter를 포함할 수 있어요:

router.include_router(other_router)

routerFastAPI 앱에 포함하기 전에나 후에 이걸 할 수 있어요. FastAPI는 여전히 other_router경로 연산을 라우팅과 OpenAPI에 포함해요.

나중에 라우터에 추가되는 경로 연산에도 마찬가지예요. 그것들은 앞선 포함을 통해서도 보여요.

기술적 세부사항 — 라우터를 포함한 뒤 router.routes를 직접 변경하는 건 피하세요. FastAPI는 라우터 포함을 "실시간(live)"으로 취급하므로, 원래 라우터와 그 라우트가 라우팅과 OpenAPI 생성의 일부로 남아요. 라우트와 라우터를 추가하려면 경로 연산 데코레이터나 .include_router() 같은 문서화된 API를 쓰세요. router.routes를 라우트 정의와 포함된 라우터를 담을 수 있는 저수준 라우트 트리로 취급하고, 그것을 최종 경로 연산의 평평한 목록으로 의존하지 마세요.

더 알아보기 (Learn more)