FastAPI로 웹 애플리케이션을 개발할 때, 코드의 유지보수성과 확장성을 높이기 위해 레이어드 아키텍처(Layered Architecture)를 적용하는 것이 일반적이다.
레이어드 아키텍처는 기능별로 계층을 분리하여 각 계층이 독립적으로 동작하도록 설계하는 방식으로,
이를 통해 코드의 결합도를 낮추고, 가독성과 재사용성을 높일 수 있다.
또한, FastAPI의 의존성 주입(Dependency Injection, DI) 기능을 활용하면 레이어 간의 결합도를 줄이고 테스트가 용이한 구조를 만들 수 있다.
이를 위해 의존성 주입을 관리하는 dependency.py를 추가하여 객체 생성을 통합적으로 관리하도록 한다.
FastAPI에서 일반적으로 3계층 아키텍처로 나눈다.
폴더의 구성은 예시로 아래와같은 구조를 갖는다고 가정 후 설명하겠습니다.
📂 src/
├── 📂 book/ # Book 도메인 관련 코드
│ ├── models.py # (Entity Layer) 데이터베이스 모델 정의
│ ├── repository.py # (Persistence Layer) 데이터베이스와 직접 통신
│ ├── service.py # (Service Layer) 비즈니스 로직 처리
├── 📂 routers/ # (Presentation Layer) API 요청을 처리
│ ├── book.py # Book API 엔드포인트
├── 📂 dependencies/ # 의존성 주입을 관리
│ ├── dependency.py # DI 관련 메서드 정의
├── 📂 database/ # 데이터베이스 연결 및 세션 관리
│ ├── database.py # Async DB 세션 및 연결 관리
| 계층 | 역할 |
|---|---|
Presentation Layer (routers/book.py) | API 요청을 받아 Service Layer로 전달 |
Service Layer (src/book/service.py) | 비즈니스 로직을 처리하고 Repository Layer와 통신 |
Persistence Layer (src/book/repository.py) | 데이터베이스와 직접 연결되어 데이터를 저장 및 조회 |
Entity Layer (src/book/models.py) | 데이터베이스 테이블을 정의 |
✅ 각 계층은 독립적으로 동작하며, Service Layer는 Repository를 통해 데이터에 접근한다.
✅ FastAPI의 Depends()와 dependency.py를 활용하여, 객체 생성을 통합 관리할 수 있도록 한다.
✅ Session 관리를 위한 database.py를 추가하여, 라우터에서 직접 Session을 다루지 않도록 개선
FastAPI에서 레이어드 아키텍처를 적용한 디렉터리 구조는 다음과 같다.
fastapi_project/
│── webapp/
│ ├── app.py # FastAPI 애플리케이션 생성
│ ├── routers/
│ │ ├── book.py # API 라우터 (Presentation Layer)
│── src/
│ ├── book/
│ │ ├── models.py # 데이터베이스 모델 (Entity Layer)
│ │ ├── repository.py # Repository Layer
│ │ ├── service.py # Service Layer
│── dependencies/
│ ├── dependency.py # 의존성 관리 (Repository, Service)
│── database/
│ ├── database.py # Async DB 세션 관리
│── db.py # 데이터베이스 설정
│── main.py # FastAPI 실행 파일
models.pyfrom sqlalchemy import Column, Integer, String
from src.database.base import Base
class Book(Base):
__tablename__ = "books"
id = Column(Integer, primary_key=True, index=True)
title = Column(String, index=True)
author = Column(String, index=True)
published_year = Column(Integer)
repository.pyfrom sqlalchemy.ext.asyncio import AsyncSession
from src.book.models import Book
class BookRepository:
async def list_books(self, db: AsyncSession):
result = await db.execute("SELECT * FROM books")
return result.fetchall()
async def create_book(self, db: AsyncSession, title: str, author: str, published_year: int):
book = Book(title=title, author=author, published_year=published_year)
db.add(book)
await db.commit()
await db.refresh(book)
return book
service.pyfrom src.book.repository import BookRepository
from src.book.dto import BookCreateDTO, BookResponseDTO
from sqlalchemy.ext.asyncio import AsyncSession
class BookService:
def __init__(self, repository: BookRepository):
self.repository = repository
async def list_books(self, db: AsyncSession):
books = await self.repository.list_books(db)
return [BookResponseDTO.model_validate(book) for book in books]
async def create_book(self, db: AsyncSession, book_data: BookCreateDTO):
book = await self.repository.create_book(db, book_data.title, book_data.author, book_data.published_year)
return BookResponseDTO.model_validate(book)
dependency.pyfrom fastapi import Depends
from src.database.database import get_async_db
from src.book.repository import BookRepository
from src.book.service import BookService
def dependency_book_repository(db=Depends(get_async_db)) -> BookRepository:
return BookRepository()
def dependency_book_service(
repository: BookRepository = Depends(dependency_book_repository)
) -> BookService:
return BookService(repository)
✅ 모든 의존성을 dependency.py에서 관리하여 객체 생성을 통합적으로 관리
✅ 라우터에서는 Service만 주입받고, Repository 및 DB 관련 객체를 직접 다루지 않도록 함
database.pyfrom sqlalchemy.ext.asyncio import AsyncSession, create_async_engine, async_sessionmaker
DATABASE_URL = "sqlite+aiosqlite:///./test.db"
engine = create_async_engine(DATABASE_URL, echo=True)
SessionLocal = async_sessionmaker(autocommit=False, autoflush=False, bind=engine, class_=AsyncSession)
async def get_async_db():
async with SessionLocal() as session:
yield session
✅ AsyncSession을 사용하여 비동기 DB 관리
✅ 라우터에서는 직접 Session을 다루지 않고, dependency.py에서 관리
routers/book.pyfrom fastapi import APIRouter, Depends
from src.book.service import BookService
from src.book.dto import BookCreateDTO, BookResponseDTO
from typing import List
from dependencies.dependency import dependency_book_service
router = APIRouter(prefix="/books", tags=["Books"])
@router.get("/", response_model=List[BookResponseDTO])
async def list_books(
book_service: BookService = Depends(dependency_book_service)
):
return await book_service.list_books()
@router.post("/", response_model=BookResponseDTO)
async def create_book(
book_data: BookCreateDTO,
book_service: BookService = Depends(dependency_book_service)
):
return await book_service.create_book(book_data)
✅ 라우터에서는 Depends(dependency_book_service)를 활용하여 서비스만 주입받음
✅ Repository 및 DB 관련 의존성은 dependency.py에서 관리하여 직접 참조하지 않도록 개선
Depends()를 활용하여 객체 생성을 자동으로 관리할 수 있음 database.py 추가)로 인해 확장성이 뛰어남 FastAPI에서 레이어드 아키텍처 + Dependency Injection(DI) + Async DB 관리를 적용하면
보다 효율적이고 유지보수하기 쉬운 프로젝트를 개발할 수 있다.