REAL Python – FastAPI] – “풀스택 블로그 개발하기 – 프로젝트 구조 만들기”
REAL Python – FastAPI] – “풀스택 블로그 개발하기 – 프로젝트 구조 만들기”
폴더 구조 작성하기
모든 것을 단일 파일에 넣고 실행해도 문제가 없는
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_url
을 migrations/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 를 개발하겠습니다.
꼭 settings.py, db.py 에서 가져오는 것을 잊지 말아주세요!
-> 이걸 추가하면 되는 건가요? 해결이 잘 안되네요.
from config.db import Model
from config.settings import settings
안녕하세요. 아래에 코드의 원본을 첨부해 드립니다!
https://github.com/TGoddessana/shovelog-backend/blob/601e625d4ab229736a39280ecdec8480a4bd6174/migrations/env.py#L10
말씀하신 대로 import 를 추가하는 것이 맞는데, 혹시 문제가 있으시면 에러 코드나 문제 상황을 설명해 주시겠어요??