FastAPI + React로 회원관리 시스템 구축 (MySQL 연동)
MySQL 설치
아래 링크를 참고하여 MySQL과 Workbench를 설치합니다.
https://myanjini.tistory.com/entry/MySQL-8034-MySQL-Workbench-80-설치
스키마 생성
MySQL Workbench에서 fastapiuser
사용자로 접속한 상태에서 아래 쿼리를 실행합니다.
CREATE SCHEMA `fastapidb` DEFAULT CHARACTER SET utf8;
실행이 완료되면 왼쪽 SCHEMAS 목록에서 fastapidb
가 생성된 것을 확인할 수 있습니다.
MySQL 연동 설정 (database/connection.py
)
# 데이터베이스 설정
from sqlmodel import SQLModel, create_engine, Session
# MySQL + PyMySQL 연결 문자열 (@는 %40으로 인코딩)
database_connection_string = "mysql+pymysql://fastapiuser:p%40ssw0rd@localhost:3306/fastapidb"
engine_url = create_engine(
database_connection_string,
echo=True # SQL 쿼리 로그 확인용
)
# 테이블 생성 함수
def conn():
SQLModel.metadata.create_all(engine_url)
# 세션 제공 함수
def get_session():
with Session(engine_url) as session:
yield session
데이터베이스 연결 정보를 별도 파일(.env)에서 읽어오도록 수정
프로젝트 설정 정보를 코드에 직접 작성하는 대신, .env
파일을 통해 외부에서 관리하도록 수정할 수 있습니다.
이를 통해 보안성과 유지보수성이 향상되며, 실무에서도 가장 권장되는 방식입니다.
pydantic-settings 설치
pip install pydantic-settings
pip freeze > requirements.txt
.env 파일 생성 (루트 경로)
# .env 파일 내용
DATABASE_URL=mysql+pymysql://fastapiuser:p%40ssw0rd@localhost:3306/fastapidb
database/connection.py 코드 수정
from typing import Optional
from pydantic_settings import BaseSettings
from sqlmodel import SQLModel, create_engine, Session
# .env에서 설정값 로드
class Settings(BaseSettings):
DATABASE_URL: Optional[str] = None
class Config:
env_file = ".env" # 오타 주의: evn_file ❌
settings = Settings()
# 연결 문자열은 .env에서 불러온 값으로 대체
engine_url = create_engine(
settings.DATABASE_URL,
echo=True,
)
# 테이블 생성 함수
def conn():
SQLModel.metadata.create_all(engine_url)
# 세션 제공 함수
def get_session():
with Session(engine_url) as session:
yield session
settings.DATABASE_URL
이 None일 경우를 대비해 중간에 아래와 같은 디버깅 코드를 추가하는 것도 좋습니다.
print("📌 현재 DATABASE_URL =", settings.DATABASE_URL)
환경 파일(.env)을 사용하면 실제 배포 환경이나 로컬 환경에서 서로 다른 DB 연결 정보를 유연하게 적용할 수 있습니다.
순환 참조 방지 (TYPE_CHECKING
사용)
models.users
와 models.events
처럼 서로를 참조하는 구조에서는 순환 참조가 발생할 수 있습니다.
이를 방지하기 위해 TYPE_CHECKING
을 사용하여 타입 힌트만 인식되도록 처리합니다.
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from models.events import Event # 또는 반대로 from models.users import User
📁 models/events.py
from typing import TYPE_CHECKING, List, Optional
from sqlmodel import SQLModel, Field, Relationship
if TYPE_CHECKING:
from models.users import User
class Event(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
title: str
image: str
description: str
tags: Optional[str] # JSON 대신 str로 저장
location: str
user_id: Optional[int] = Field(default=None, foreign_key="user.id")
user: Optional["User"] = Relationship(back_populates="events")
📁 models/users.py
from typing import TYPE_CHECKING, List, Optional
from pydantic import EmailStr
from sqlmodel import SQLModel, Field, Relationship
if TYPE_CHECKING:
from models.events import Event
class User(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
email: EmailStr
password: str
username: str
events: List["Event"] = Relationship(back_populates="user")
관계 설정: Relationship(back_populates)
두 모델 간의 관계를 정의할 때 사용하는 Relationship()
은 실제 DB 컬럼을 생성하지 않으며, ORM 객체 간의 연결을 위한 속성입니다.
양쪽에서 back_populates
이름을 서로 일치시켜야 관계가 올바르게 작동합니다.
# models/users.py
events: List["Event"] = Relationship(back_populates="user")
# models/events.py
user: Optional["User"] = Relationship(back_populates="events")
※ 이 필드는 실제 테이블에는 존재하지 않으며, 파이썬 코드 상에서만 작동합니다.
SQLModel의 Relationship과 Optional 필드 동작
FastAPI + SQLModel 환경에서 자주 마주치는 개념 두 가지를 정리합니다.
Relationship
은 테이블 컬럼이 아닌 객체 간 연결용 필드Optional
필드는 테이블에 생성되지만 NULL 허용
① Relationship은 실제 컬럼이 아니다
class User(SQLModel, table=True):
events: List["Event"] = Relationship(back_populates="user")
위 코드는 객체 간 연결을 위한 설정이며, 실제 DB에는 events
컬럼이 존재하지 않습니다.
② 외래키는 관계의 반대쪽에서 정의
class Event(SQLModel, table=True):
user_id: Optional[int] = Field(default=None, foreign_key="user.id")
user: Optional["User"] = Relationship(back_populates="events")
실제 외래키 컬럼(user_id
)은 event
테이블에 생성되며, 관계의 기준이 됩니다.
③ Optional 필드의 동작 방식
description: Optional[str] = None # → DB에서는 DEFAULT NULL
Optional
로 선언된 필드는 DB에서 NULL 허용 컬럼으로 생성되며, 값이 없을 경우 NULL
이 저장됩니다.
요약
Relationship()
은 ORM 전용 필드, 실제 DB 컬럼 X- 외래키 컬럼은 관계를 맺는 반대쪽 모델에 정의
back_populates
는 양방향 관계 매핑에 필수Optional
은 선택 입력 필드이며 NULL 허용
'Python' 카테고리의 다른 글
FastAPI에서 JWT 토큰을 이용한 로그인 인증 구현 (0) | 2025.05.21 |
---|---|
React와 Python 연결 (0) | 2025.05.20 |
FastAPI-DB연결 (0) | 2025.05.18 |
FastAPI-CRUD (0) | 2025.05.16 |
Path, Query, Response_model (0) | 2025.05.15 |
FastAPI 백엔드 개발 환경 세팅 & 기본 API 구현 정리 (0) | 2025.05.14 |