FastAPI 백엔드 코드 상세 설명
app = FastAPI(
title=settings.API_TITLE,
version=settings.API_VERSION,
debug=settings.DEBUG,
)
# 미들웨어 순서 (중요!)
# 1. CORS
# 2. GZip
# 3. TrustedHost (운영 환경)# 개발 환경
python run_server.py
# 또는 직접 실행
uvicorn app.main:app --host 0.0.0.0 --port 8080 --reloadclass Settings:
# API 기본 설정
API_TITLE = "My Research Platform API"
API_VERSION = "0.1.0"
DEBUG = True
# MongoDB 설정
MONGO_URI = "mongodb://localhost:27017"
DATABASE_NAME = "research_forest"
# JWT 설정
SECRET_KEY = "your-secret-key"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 120 # 2시간
REFRESH_TOKEN_EXPIRE_DAYS = 30 # 30일
# 이메일 설정
EMAIL_PROVIDER = "gmail" # "gmail" 또는 "naver"
# AI 서비스
AI_SERVICE_URL = "http://ai-service:8001"
# 서버 설정
HOST = "0.0.0.0"
PORT = 8080
HOST_IP = "필수 설정!"config.py의 CORS_ORIGINS 프로퍼티에서 자동 관리:
- 로컬 개발 오리진
- HOST_IP 기반 오리진
- Netlify 배포 도메인
from app.core.database import get_database
async def some_function():
db = await get_database()
collection = db["posts"]
result = await collection.find_one({"_id": post_id})| 컬렉션명 | 설명 |
|---|---|
| users | 사용자 정보 |
| posts | 게시글 |
| comments | 댓글 |
| attachments | 첨부파일 메타데이터 |
| chat_rooms | 채팅방 |
| chat_messages | 채팅 메시지 |
| banners | 배너 |
| drafts | 임시저장 |
from app.core.database import get_connection_status, get_database_stats
status = await get_connection_status()
stats = await get_database_stats()app.include_router(auth.router, prefix="/api/auth", tags=["인증"])
app.include_router(board.router, prefix="/api/board", tags=["게시판"])
app.include_router(research.router, prefix="/api/research", tags=["연구"])
app.include_router(chat.router, prefix="/api/chat", tags=["채팅"])
app.include_router(attachment.router, prefix="/api/attachment", tags=["첨부파일"])
app.include_router(admin.router, prefix="/api/admin", tags=["관리"])
app.include_router(ai_proxy.router, prefix="/api/ai", tags=["AI"])
app.include_router(draft.router, prefix="/api/draft", tags=["임시저장"])
# WebSocket
app.include_router(websocket_native.router, prefix="/ws", tags=["웹소켓"])| 파일 | 주요 엔드포인트 | 설명 |
|---|---|---|
| auth.py | POST /login, /signup, /refresh | 인증 처리 |
| board.py | GET/POST/PUT/DELETE /posts | 게시판 CRUD |
| research.py | 연구 관련 API | 연구 데이터 관리 |
| chat.py | GET/POST /rooms, /messages | 채팅 REST API |
| attachment.py | POST /upload, GET /download | 파일 업로드/다운로드 |
| secure_attachment.py | 보안 강화 파일 API | 암호화, 접근 제어 |
| enterprise_attachment.py | 엔터프라이즈 파일 API | 버전 관리, 감사 로그 |
| admin.py | 관리자 전용 API | 사용자/시스템 관리 |
| ai_proxy.py | AI 서비스 프록시 | AI 마이크로서비스 연동 |
| draft.py | 임시저장 API | 글 작성 중 자동 저장 |
| banner.py | 배너 관리 | 메인 페이지 배너 |
| activity.py | 활동 기록 | 사용자 활동 로깅 |
| websocket_native.py | WebSocket 연결 | 실시간 채팅 |
| google_oauth.py | Google OAuth | Google 로그인 |
# Access Token 페이로드
{
"sub": "user_id",
"email": "user@email.com",
"role": "student",
"exp": 1234567890 # 만료 시간
}
# Refresh Token 페이로드
{
"sub": "user_id",
"type": "refresh",
"exp": 1234567890
}from app.utils.auth_middleware import get_current_user
@router.get("/protected")
async def protected_route(current_user: dict = Depends(get_current_user)):
return {"user": current_user}from app.utils.permissions import require_permission, require_admin
@router.post("/admin-only")
async def admin_only_route(
current_user: dict = Depends(get_current_user),
_: None = Depends(require_admin)
):
return {"message": "관리자 전용"}
@router.post("/write")
async def write_post(
current_user: dict = Depends(get_current_user),
_: None = Depends(require_permission("write"))
):
return {"message": "글쓰기 권한 있음"}user_schema = {
"_id": ObjectId,
"email": str,
"name": str,
"password_hash": str,
"role": str, # "student", "researcher", "professor", "admin"
"is_active": bool,
"is_admin": bool,
"permissions": list,
"created_at": datetime,
"updated_at": datetime
}post_schema = {
"_id": ObjectId,
"title": str,
"content": str,
"category": str,
"author_id": ObjectId,
"author_name": str,
"attachments": list,
"view_count": int,
"like_count": int,
"comment_count": int,
"created_at": datetime,
"updated_at": datetime
}Pydantic 모델로 API 요청/응답 검증
from pydantic import BaseModel, EmailStr
class UserCreate(BaseModel):
email: EmailStr
name: str
password: str
class UserResponse(BaseModel):
id: str
email: str
name: str
role: str
is_admin: bool@router.post("/signup", response_model=UserResponse)
async def signup(user_data: UserCreate):
# user_data는 자동으로 검증됨
...# 비밀번호 해싱
hash_password(password: str) -> str
verify_password(plain: str, hashed: str) -> bool
# JWT 토큰
create_access_token(data: dict) -> str
create_refresh_token(data: dict) -> str
decode_token(token: str) -> dict# 인증 이메일 발송
send_verification_email(email: str, code: str)
# 비밀번호 재설정 이메일
send_password_reset_email(email: str, reset_link: str)# 권한 확인
check_permission(user: dict, permission: str) -> bool
# 게시글 작성자 확인
is_post_owner(user: dict, post: dict) -> bool
# 관리자 확인
is_admin(user: dict) -> bool# 파일 타입 검증
validate_file_type(filename: str, content: bytes) -> bool
# 악성 파일 검사
scan_file(file_path: str) -> bool
# 안전한 파일명 생성
generate_safe_filename(original: str) -> str# 스케줄러 초기화
init_scheduler(config: dict, db)
# 정리 작업 예약
# - 만료된 임시저장 삭제
# - 오래된 로그 정리
# - 미사용 파일 정리# 감사 로그 기록
log_action(user_id: str, action: str, details: dict)
# 예: 파일 다운로드 로깅
log_action(user_id, "file_download", {"file_id": file_id})비즈니스 로직 분리
# AI 서비스 호출
async def call_ai_service(prompt: str, context: dict) -> str:
response = await httpx.post(
f"{settings.AI_SERVICE_URL}/generate",
json={"prompt": prompt, "context": context}
)
return response.json()# Google Drive 파일 업로드
upload_to_drive(file_path: str, folder_id: str) -> str
# 파일 다운로드
download_from_drive(file_id: str) -> bytes@app.middleware("http")
async def performance_monitoring_middleware(request: Request, call_next):
start_time = time.time()
response = await call_next(request)
process_time = time.time() - start_time
if process_time > 2.0:
logger.warning(f"느린 응답: {request.url.path} - {process_time:.3f}초")
response.headers["X-Process-Time"] = str(process_time)
return responseapp.add_middleware(
CORSMiddleware,
allow_origins=settings.CORS_ORIGINS,
allow_credentials=False, # Authorization 헤더만 사용
allow_methods=["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"],
allow_headers=["Authorization", "Content-Type"],
max_age=86400, # Preflight 24시간 캐시
)@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception):
error_id = int(time.time() * 1000000) % 1000000
logger.error(f"예외 발생 [ID: {error_id}]: {exc}")
return JSONResponse(
status_code=500,
content={
"error": "서버 내부 오류",
"error_id": error_id,
"detail": str(exc) if settings.DEBUG else "관리자에게 문의"
}
)from fastapi import HTTPException
# 사용 예시
if not user:
raise HTTPException(status_code=404, detail="사용자를 찾을 수 없습니다")
if not authorized:
raise HTTPException(status_code=403, detail="권한이 없습니다")# 로그 레벨: LOG_LEVEL 환경변수로 제어
# 기본값: WARNING
# 개발환경: DEBUG
# 운영환경: WARNING 또는 ERROR
LOG_LEVEL = os.getenv("LOG_LEVEL", "WARNING").upper()- Linux/Unix:
/tmp/research_board.log - Windows:
%TEMP%/research_board/research_board.log
2026-02-03 10:30:00 - app.main - WARNING - 느린 응답: /api/posts - 2.5초
GET /api/health
응답:
{
"status": "healthy",
"timestamp": 1234567890,
"database": {"connected": true, "latency_ms": 5},
"system": {
"memory_usage_percent": 45.2,
"cpu_usage_percent": 12.5,
"disk_usage_percent": 60.0
},
"version": "0.1.0"
}GET /api/system/stats
// 클라이언트
const ws = new WebSocket('wss://your-domain/ws/chat/{room_id}');// 메시지 전송
{
"type": "message",
"content": "안녕하세요",
"sender_id": "user_id"
}
// 입장 알림
{
"type": "join",
"user_id": "user_id",
"user_name": "홍길동"
}- 단순 업로드/다운로드
- 파일 크기 제한
- 파일 암호화
- 접근 권한 검사
- 다운로드 로깅
- 파일 버전 관리
- 감사 로그
- 중복 파일 감지
- Google Drive 연동
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8080"]HOST_IP=your-server-ip # 필수!
MONGO_URI=mongodb://... # 필수
SECRET_KEY=your-secret # 필수
DATABASE_NAME=research_forest
# 선택
LOG_LEVEL=WARNING
ENVIRONMENT=production
DEBUG=False