# Project_folder/main.py
from app.core.inits import initialize_app
app = initialize_app()
# Project_folder/app/core/inits.py
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
from fastapi.middleware.cors import CORSMiddleware
from fastapi_csrf_jinja.middleware import FastAPICSRFJinjaMiddleware
from app.core.settings import STATIC_DIR, MEDIA_DIR, CONFIG
from app.views import index
def including_middleware(app):
app.add_middleware(CORSMiddleware,
allow_origins=["*"], # 실제 프론트 주소
allow_methods=["*"],
allow_headers=["*"],
allow_credentials=True,
max_age=-1)
app.add_middleware(FastAPICSRFJinjaMiddleware, secret=CONFIG.SECRET_KEY,
cookie_name="csrf_token", header_name="X-CSRF-Token")
def including_router(app):
app.include_router(index.router, prefix="", tags=["Index"])
def initialize_app():
app = FastAPI()
app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static")
app.mount("/media", StaticFiles(directory=MEDIA_DIR), name="media")
including_middleware(app)
including_router(app)
return app
# Project_folder/app/core/settings.py
import os
from functools import lru_cache
from pathlib import Path
from typing import Optional
from fastapi.templating import Jinja2Templates
from fastapi_csrf_jinja.jinja_processor import csrf_token_processor
from pydantic import Field, model_validator
from pydantic_settings import BaseSettings, SettingsConfigDict
PRESENT_DIR = os.path.dirname(os.path.abspath(__file__))
APP_DIR = Path(__file__).resolve().parent.parent
ROOT_DIR = Path(__file__).resolve().parent.parent.parent ## root폴더
TEMPLATE_DIR = os.path.join(APP_DIR, 'templates')
STATIC_DIR = os.path.join(APP_DIR, 'static')
MEDIA_DIR = os.path.join(APP_DIR, 'media')
templates = Jinja2Templates(
directory=TEMPLATE_DIR,
context_processors=[csrf_token_processor("csrf_token", "X-CSRF-Token")]
)
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
SECRET_KEY: Optional[str] = None
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. setting start... 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()
print("2. settings...APP_ENV: ", app_env)
return DevSettings()
CONFIG = get_settings()
# Project_folder/templates/common/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2><stong>{{message}}</stong></h2>
</body>
</html>
# Project_folder/.env
# 배포시에 주석해제
#APP_ENV=production
DEBUG_TRUE=true
DEBUG_FALSE=false
# production시 DEBUG_FALSE = false
SECRET_KEY=7b6b057992ae6b79d3fecc6d3ebe40626ef4e2ed8221303202e60fdff204d2a7d334b0
ALGORITHM = HS256
DB_TYPE=mysql
DB_DRIVER=aiomysql
DEV_DB_NAME=advanced_db
DEV_DB_HOST=localhost
DEV_DB_PORT=3306
DEV_DB_USER=root
DEV_DB_PASSWORD=<비밀번호>
PROD_APP_NAME=Deployed APP
PROD_APP_VERSION=Deployed V_0
PROD_APP_DESCRIPTION=FastAPI_Jinja를 이용해 개발한 프로젝트에 대한 배포판
PROD_DB_NAME=advanced_db
PROD_DB_HOST=<배포서버 IP주소>
PROD_DB_PORT=<포트번호>
PROD_DB_USER=root
PROD_DB_PASSWORD=<비밀번호>
# redis_config.py 연결 설정: REDIS_PASSWORD None은 에러 유발, 배포 과정에 비번 지정
REDIS_HOST=<배포서버 IP주소>
REDIS_PORT=<포트번호>
REDIS_DB=0
REDIS_PASSWORD=<비밀번호>
# 이메일 보내기 sender 정보
SMTP_USERNAME=abcdefg@gmail.com
SMTP_PASSWORD=iccxyzmnkyfvszux
SMTP_FROM=abcdefg@gmail.com
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
ACCESS_COOKIE_NAME = access_token
REFRESH_COOKIE_NAME = refresh_token
NEW_ACCESS_COOKIE_NAME = new_access_token
NEW_REFRESH_COOKIE_NAME = new_refresh_token
ACCESS_TOKEN_EXPIRE = 30
REFRESH_TOKEN_EXPIRE = 7
PROFILE_IMAGE_URL = user_images/accounts/profiles
# 프론트엔드 개발에 사용되는 포트: 아래 두개(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:5174","http://localhost:7100","http://localhost:7101","http://127.0.0.1:5173","http://127.0.0.1:5174","http://127.0.0.1:7100","http://127.0.0.1:7101"]
PROD_ORIGINS=[]