React + Python FastAPI๋ก
์น ๊ฒ์ ๋ง๋ค๊ธฐ #1
ํ๋ก์ ํธ ์ธํ โ ํด๋ ๊ตฌ์กฐ, Vite, FastAPI ํ๊ฒฝ ๊ตฌ์ฑ๊น์ง
1 ์ ์ด ๊ธฐ์ ์คํ์ ๊ณจ๋๋๊ฐ
์ด๋ฒ์ ๋ง๋ค ๊ฒ์์ ์ฅ์ ๋ฌผ์ ํผํด ์ต๋ํ ์ค๋ ๋ฒํฐ๋ ๋ฌดํ ๋ฌ๋ฆฌ๊ธฐ ๊ฒ์์ด๋ค. ์๊ฐ์ด ์ง๋ ์๋ก ์๋๊ฐ ๋นจ๋ผ์ง๊ณ , ๊ธฐ๋ก์ ๋๋ค์๊ณผ ํจ๊ป ๋ฆฌ๋๋ณด๋์ ๋จ๊ธธ ์ ์๋ค.
๊ฒ์ ๋ก์ง ์์ฒด๋ JavaScript๋ก ์ถฉ๋ถํ์ง๋ง, ์ ์ ์ ์ฅ๊ณผ ๋ฆฌ๋๋ณด๋๋ฅผ ์ฌ๋ฌ ์ฌ๋์ด ๊ณต์ ํ๋ ค๋ฉด ์๋ฒ๊ฐ ํ์ํ๋ค. ๊ทธ๋์ ํ๋ก ํธ์ ๋ฐฑ์๋๋ฅผ ๋ถ๋ฆฌํ๋ค.
๊ฒ์ ํ๋ฉด, UI, ์บ๋ฆญํฐ ์ ๋๋ฉ์ด์ ๋ฑ ๋ชจ๋ ํ๋ฉด์ ๋ด๋น. ์ปดํฌ๋ํธ ๋จ์๋ก ๋๋ ์ ๊ด๋ฆฌํ๊ธฐ ์ฝ๋ค.
Create React App๋ณด๋ค ํจ์ฌ ๋น ๋ฅธ ๊ฐ๋ฐ ์๋ฒ. ์ ์ฅํ๋ฉด ์ฆ์ ๋ฐ์(HMR)๋์ด ๊ฐ๋ฐ ์๋๊ฐ ๋น ๋ฅด๋ค.
Python ๊ธฐ๋ฐ ์น ํ๋ ์์ํฌ. ์ฝ๋๊ฐ ๊ฐ๊ฒฐํ๊ณ , ์๋์ผ๋ก API ๋ฌธ์(/docs)๋ฅผ ๋ง๋ค์ด์ค์ ํ ์คํธํ๊ธฐ ํธํ๋ค.
FastAPI๋ฅผ ์คํ์์ผ์ฃผ๋ ASGI ์๋ฒ. --reload ์ต์
์ผ๋ก ์ฝ๋ ์์ ์ ์๋ ์ฌ์์๋๋ค.
2 ์ ์ฒด ํด๋ ๊ตฌ์กฐ
ํ๋ก์ ํธ ๋ฃจํธ๋ฅผ D:\Lee_project\neon-runner\๋ก ์ก๊ณ , ํ๋ก ํธ์ ๋ฐฑ์๋๋ฅผ ๋ช
ํํ๊ฒ ๋ถ๋ฆฌํ๋ค.
โโโ frontend\ โ React + Vite ๊ฒ์ ํ๋ฉด
โ โโโ src\
โ โ โโโ App.jsx โ ๊ฒ์ ๋ฉ์ธ ์ปดํฌ๋ํธ
โ โ โโโ main.jsx โ React ์ง์ ์
โ โโโ index.html
โ โโโ package.json
โ โโโ vite.config.js
โโโ backend\ โ Python FastAPI ์ ์ ์๋ฒ
โโโ venv\ โ Python ๊ฐ์ํ๊ฒฝ
โโโ main.py โ FastAPI ์๋ฒ ์ฝ๋
โโโ scores.json โ ์ ์ ์ ์ฅ ํ์ผ (์๋์์ฑ)
ํ๋์ ํด๋์ ๋ค ๋ฃ์ผ๋ฉด Python ํจํค์ง์ JS ํจํค์ง๊ฐ ์์ฌ์ ๊ด๋ฆฌ๊ฐ ์ด๋ ค์์ง๋ค. ์ญํ ๋ณ๋ก ํด๋๋ฅผ ๋ถ๋ฆฌํ๋ฉด ๋์ค์ ๋ฐฐํฌํ ๋๋ ๊ฐ๊ฐ ๋ ๋ฆฝ์ ์ผ๋ก ์ฌ๋ฆด ์ ์๋ค.
3 PowerShell ์คํ ์ ์ฑ ํด์
Windows์์ PowerShell๋ก npm ๋ช
๋ น์ด๋ฅผ ์ฒ์ ์คํํ๋ฉด ์ด๋ฐ ์ค๋ฅ๊ฐ ๋๋ค.
npm : ์ด ์์คํ
์์ ์คํฌ๋ฆฝํธ๋ฅผ ์คํํ ์ ์์ผ๋ฏ๋ก
C:\Program Files\nodejs\npm.ps1 ํ์ผ์ ๋ก๋ํ ์ ์์ต๋๋ค.
+ CategoryInfo : ๋ณด์ ์ค๋ฅ: (:) [], PSSecurityException
Windows PowerShell์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ธ๋ถ ์คํฌ๋ฆฝํธ ์คํ์ ๋ง์๋๋๋ค. npm์ ๋ด๋ถ์ ์ผ๋ก .ps1 ์คํฌ๋ฆฝํธ๋ฅผ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ์ด ์ ์ฑ
์ ์ํํด์ผ ํ๋ค.
๊ด๋ฆฌ์ ๊ถํ PowerShell์์ ์๋ ๋ช ๋ น์ด๋ฅผ ์คํํ๋ค.
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned
-Scope CurrentUser โ ํ์ฌ ์ฌ์ฉ์์๊ฒ๋ง ์ ์ฉ (์์คํ
์ ์ฒด ๋ณ๊ฒฝ X)RemoteSigned โ ๋ก์ปฌ ์คํฌ๋ฆฝํธ๋ ํ์ฉ, ์ธํฐ๋ท์์ ๋ฐ์ ์คํฌ๋ฆฝํธ๋ ์๋ช
ํ์โ ๋ณด์์ ์ ์งํ๋ฉด์ npm์ด ๋์ํ ์ ์๋ ์ต์ํ์ ์ค์ ์ด๋ค.
4 React + Vite ํ๋ก ํธ์๋ ์ธํ
frontend\ ํด๋ ์์์ Vite ํ๋ก์ ํธ๋ฅผ ์์ฑํ๋ค. .์ ๊ฒฝ๋ก๋ก ์ฐ๋ฉด ํ์ฌ ํด๋์ ๋ฐ๋ก ์์ฑ๋๋ค.
# frontend ํด๋๋ก ์ด๋ cd D:\Lee_project\neon-runner\frontend # Vite + React ํ๋ก์ ํธ ์์ฑ (ํ์ฌ ํด๋์) npm create vite@latest . -- --template react # ํจํค์ง ์ค์น npm install # ๊ฐ๋ฐ ์๋ฒ ์คํ npm run dev
์ค์น ํ http://localhost:5173 ์์ React ๊ธฐ๋ณธ ํ๋ฉด์ด ๋จ๋ฉด ์ฑ๊ณต์ด๋ค.
๊ธฐ๋ณธ์ผ๋ก ์์ฑ๋ ํ์ผ ์ค ๊ฒ์์ ํ์ ์๋ ํ์ผ๋ค์ ์ ๋ฆฌํ๋ค.
Remove-Item src\App.css Remove-Item src\index.css Remove-Item -Recurse src\assets
๊ทธ๋ฆฌ๊ณ App.jsx์ main.jsx๋ฅผ ๊น๋ํ๊ฒ ์ด๊ธฐํํ๋ค.
import { StrictMode } from 'react' import { createRoot } from 'react-dom/client' import App from './App.jsx' createRoot(document.getElementById('root')).render( <StrictMode> <App /> </StrictMode>, )
export default function App() { return ( <div> <h1>NEON RUNNER</h1> </div> ) }
5 Python FastAPI ๋ฐฑ์๋ ์ธํ
์ ์ ์ ์ฅ๊ณผ ๋ฆฌ๋๋ณด๋ ์กฐํ๋ฅผ ๋ด๋นํ๋ API ์๋ฒ๋ฅผ Python์ผ๋ก ๋ง๋ ๋ค.
๋จผ์ ๊ฐ์ํ๊ฒฝ(venv)์ ๋ง๋ ๋ค. ๊ฐ์ํ๊ฒฝ์ ํ๋ก์ ํธ๋ณ๋ก Python ํจํค์ง๋ฅผ ๋ ๋ฆฝ์ ์ผ๋ก ๊ด๋ฆฌํ ์ ์๊ฒ ํด์ค๋ค. ๋ค๋ฅธ ํ๋ก์ ํธ ํจํค์ง์ ์ถฉ๋ํ์ง ์๋๋ค.
# backend ํด๋๋ก ์ด๋ cd D:\Lee_project\neon-runner\backend # ๊ฐ์ํ๊ฒฝ ์์ฑ python -m venv venv # ๊ฐ์ํ๊ฒฝ ํ์ฑํ (์์ (venv) ํ์ ๋จ๋ฉด ์ฑ๊ณต) venv\Scripts\activate # FastAPI + ์๋ฒ(uvicorn) ์ค์น pip install fastapi uvicorn
์ ์ญ Python ํ๊ฒฝ์ ํจํค์ง๋ฅผ ์ค์นํ๋ฉด ํ๋ก์ ํธ๋ง๋ค ๋ฒ์ ์ถฉ๋์ด ๋ ์ ์๋ค. venv๋ก ๊ฒฉ๋ฆฌํ๋ฉด ์ด ํ๋ก์ ํธ์์๋ง ์ฌ์ฉํ๋ ํจํค์ง ๋ฒ์ ์ ๋ฐ๋ก ๊ด๋ฆฌํ ์ ์๋ค.
์ด์ backend\main.py๋ฅผ ๋ง๋ ๋ค. ์ด ํ์ผ ํ๋๊ฐ API ์๋ฒ ์ ์ฒด๋ค.
from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from pydantic import BaseModel import json import os app = FastAPI() # React ๊ฐ๋ฐ์๋ฒ(5173)์์ ์ค๋ ์์ฒญ ํ์ฉ app.add_middleware( CORSMiddleware, allow_origins=["http://localhost:5173"], allow_methods=["*"], allow_headers=["*"], ) DB_FILE = "scores.json" def load_scores(): if not os.path.exists(DB_FILE): return [] with open(DB_FILE, "r", encoding="utf-8") as f: return json.load(f) def save_scores(scores): with open(DB_FILE, "w", encoding="utf-8") as f: json.dump(scores, f, ensure_ascii=False, indent=2) # ์ ์ ๋ฐ์ดํฐ ํํ ์ ์ class ScoreEntry(BaseModel): nickname: str score: int time: float # GET /leaderboard โ ์์ 20๊ฐ ์ ์ ์กฐํ @app.get("/leaderboard") def get_leaderboard(): scores = load_scores() return scores[:20] # POST /score โ ์ ์ ์ ์ฅ ํ ์์ ๋ฐํ @app.post("/score") def post_score(entry: ScoreEntry): scores = load_scores() scores.append(entry.model_dump()) scores.sort(key=lambda x: x["score"], reverse=True) save_scores(scores) rank = next(i+1 for i, s in enumerate(scores) if s["nickname"] == entry.nickname and s["score"] == entry.score) return {"rank": rank, "total": len(scores)}
์๋ฒ ์คํ:
uvicorn main:app --reload # ์ฑ๊ณต ์ ์ถ๋ ฅ: # INFO: Uvicorn running on http://127.0.0.1:8000
http://127.0.0.1:8000/leaderboard ์์ []๊ฐ ๋ณด์ด๋ฉด API ์ ์ ๋์์ด๋ค. http://127.0.0.1:8000/docs์์๋ ์๋ ์์ฑ๋ API ๋ฌธ์๋ ๋ณผ ์ ์๋ค.
6 CORS ์ค์ ์ด ์ ํ์ํ๊ฐ
React๋ localhost:5173์์ ์คํ๋๊ณ , FastAPI๋ localhost:8000์์ ์คํ๋๋ค. ๋ธ๋ผ์ฐ์ ๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ค๋ฅธ ํฌํธ(์ถ์ฒ)๋ก ๋ณด๋ด๋ ์์ฒญ์ ์ฐจ๋จํ๋ค. ์ด๊ฒ์ด CORS(Cross-Origin Resource Sharing) ์ ์ฑ
์ด๋ค.
app.add_middleware( CORSMiddleware, allow_origins=["http://localhost:5173"], # React ์ฃผ์๋ง ํ์ฉ allow_methods=["*"], # GET, POST ๋ฑ ๋ชจ๋ ํ์ฉ allow_headers=["*"], # ๋ชจ๋ ํค๋ ํ์ฉ )
allow_origins๋ฅผ ์ค์ ๋๋ฉ์ธ์ผ๋ก ๋ฐ๊ฟ์ผ ํ๋ค.7 ์งํ ์ํฉ & ๋ค์ ํธ ์๊ณ
- ์๋ฃ D:\Lee_project\neon-runner\ ํด๋ ๊ตฌ์กฐ ์์ฑ
- ์๋ฃ PowerShell ์คํ ์ ์ฑ ํด์
- ์๋ฃ React + Vite ํ๋ก ํธ์๋ ์ธํ (localhost:5173)
- ์๋ฃ Python ๊ฐ์ํ๊ฒฝ + FastAPI ์ธํ (localhost:8000)
- ์๋ฃ ์ ์ ์ ์ฅ / ๋ฆฌ๋๋ณด๋ API ๊ธฐ๋ณธ ๊ตฌ์กฐ ์์ฑ
- ๋ค์ํธ Canvas๋ก ๊ฒ์ ๋ฐฐ๊ฒฝ + ์บ๋ฆญํฐ ๊ทธ๋ฆฌ๊ธฐ
- ์์ ์ฅ์ ๋ฌผ ์์ฑ + ์ถฉ๋ ๊ฐ์ง
- ์์ ์ ์ ์์คํ + FastAPI ์ฐ๋
์ด๋ฒ ํธ์ ๊ฒ์ ์ฝ๋๋ณด๋ค ํ๊ฒฝ ์ธํ ์ด ์ฃผ์๋ค.
์ธํ ์ด ์ ๋์ด์์ด์ผ ๋์ค์ ํค๋งค์ง ์๋๋ค.
๋ค์ ํธ์์๋ ๋ณธ๊ฒฉ์ ์ผ๋ก ๊ฒ์ ํ๋ฉด์ ๊ทธ๋ฆฐ๋ค.
'Python' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| React + FastAPI๋ก ์น ๊ฒ์ ๋ง๋ค๊ธฐ #4 - Render + Vercel ๋ฐฐํฌ (0) | 2026.04.24 |
|---|---|
| React + FastAPI๋ก ์น ๊ฒ์ ๋ง๋ค๊ธฐ #3 - ์์ฑ (0) | 2026.04.23 |
| React + Python FastAPI๋ก์น ๊ฒ์ ๋ง๋ค๊ธฐ #2 (0) | 2026.04.23 |
| ๐ค๏ธ๋ ์จ ๋ฐ์ดํฐ(2) - ์ ์ฅํ๊ณ API ์๋ฒ๊น์ง ๋ง๋ค๊ธฐ (0) | 2026.03.25 |
| ๐ค๏ธ๋ ์จ ๋ฐ์ดํฐ(1) - ๋ ์จ ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ (์ธ๋ถ API ํธ์ถ) (0) | 2026.03.25 |