백엔드 엔진 (main.py)
Python
import uvicorn
from fastapi import FastAPI, WebSocket
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse
import os
app = FastAPI()
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
try:
while True:
data = await websocket.receive_text()
print(f"Received: {data}")
await websocket.send_text(f"Python: {data}")
except:
pass
current_dir = os.path.dirname(os.path.abspath(__file__))
base_dir = os.path.dirname(current_dir)
out_path = os.path.join(base_dir, "client", "out")
if os.path.exists(out_path):
app.mount("/", StaticFiles(directory=out_path, html=True), name="static")
@app.get("/{full_path:path}")
async def serve_spa(full_path: str):
return FileResponse(os.path.join(out_path, "index.html"))
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
📱 프론트엔드 클라이언트 (page.tsx)
TypeScript
"use client";
import { useEffect, useState } from "react";
export default function Home() {
const [socket, setSocket] = useState<WebSocket | null>(null);
const [msg, setMsg] = useState("대기 중...");
useEffect(() => {
const ws = new WebSocket(`ws://${window.location.host}/ws`);
ws.onopen = () => setMsg("연결됨!");
ws.onmessage = (e) => setMsg(e.data);
setSocket(ws);
return () => ws.close();
}, []);
return (
<div style={{ padding: "20px", textAlign: "center" }}>
<h1>Hello World</h1>
<p>{msg}</p>
<button onClick={() => socket?.send("안녕!")}>보내기</button>
</div>
);
}
📖 시스템 구조 및 작동 원리 설명
1. 프로젝트 경로 체계
현재 프로젝트는 host 폴더(Python)와 client 폴더(Next.js)로 이원화되어 있다. main.py에서는 os.path.dirname 함수를 두 번 중첩 사용하여 host 폴더 상위의 프로젝트 루트로 이동한 뒤, 다시 client/out 경로를 추적하도록 설계되었다. 이 방식은 실행 환경에 관계없이 빌드된 정적 파일의 위치를 정확히 지목하게 한다.
2. 정적 파일 서빙과 SPA 라우팅
FastAPI의 StaticFiles 모듈은 Next.js가 npm run build를 통해 생성한 HTML, CSS, JavaScript 파일들을 브라우저에 전달하는 웹 서버 역할을 수행한다. 또한, 브라우저가 직접 특정 경로로 접속했을 때 발생할 수 있는 404 에러를 방지하기 위해, 모든 경로 요청을 index.html로 리다이렉트하는 SPA(Single Page Application) 대응 로직이 포함되어 있다.
3. 웹소켓 기반의 실시간 통신
브라우저에서 window.location.host를 사용하여 웹소켓 연결을 시도하면, 서버는 클라이언트의 IP 주소와 상관없이 현재 가동 중인 Python 서버와 실시간 양방향 채널을 형성한다. 클라이언트에서 전송한 텍스트 데이터는 Python의 websocket_endpoint에서 수신되어 터미널에 출력되며, 서버는 즉시 확인 메시지를 응답으로 돌려주어 연결 무결성을 증명한다.
4. 배포를 위한 사전 설정
Next.js 환경에서는 반드시 next.config.js 파일에 output: 'export' 설정을 추가해야 한다. 이 설정이 활성화되어야만 Node.js 서버 없이도 Python에서 직접 실행 가능한 순수 정적 파일(out 폴더)이 생성된다.