REAL Python – FastAPI] – “풀스택 블로그 개발하기 – 프로젝트 구조 만들기”

REAL Python – FastAPI] – “풀스택 블로그 개발하기 – 프로젝트 구조 만들기”

12월 10, 2023

폴더 구조 작성하기

모든 것을 단일 파일에 넣고 실행해도 문제가 없는 FastAPI 의 특성상, “어떤 폴더 구조를 가질 것인가?”, “어떻게 프로젝트를 구성할 것인가?” 는 때때로 불타오르는 논쟁거리입니다. 아래에 제시할 구조는 제가 Flask 에서 경험한 것과 https://github.com/zhanymkanov/fastapi-best-practices#1-project-structure-consistent–predictable 를 참고한 것입니다. 개발을 진행하며, 개선할 점을 체크해둔 후 자신만의 Best Practice 를 만들어 보세요.

첫 번째 강좌에서 단 몇 줄의 코드만으로도 서버가 실행되는 모습에서 추측할 수 있듯이, FastAPI 는 개발자에게 프로젝트 구조를 구성하는 자유로움을 주는 프레임워크입니다. django 와는 확실히 다르죠. 이번 시간에는 우리 블로그를 위한 프로젝트를 어떻게 구성할 것인지에 대해 알아보고, 앞으로 코드를 작성하기 위한 폴더 구조를 만들어보겠습니다. 이전 시간까지 존재했던 app.py 는 과감하게 지워주세요.

alembic 세팅하기

alembic 은 우리의 ORM 코드로 데이터베이스를 관리하기 쉽게 만들어주는 유용한 도구입니다. 모델 코드에 변경이 생기거나, 데이터베이스의 변경을 되돌리고 싶을 때 유용하게 사용할 수 있죠. 먼저 poetry add alembic 으로 alembic 을 설치합니다.

성공적으로 설치했다면, alembic init -t async migrations 를 터미널에 입력해 설정 파일을 만듭니다.

아래처럼 migrations 폴더가 만들어지고, 루트 폴더 아래에 alembic.ini 파일이 만들어졌음을 확인해 보세요.

그리고, 밖에 생긴 alembic.ini 파일을 아래와 같이 수정합니다. 마이그레이션 파일명은 의미 있는 이름으로 짓는 것이 좋습니다.

file_template = %%(rev)s_%%(slug)s

이제 “어떤 DB 에 연결할 것인지, 어떤 SQLAlchemy 클래스를 참조할 것인지” 를 설정해야 하는데요, 값을 하드코딩하기보단 파이썬 파일로 관리해 봅시다. 루트 폴더 아래에 config 패키지 (루트 폴더 아래에 config 폴더를 만들고, 그 안에 __init__.py 를 만들어주세요.) 와 db.py, settings.py 를 만듭니다.

settings.py 안에는 아래처럼 db 접속과 관련된 내용을 작성합니다.

from pydantic_settings import BaseSettings


class Settings(BaseSettings):
    DB_NAME: str = "app.db"
    DB_URL: str = f"sqlite+aiosqlite:///{DB_NAME}"


settings = Settings()

db.py 안에는 db 연결과 관련된 코드를 작성해야 합니다.

from sqlalchemy import MetaData
from sqlalchemy.ext.asyncio import (
    create_async_engine,
    AsyncSession,
    async_sessionmaker,
)
from sqlalchemy.orm import declarative_base
from config.settings import settings


engine = create_async_engine(
    settings.DB_URL, echo=True, connect_args={"check_same_thread": False}
)


AsyncSessionLocal = async_sessionmaker(
    engine,
    autocommit=False,
    expire_on_commit=False,
    class_=AsyncSession,
)


naming_convention = {
    "ix": "%(column_0_label)s_idx",
    "uq": "%(table_name)s_%(column_0_name)s_key",
    "ck": "%(table_name)s_%(constraint_name)s_check",
    "fk": "%(table_name)s_%(column_0_name)s_fkey",
    "pk": "%(table_name)s_pkey",
}

Model = declarative_base(metadata=MetaData(naming_convention=naming_convention))

이제 필요한 metadata, db_urlmigrations/env.py 에 등록해줍니다. 꼭 settings.py, db.py 에서 가져오는 것을 잊지 말아주세요!

이제, alembic upgrade head 를 터미널에 입력해 문제가 없는지 확인해 보세요.

만약 greenlet 라이브러리가 없다면, poetry add greenlet 을 터미널에 입력하여 설치합니다.

설정 파일 관리로 앱 생성하기

우리가 개발하는 앱은 여러 환경에서 실행될 수 있습니다. 예컨대 현재 우리의 상황처럼, 앱을 개발하는 상황에서는 앱에서 에러가 생기면 그 에러에 대한 정보를 앱에서 보여줘야 하고, 실제 배포 상황이라면 에러의 상세 메시지를 노출하면 안 됩니다. 우리의 사이트를 호시탐탐 노리는 해커들에게 꼭 필요한 정보를 노출할 수 있기 때문입니다.

from functools import lru_cache

from dotenv import load_dotenv
from pydantic_settings import BaseSettings, SettingsConfigDict


class Settings(BaseSettings):
    # 환경 변수 설정
    model_config = SettingsConfigDict(env_file=".env")

    DB_URL: str


@lru_cache
def get_settings() -> Settings:
    load_dotenv()
    return Settings()


settings = get_settings()

먼저, 위처럼 settings.py 를 변경합니다. 위의 변경으로, .env 라는 파일로부터 환경 변수를 읽어온 다음, DB_URL 을 설정할 수 있습니다.

루트 폴더 아래에,.env 파일을 만든 후 아래와 같은 내용을 넣어 주세요.

DB_URL=sqlite+aiosqlite:///app.db

이제, settings.py 로 관리되는 새로운 앱을 만들기 위해서, 아래와 같이 비어있는 app.py 를 만듭니다.

settings.py 에 아래와 같은 내용을 추가한 다음,

from functools import lru_cache

from dotenv import load_dotenv
from pydantic_settings import BaseSettings, SettingsConfigDict


class Settings(BaseSettings):
    # 환경 변수 설정
    model_config = SettingsConfigDict(env_file=".env")

    TITLE: str = "Shovelog Backend API"
    DESCRIPTION: str = "Backend API for Shovelog."

    DB_URL: str
    ECHO_SQL: bool = False


@lru_cache
def get_settings() -> Settings:
    load_dotenv()
    return Settings()


settings = get_settings()

아래처럼 app.py 를 작성하면 우리의 API 문서에 제목과 간단한 설명을 추가할 수 있습니다.

from fastapi import FastAPI

from config.settings import settings


app = FastAPI(
    title=settings.TITLE,
    description=settings.DESCRIPTION,
)

users 패키지 생성하기

api 구현을 위해서, 루트 디렉토리 아래에 api 패키지를 만듭니다. 그 아래에 users/ 패키지를 만들고 아래의 빈 파일들을 만듭니다.

api/users
├── __init__.py 
├── models.py  # ORM 모델을 정의하는 곳
├── repositories.py  # 데이터 접근을 추상화하는 곳
├── routers.py  # 라우터를 정의하는 곳
├── schemas.py  # 직렬화 및 역직렬화를 위한 스키마를 정의하는 곳
└── services.py  # 비즈니스 로직이 구현되는 곳 

지금까지 구성한 하나의 주제에 대한 앱은 크게 위와 같은 구조를 따릅니다. 전체 구조는 아래와 같습니다!

shovelog-backend
├── README.md
├── alembic.ini
├── api
│   ├── __init__.py
│   └── users
│       ├── __init__.py
│       ├── models.py
│       ├── repositories.py
│       ├── routers.py
│       ├── schemas.py
│       └── services.py
├── app.db
├── app.py
├── config
│   ├── __init__.py
│   ├── db.py
│   └── settings.py
├── migrations
│   ├── README
│   ├── env.py
│   ├── script.py.mako
│   └── versions
├── poetry.lock
└── pyproject.toml

회원가입 및 로그인과 같은 회원 관련 코드들이 위치할 곳을 만들었으니, 다음에는 회원 관련 API 를 개발하겠습니다.

2 Comments

  1. 디에고 2024-09-27 at 7:53 오후 - Reply

    꼭 settings.py, db.py 에서 가져오는 것을 잊지 말아주세요!
    -> 이걸 추가하면 되는 건가요? 해결이 잘 안되네요.
    from config.db import Model
    from config.settings import settings

Leave A Comment

Avada Programmer

Hello! We are a group of skilled developers and programmers.

Hello! We are a group of skilled developers and programmers.

We have experience in working with different platforms, systems, and devices to create products that are compatible and accessible.