
I. DB(MySQL) 설정 및 연결
1. 필요한 라이브러리들을 설치한다.
2. MySQL워크 벤치에서 DATABASE 생성할 때 아래처럼 생성한다.
Default Charset : utf8mb4
Default Collation : utf8mb4_0900_ai_ci
3. Svelte_0.0.1/app/core/database.py를 생성하고 작성
비동기적 서버운영을 위해, ASYNC_ENGINE = create_async_engine...과 AsyncSessionLocal = async_sessionmaker... 를 작성해야 한다.
### mysql 비동기적으로 사용하기 위한 라이브러리들 #####
pip install sqlalchemy
pip install aiomysql
"""# aiomysql : 이걸 설치하면 pymysql는 같이 설치된다.
# aiomysql 은 pymysql 기반에서 동작하며, native DB Driver의 Async 처리 지원.
또한 Connection Pooling 기능도 지원
### RuntimeError: 'cryptography'
package is required for sha256_password or caching_sha2_password auth methods
# mysql password를 지나갈 때 cryptography가 필요하다. """
pip install cryptography
# db migraions에 필요한 alembic
pip install alembic
# Svelte_0.0.2/app/core/database.py
from datetime import datetime, timezone
from typing import AsyncGenerator
from sqlalchemy import Integer, DateTime
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker, AsyncSession
from sqlalchemy.orm import declarative_base, Mapped, mapped_column
from app.core.settings import CONFIG
DATABASE_URL = f"{CONFIG.DB_TYPE}+{CONFIG.DB_DRIVER}://{CONFIG.DB_USER}:{CONFIG.DB_PASSWORD}@{CONFIG.DB_HOST}:{CONFIG.DB_PORT}/{CONFIG.DB_NAME}?charset=utf8"
print("2. DATABASE_URL:", DATABASE_URL.split("//")[0])
ASYNC_ENGINE = create_async_engine(DATABASE_URL,
echo=CONFIG.DEBUG,
future=True,
pool_size=10, max_overflow=0, pool_recycle=300, # 5분마다 연결 재활용
# encoding="utf-8"
)
# 세션 로컬 클래스 생성
AsyncSessionLocal = async_sessionmaker(
ASYNC_ENGINE,
class_=AsyncSession, # add
expire_on_commit=False,
autocommit=False,
autoflush=False,
# poolclass=NullPool, # SQLite에서는 NullPool 권장
)
"""
autocommit=False 부분은 주의하자.
autocommit=False로 설정하면 데이터를 변경했을때 commit 이라는 사인을 주어야만 실제 저장이 된다.
만약 autocommit=True로 설정할 경우에는 commit이라는 사인이 없어도 즉시 데이터베이스에 변경사항이 적용된다.
그리고 autocommit=False인 경우에는 데이터를 잘못 저장했을 경우 rollback 사인으로 되돌리는 것이 가능하지만
autocommit=True인 경우에는 commit이 필요없는 것처럼 rollback도 동작하지 않는다는 점에 주의해야 한다.
"""
Base = declarative_base() # Base 클래스 (모든 모델이 상속)
async def get_db() -> AsyncGenerator[AsyncSession, None]:
session: AsyncSession = AsyncSessionLocal()
print(f"[get_session] new session: {id(session)}")
try:
yield session
except Exception as e:
print(f"Session rollback triggered due to exception: {e}")
await session.rollback()
raise
finally:
print(f"[get_session] close session: {id(session)}")
await session.close()
II. Redis(Inmemory 저장소) 설정 및 연결
윈도우 데스크탑 도커를 설치하고, container를 만들어 Redis서버를 기동하여 Redis를 사용한다.

- 인메모리 저장: 데이터를 메모리에 저장하므로 디스크에 저장하는 기존 데이터베이스보다 훨씬 빠른 접근 속도를 제공합니다.
- 키-값(Key-Value) 구조: 데이터를 키와 값의 쌍으로 저장하고 관리합니다.
- 다양한 데이터 구조 지원: 문자열, 해시, 리스트, 셋, 소티드 셋 등 다양한 자료구조를 지원하여 데이터를 효율적으로 저장할 수 있습니다.
- 캐싱 및 세션 저장소: 빠른 속도를 활용하여 데이터베이스의 부하를 줄이는 캐시나 사용자의 세션 정보를 저장하는 데 주로 사용됩니다.
- 다양한 용도: 데이터베이스, 캐시 외에도 메시지 큐, 실시간 순위표 등 다양한 용도로 활용됩니다.
- 싱글 스레드: 기본적으로 싱글 스레드로 동작하여 CPU 최적화 효율은 크지 않지만, 개발과 사용의 단순성을 제공합니다.
- 영속성(Persistence): 서버가 재시작되어도 데이터가 유지될 수 있도록 디스크에 데이터를 저장하는 기능도 제공합니다.
pip install redis
.env파일에 Redis서버 설정을 추가한다.
# # Svelte_0.0.1/.env에 배포과정에 필요한 아래내용 추가(없다면, 임시로 로컬서버용으로라도...)
# redis_config.py 연결 설정: REDIS_PASSWORD None은 에러 유발, 배포 과정에 비번 지정
REDIS_HOST = <Redis 서버 IP>
REDIS_PORT = <Redis 서버 Port>
REDIS_DB = 0
REDIS_PASSWORD = <비밀번호>
Svelte_0.0.1/app/core/settings.py의 배포시에 관련한 설정 등록한다.
# Svelte_0.0.1/app/core/settings.py
import os
from functools import lru_cache
from typing import List, Optional
from pydantic import Field, model_validator
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
"""여기에서 값이 비어있는 것은 .env 파일에서 채워진다.
그 값은 override 되지 않는다. 흐름상 그럴것 같기는 한데...???
최종적으로 .env에 설정된 값으로 채워져 버리는 것인것 같다."""
APP_ENV: str = "development"
APP_NAME: str = "Svelte_FastAPI"
APP_VERSION: str = "0.0.2"
APP_DESCRIPTION: str = "FastAPI_Svelte을 이용한 프로젝트 개발"
DEBUG: bool = False
# 기본값은 두지 않고 .env에서 읽히도록 둡니다.
DB_TYPE: str
DB_DRIVER: str
SECRET_KEY: Optional[str] = None
ORIGINS: List[str] = Field(default_factory=list)
model_config = SettingsConfigDict(
env_file=".env",
env_file_encoding="utf-8",
extra="ignore",
case_sensitive=False,
)
class DevSettings(Settings):
DEBUG: bool = Field(..., validation_alias="DEBUG_TRUE")
ORIGINS: str = Field(..., validation_alias="DEV_ORIGINS")
DB_NAME: str = Field(..., validation_alias="DEV_DB_NAME")
DB_HOST: str = Field(..., validation_alias="DEV_DB_HOST")
DB_PORT: str = Field(..., validation_alias="DEV_DB_PORT")
DB_USER: str = Field(..., validation_alias="DEV_DB_USER")
DB_PASSWORD: str = Field(..., validation_alias="DEV_DB_PASSWORD")
class ProdSettings(Settings):
APP_NAME: str = Field(..., validation_alias="PROD_APP_NAME")
APP_VERSION: str = Field(..., validation_alias="PROD_APP_VERSION")
APP_DESCRIPTION: str = Field(..., validation_alias="PROD_APP_DESCRIPTION")
ORIGINS: str = Field(..., validation_alias="PROD_ORIGINS")
DB_NAME: str = Field(..., validation_alias="PROD_DB_NAME")
DB_HOST: str = Field(..., validation_alias="PROD_DB_HOST")
DB_PORT: str = Field(..., validation_alias="PROD_DB_PORT")
DB_USER: str = Field(..., validation_alias="PROD_DB_USER")
DB_PASSWORD: str = Field(..., validation_alias="PROD_DB_PASSWORD")
REDIS_HOST: str = Field(..., validation_alias="REDIS_HOST")
REDIS_PORT: str = Field(..., validation_alias="REDIS_PORT")
REDIS_DB: str = Field(..., validation_alias="REDIS_DB")
REDIS_PASSWORD: str = Field(..., validation_alias="REDIS_PASSWORD")
# 운영에서는 SECRET_KEY 필수
@model_validator(mode="after")
def ensure_secret_key(self):
if not self.SECRET_KEY or len(self.SECRET_KEY) < 16:
raise ValueError("In production, SECRET_KEY must be set and sufficiently long.")
return self
@lru_cache(maxsize=1)
def get_settings() -> Settings:
print("1. get_settings... Only One")
# model_config의 env_file 설정만으로도 충분하지만, 아래 호출이 있어도 문제는 없습니다.
try:
from dotenv import load_dotenv
load_dotenv(".env", override=False, encoding="utf-8")
except Exception as e:
print(f"load_dotenv error: {e}")
# python-dotenv 미설치 등인 경우에도 설정 로딩은 pydantic-settings가 처리하므로 무시 가능
pass
app_env = os.getenv("APP_ENV", "development").strip().lower()
if app_env == "production":
return ProdSettings()
return DevSettings()
CONFIG = get_settings()
Svelte_0.0.1/app/core/redis.py에 사용할 redis_client 객체를 생성한다.
# Svelte_0.0.2/app/core/redis.py
import os
from redis.asyncio import Redis, ConnectionPool
from app.core.settings import CONFIG
# # Connection Pool 기반 access ##############################
""" # AI Chat # 비동기적으로 ConnectionPool 적용
비동기 Redis 클라이언트와 함께 쓰려면 동기용 ConnectionPool이 아니라 asyncio 전용 풀을 써야 합니다.
즉, redis.ConnectionPool이 아니라 redis.asyncio.ConnectionPool을 사용해야 합니다.
from redis.asyncio import Redis, ConnectionPool
예시 1) asyncio용 ConnectionPool을 직접 생성해서 사용
"""
host = CONFIG.REDIS_HOST if CONFIG.APP_ENV == "production" else "localhost"
port = CONFIG.REDIS_PORT if CONFIG.APP_ENV == "production" else 6379
password = CONFIG.REDIS_PASSWORD if CONFIG.APP_ENV == "production" else None
db = CONFIG.REDIS_DB if CONFIG.APP_ENV == "production" else 0
redis_pool = ConnectionPool(
host=host,
port=port,
db=db,
password=password,
decode_responses=True, # 문자열 응답을 자동으로 디코딩
max_connections=10)
redis_client = Redis(connection_pool=redis_pool)
III. inits.py에 lifespan함수와 CORSMiddleware 설정과 등록
1. lifespan 함수 작성
@asynccontextmanager
async def lifespan(app: FastAPI):
print("Initializing database......")
# FastAPI 인스턴스 기동시 필요한 작업 수행.
try:
await redis_client.ping() # Redis 연결 테스트
print("Redis connection established......")
except redis.exceptions.ConnectionError:
print("Failed to connect to Redis......")
print("Starting up...")
yield
# FastAPI 인스턴스 종료시 필요한 작업 수행
await redis_client.aclose()
print("Redis connection closed......")
print("Shutting down...")
await ASYNC_ENGINE.dispose()
2. 작성한 lifespan함수를 FastAPI app 객체 생성에 적용
app = FastAPI(title=, CONFIG.APP_NAME,
version=, CONFIG.APP_VERSION,
description=, CONFIG.APP_DESCRIPTION,
lifespan=lifespan,
# docs_url=None, redoc_url=None 을 주면 기본 /docs, /redoc 페이지가 비활성화됩니다.
docs_url=None , redoc_url=None) # docs_url을 지정하는 커스텀 마이징할 때 (CSRF_TOKEN적용시)
# docs_url = None, redoc_url = "/swagger/redoc")
3. 프론트엔드 연결을 위한 CORSMiddleware를 설정이 필요하다. 그렇지 않고, 바로 프론트엔드 서버를 열결하면 접근할 수 없다는 콘솔의 에러가 뜬다.
.env파일에 ORIGINS 추가한다. (리스트로 한 줄로...)
# 프론트엔드 개발에 사용되는 포트: 아래 두개(127.0.0.1, localhost)는 별개로 인식한다. 둘다 필요하다.
# 리스트는 한 줄로..., 파싱 가능한 형태로 바꾸기
#DEV_ORIGINS="http://localhost:5173,http://localhost:5100,http://127.0.0.1:5173,http://127.0.0.1:5100"
DEV_ORIGINS=["http://localhost:5173/","http://localhost:5100/","http://127.0.0.1:5173/","http://127.0.0.1:5100/"]
PROD_ORIGINS=[]
inits.py에 CORSMiddleware를 설정하고, initialize_app() 함수에도 등록을 해준다.
def including_middleware(app):
app.add_middleware(
CORSMiddleware,
allow_origins=CONFIG.ORIGINS,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
def initialize_app():
print("3. Initializing app.....APP_ENV, CONFIG.ORIGINS: ", CONFIG.APP_ENV, CONFIG.ORIGINS)
app = FastAPI(title=CONFIG.APP_NAME,
version=CONFIG.APP_VERSION,
description=CONFIG.APP_DESCRIPTION,
lifespan=lifespan,
# docs_url=None, redoc_url=None 을 주면 기본 /docs, /redoc 페이지가 비활성화됩니다.
docs_url=None , redoc_url=None) # docs_url을 지정하는 커스텀 마이징할 때 (CSRF_TOKEN적용시)
# docs_url = None, redoc_url = "/swagger/redoc")
'''# 주소만 커스터마이징할 때 redoc_url = "/swagger/redoc" 이렇게 하면된다.
주소 뿐만 아니라 html 문서자체를 커스텀 마이징하는 경우는 views/swagger.py 의 함수를 사용하면 된다.
'''
including_router(app)
including_middleware(app)
return app
4. 완성된 Svelte_0.0.1/app/core/inits.py
# Svelte_0.0.2/app/core/inits.py
import os
from contextlib import asynccontextmanager
import redis
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from app.core.database import ASYNC_ENGINE
from app.core.redis import redis_client
from app.core.settings import CONFIG
from app.views import root, swagger
@asynccontextmanager
async def lifespan(app: FastAPI):
print("Initializing database......")
# FastAPI 인스턴스 기동시 필요한 작업 수행.
try:
await redis_client.ping() # Redis 연결 테스트
print("Redis connection established......")
except redis.exceptions.ConnectionError:
print("Failed to connect to Redis......")
print("Starting up...")
yield
# FastAPI 인스턴스 종료시 필요한 작업 수행
await redis_client.aclose()
print("Redis connection closed......")
print("Shutting down...")
await ASYNC_ENGINE.dispose()
def including_router(app):
app.include_router(swagger.router, prefix="/swagger")
app.include_router(root.router, prefix="", tags=["Root"]) # root 페이지는 / 슬래시를 없애라.
def including_middleware(app):
app.add_middleware(
CORSMiddleware,
allow_origins=CONFIG.ORIGINS,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
def initialize_app():
print("3. Initializing app.....APP_ENV, CONFIG.ORIGINS: ", CONFIG.APP_ENV, CONFIG.ORIGINS)
app = FastAPI(title=CONFIG.APP_NAME,
version=CONFIG.APP_VERSION,
description=CONFIG.APP_DESCRIPTION,
lifespan=lifespan,
# docs_url=None, redoc_url=None 을 주면 기본 /docs, /redoc 페이지가 비활성화됩니다.
docs_url=None , redoc_url=None) # docs_url을 지정하는 커스텀 마이징할 때 (CSRF_TOKEN적용시)
# docs_url = None, redoc_url = "/swagger/redoc")
'''# 주소만 커스터마이징할 때 redoc_url = "/swagger/redoc" 이렇게 하면된다.
주소 뿐만 아니라 html 문서자체를 커스텀 마이징하는 경우는 views/swagger.py 의 함수를 사용하면 된다.
'''
including_router(app)
including_middleware(app)
return app
IV. Svelte 프론트엔드 서버와 백엔드 FastAPI 서버 통신
서버간 통신에 앞서, Svelte_0.0.2/frontend/src/App.svelte 파일을 열어 아래를 붙여 넣는다. (해당파일을 모두 지우고, 붙여넣어도 되지만 적당한 위치를 찾아 js script 칸과 h1 태그를 html에 넣어도 된다.) 백엔드서버의 서버 포트 숫자를 반듯이 확인하고... CORS 설정이 제대로 되었는지 다시 확인하고... 어디라도 맞지 않으면, .... 뒤의 { message }가 프론트엔드 렌더링이 일어나지 않는다.
# Svelte_0.0.2/frontend/src/App.svelte
<script>
let message;
fetch("http://127.0.0.1:8000").then((response) => {
response.json().then((json) => {
message = json.message;
});
});
</script>
<h1>...{message}</h1>
cd frontend로 이동한 다음 npm run dev로 프론트엔드 서버를 구동하면...(물론, 그전에 백엔드를 구동해 놓아야 한다.)
위에서 CORSMiddleware를 설정하고, initialize_app() 함수에도 등록해놓지 않으면, 혹은 잘못 설정되었다면 통신이 이루어지지 않을 것이다. 콘솔창을 확인해보면 아래와 같은 메시지가 나온다.


설정이 잘 되었다면,,,, # Svelte_0.0.2/app/views/root.py 로 분리한 @router.get("/")이 렌더링 될 것이다. main.py를 분리하면서 router도 inits.py로 옮겨 한 꺼번에 관리할 수 있도록 해놓았었다.
# Svelte_0.0.2/app/views/root.py
from fastapi import APIRouter, Request, Depends
router = APIRouter()
@router.get("/")
def get_root():
"""svelte 프론트엔드 단에서는 Home.svelte ("/") 으로 가게 변경함"""
return {"message": "Hello World"}

'FastAPI' 카테고리의 다른 글
| alembic.ini파일과 migrations폴더를 삭제해도 되는가? (0) | 2025.11.06 |
|---|---|
| User model 생성 및 첫 DB migrations (0) | 2025.11.06 |
| main.py 수정 및 Swagger 커스터마이징 (0) | 2025.11.05 |
| 앱 개발을 위한 설정파일 만들기 (0) | 2025.11.05 |
| FastAPI 백엔드 개발 순서(Google AI Mode) (0) | 2025.11.05 |