์์ฆ ํ๋ก์ ํธ๋ฅผ ํ๋ฉด์ FastAPI Router๋ฅผ ๊ฐ๋ฐํ๋ฉด์ ์๊ธด Trouble Shooting์ ๋ฐํ์ฌ ํ๋ฒ ์ ์ด๋ณด๋ ค๊ณ ํฉ๋๋ค.
FastAPI์ Swagger๋ฅผ ์ฌ์ฉํ์ฌ VLM ์์ ์ฒ๋ฆฌ ์๋ฒ๋ฅผ ๊ฐ๋ฐํ๊ณ , ์ด๋ฅผ AWS EC2์ ๋ฐฐํฌํ์ฌ Docker ์ด๋ฏธ์ง๋ก ๋น๋ํ๊ณ ์ปจํ ์ด๋๋ฅผ ์คํํ์ต๋๋ค. ์๋ฒ ์์ฒด๋ ์ ์์ ์ผ๋ก ์๋ํ์ง๋ง, ํ๋ก ํธ์๋์ ์ฐ๋ํ์ ๋ ์์์น ๋ชปํ CORS(Cross-Origin Resource Sharing) ์๋ฌ๋ฅผ ๋ง์ฃผํ๊ฒ ๋์์ต๋๋ค.
์ด ๊ธ์์๋ CORS ์๋ฌ๊ฐ ๋ฐ์ํ ์์ธ๊ณผ ์ด๋ฅผ ์ด๋ป๊ฒ ํด๊ฒฐํ๋์ง, ๊ทธ๋ฆฌ๊ณ ์ด์ ํ๊ฒฝ์์์ ๋ณด์ ๊ณ ๋ ค ์ฌํญ์ ๋ํด ์์ธํ ๋ค๋ค๋ณด๊ฒ ์ต๋๋ค.
๋ธ๋ผ์ฐ์ ์์์ CORS ์๋ฌ
ํ๋ก ํธ์๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฐ๊ฒฐํ๋ฉด์, http://localhost:5173์์ FastAPI ์๋ฒ(http://43.202.66.178:8000)๋ก API ์์ฒญ์ ๋ณด๋์ ๋ ๋ค์๊ณผ ๊ฐ์ CORS ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค:
Access to fetch at 'http://43.202.66.178:8000/api/video/receive-video/' from origin 'http://localhost:5173' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
์ด ์๋ฌ๋ก ์ธํด AI Server์์ ํ๋ก ํธ์๋๋ฐ ๋ฐฑ์๋ API์ ์ ๊ทผํ ์ ์์๋ค๋ ๋ฌธ์ ๊ฐ ์๊ฒผ์ต๋๋ค.
๋ธ๋ผ์ฐ์ ๊ฐ ๋ค๋ฅธ ์ถ์ฒ(Origin)์์ ๋ฆฌ์์ค๋ฅผ ์์ฒญํ์ผ๋, ์๋ฒ๊ฐ ์ด๋ฅผ ํ์ฉํ์ง ์์๊ธฐ ๋๋ฌธ์ ๋ฐ์ํฉ๋๋ค.
CORS๋ ๋ฌด์์ธ๊ฐ?
CORS(Cross-Origin Resource Sharing)๋ ๊ต์ฐจ ์ถ์ฒ ์์ ๊ณต์ ๋ฅผ ์๋ฏธํ๋ฉฐ, ์น ๋ธ๋ผ์ฐ์ ์์ ๋ณด์์์ ์ด์ ๋ก ๋๋ฉ์ธ ๊ฐ์ ์์ฒญ์ ์ ํํ๋ ์ ์ฑ ์ ๋๋ค. ์ฆ, ํ ๋๋ฉ์ธ์์ ๋ก๋๋ ์น ํ์ด์ง๊ฐ ๋ค๋ฅธ ๋๋ฉ์ธ์ ์์์ ์ ๊ทผํ๋ ค๊ณ ํ ๋, ๋ธ๋ผ์ฐ์ ๋ ๋ณด์์ ์ํด ์ด๋ฅผ ์ฐจ๋จํฉ๋๋ค.
์๋ฅผ ๋ค์ด, ํ๋ก ํธ์๋๊ฐ http://localhost:5173์์ ์คํ๋๊ณ ๋ฐฑ์๋๊ฐ http://43.202.66.178:8000์ ์์ ๋, ๋ ๋๋ฉ์ธ์ ์๋ก ๋ค๋ฅด๋ฏ๋ก ๋ธ๋ผ์ฐ์ ๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์ด๋ฌํ ์์ฒญ์ ํ์ฉํ์ง ์์ต๋๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก ์น ๋ธ๋ผ์ฐ์ ๋ ๋ณด์์์ ์ด์ ๋ก ๋์ผ ์ถ์ฒ ์ ์ฑ (Same-Origin Policy)์ ๋ฐ๋ฅด๋ฉฐ, ๋ค๋ฅธ ์ถ์ฒ์ ๋ฆฌ์์ค์ ๋ํ ์ ๊ทผ์ ์ ํํฉ๋๋ค. CORS๋ ์ด๋ฌํ ์ ํ์ ์ํํ์ฌ ํ์ํ ๊ฒฝ์ฐ์๋ง ๋ฆฌ์์ค ์ ๊ทผ์ ํ์ฉํ ์ ์๊ฒ ํฉ๋๋ค.
๋ฌธ์ ์ ์์ธ
Access to fetch at 'http://43.202.66.178:8000/api/video/receive-video/' from origin 'http://localhost:5173' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
๋ธ๋ผ์ฐ์ ์์ ๋ฐ์ํ ์๋ฌ ๋ฉ์์ง๋ฅผ ๋ถ์ํด๋ณด๋ฉด, ์๋ฒ ์๋ต์ Access-Control-Allow-Origin ํค๋๊ฐ ์๊ธฐ ๋๋ฌธ์ ๋ธ๋ผ์ฐ์ ๊ฐ ์์ฒญ์ ์ฐจ๋จํ๊ณ ์๋ค๋ ๊ฒ์ ์ ์ ์์ต๋๋ค. ์ด๋ ์๋ฒ๊ฐ CORS ์ ์ฑ ์ ์ ์ ํ๊ฒ ์ค์ ํ์ง ์์์์ ์๋ฏธํฉ๋๋ค.
ํด๊ฒฐ ๋ฐฉ๋ฒ
FastAPI์์ CORS ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด CORSMiddleware๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
CORSMiddleware๋ฅผ ์ค์ ํ๋ ๋ฐฉ๋ฒ์๋ ๋ ๊ฐ์ง๊ฐ ์์ต๋๋ค.
- ํน์ ํ๋ก ํธ์๋ URL์ allow_origins์ ๋ช ์์ ์ผ๋ก ์ง์ ํ๊ธฐ
- allow_origins๋ฅผ "*"๋ก ์ค์ ํ์ฌ ๋ชจ๋ ์ถ์ฒ๋ฅผ ํ์ฉํ๊ธฐ
์ด ๋ ๊ฐ์ง ๋ฐฉ๋ฒ ์ค ์ฒซ ๋ฒ์งธ๋ ๋ณด์์ ๋ ์์ ํ์ง๋ง, ๋ ๋ฒ์งธ ๋ฐฉ๋ฒ์ ๊ฐ๋ฐ ๊ณผ์ ์์ ๋น ๋ฅด๊ฒ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์์ต๋๋ค.
1. FastAPI์ CORSMiddleware ์ค์
FastAPI๋ starlette.middleware.cors.CORSMiddleware๋ฅผ ํตํด CORS ์ค์ ์ ๊ฐํธํ๊ฒ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
๋ค์์ ๋ ๊ฐ์ง ์ค์ ๋ฐฉ๋ฒ์ ๋ํ ์์ ์ ๋๋ค.
์ฒซ ๋ฒ์งธ ๋ฐฉ๋ฒ: ํน์ ์ถ์ฒ(origin)๋ฅผ ๋ช ์์ ์ผ๋ก ํ์ฉ
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
origins = [
"<http://frontend-app.yourdomain.com>",
"<http://frontend-app2.yourdomain.com:7000>"
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins, # ํน์ ์ถ์ฒ๋ง ํ์ฉ
allow_methods=["*"], # ๋ชจ๋ HTTP ๋ฉ์๋ ํ์ฉ
allow_headers=["*"], # ๋ชจ๋ ํค๋ ํ์ฉ
allow_credentials=True, # ์๊ฒฉ ์ฆ๋ช
ํ์ฉ (ํ์ ์)
)
๋ ๋ฒ์งธ ๋ฐฉ๋ฒ: ๋ชจ๋ ์ถ์ฒ ํ์ฉ
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # ๋ชจ๋ ์ถ์ฒ ํ์ฉ
allow_methods=["*"], # ๋ชจ๋ HTTP ๋ฉ์๋ ํ์ฉ
allow_headers=["*"], # ๋ชจ๋ ํค๋ ํ์ฉ
allow_credentials=False, # ์๊ฒฉ ์ฆ๋ช
๋นํ์ฉ
)
์ฃผ์: allow_origins ๋ฅผ "*" ๋ก ์ค์ ํ๋ฉด allow_credentials ๋ฅผ ๋ฐ๋์ False ๋ก ์ค์ ํด์ผ ํฉ๋๋ค.
์ด๋ ๋ณด์์์ ์ด์ ๋ก ๋ธ๋ผ์ฐ์ ์์ ์๊ตฌํ๋ CORS ์ ์ฑ ์ ๋ฐ๋ฅธ ๊ฒ์ ๋๋ค.
2. ๋ณด์ ๊ณ ๋ ค์ฌํญ
์ด์ ํ๊ฒฝ์์๋ ๋ณด์์ ๊ฐํํ๊ธฐ ์ํด ๋ ๋ฒ์งธ ๋ฐฉ๋ฒ์ธ ๋ชจ๋ ์ถ์ฒ ํ์ฉ์ ํผํ๊ณ , ์ฒซ ๋ฒ์งธ ๋ฐฉ๋ฒ์ฒ๋ผ ์ ํํ ํ๋ก ํธ์๋ URL๋ง์ allow_origins์ ๋ช ์์ ์ผ๋ก ์ง์ ํ๋ ๊ฒ์ด ๊ถ์ฅ๋ฉ๋๋ค.
๋ชจ๋ ์ถ์ฒ๋ฅผ ํ์ฉํ๋ฉด ์๋์น ์์ ์ถ์ฒ์์๋ API์ ์ ๊ทผํ ์ ์๊ฒ ๋์ด ๋ณด์ ์ทจ์ฝ์ ์ด ๋ฐ์ํ ์ ์์ต๋๋ค.
์ด์ ํ๊ฒฝ ์ค์ ์์
origins = [
"<https://frontend-app.yourdomain.com>",
"<https://frontend-app2.yourdomain.com>"
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins, # ํน์ ์ถ์ฒ๋ง ํ์ฉ
allow_methods=["GET", "POST", "PUT", "DELETE"], # ํ์ํ ๋ฉ์๋๋ง ํ์ฉ
allow_headers=["Content-Type", "Authorization"], # ํ์ํ ํค๋๋ง ํ์ฉ
allow_credentials=True, # ํ์ ์
)
์ฝ๋ ๊ตฌํ
CORS ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด FastAPI์ CORSMiddleware๋ฅผ ์ค์ ํ์์ต๋๋ค. ์๋๋ ํด๋น ์ค์ ๊ณผ ๊ด๋ จ๋ ์์ ์ฝ๋์ ๋๋ค.
CORS Middleware ์ค์
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
# ๊ฐ๋ฐ ํ๊ฒฝ์์๋ ๋ชจ๋ ์ถ์ฒ๋ฅผ ํ์ฉ
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # ๋ชจ๋ ์ถ์ฒ ํ์ฉ
allow_methods=["*"],
allow_headers=["*"],
allow_credentials=False, # ์๊ฒฉ ์ฆ๋ช
๋นํ์ฉ
)
API ์๋ํฌ์ธํธ ๊ตฌํ
from fastapi import APIRouter, UploadFile, File, HTTPException, Response
import uuid
import os
import logging
router = APIRouter()
logger = logging.getLogger(__name__)
UPLOAD_DIR = "/path/to/upload"
class UploadResponse(BaseModel):
video_id: str
message: str
@router.post("/receive-video/", response_model=UploadResponse)
async def receive_video_endpoint(response: Response, file: UploadFile = File(...)):
"""
๋น๋์ค ํ์ผ์ ์
๋ก๋ ๋ฐ์ ์ ์ฅํ๊ณ , video_id๋ฅผ ๋ฐํํฉ๋๋ค.
"""
# ์๋ต ํค๋์ CORS ์ค์ ์ถ๊ฐ (๋ณด์กฐ์ ์ธ ์กฐ์น)
response.headers["Access-Control-Allow-Origin"] = "*"
response.headers["Access-Control-Allow-Credentials"] = "False"
# ์์ฒญ ์์ ๋ก๊ทธ
logger.info("receive_video_endpoint called")
logger.info(f"Received file: {file.filename}")
# ์ง์ํ๋ ํ์ผ ํ์ ํ์ธ
ALLOWED_EXTENSIONS = {"webm", "mp4", "mov", "avi", "mkv"}
file_extension = file.filename.split(".")[-1].lower()
if file_extension not in ALLOWED_EXTENSIONS:
logger.warning(f"์ง์ํ์ง ์๋ ํ์ผ ํ์: {file_extension}")
raise HTTPException(status_code=400, detail="์ง์ํ์ง ์๋ ํ์ผ ํ์์
๋๋ค.")
# ๊ณ ์ ํ video_id ์์ฑ
video_id = uuid.uuid4().hex
# ๋น๋์ค ํ์ผ ์ ์ฅ ๊ฒฝ๋ก ์ค์
video_filename = f"{video_id}.{file_extension}"
file_path = os.path.join(UPLOAD_DIR, video_filename)
# ๋น๋์ค ํ์ผ ์ ์ฅ
try:
with open(file_path, "wb") as buffer:
buffer.write(await file.read())
logger.info(f"๋น๋์ค ํ์ผ ์ ์ฅ ์๋ฃ: {file_path}")
except Exception as e:
logger.error(f"ํ์ผ ์ ์ฅ ์ค ์ค๋ฅ ๋ฐ์: {e}")
raise HTTPException(status_code=500, detail=f"ํ์ผ ์ ์ฅ ์ค ์ค๋ฅ ๋ฐ์: {e}")
return UploadResponse(
video_id=video_id,
message="๋น๋์ค ์
๋ก๋ ์๋ฃ. ํผ๋๋ฐฑ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ผ๋ ค๋ฉด /send-feedback/{video_id} ์๋ํฌ์ธํธ๋ฅผ ํธ์ถํ์ธ์."
)
์ฃผ์ ํฌ์ธํธ
- CORS Middleware ์ค์ : ๋ชจ๋ ์ถ์ฒ๋ฅผ ํ์ฉํ๋๋ก ์ค์ ํ์์ผ๋, ์ด๋ ๊ฐ๋ฐ ํ๊ฒฝ์์๋ง ์ฌ์ฉํ๊ณ , ์ด์ ํ๊ฒฝ์์๋ ํน์ ์ถ์ฒ๋ง ํ์ฉํ๋๋ก ๋ณ๊ฒฝํด์ผ ํฉ๋๋ค.
- ์๋ต ํค๋ ์ค์ : response.headers๋ฅผ ํตํด ์ถ๊ฐ์ ์ธ CORS ํค๋๋ฅผ ์ค์ ํ ์ ์์ต๋๋ค. ๊ทธ๋ฌ๋ ์ด๋ ๋ฏธ๋ค์จ์ด ์ค์ ๊ณผ ํจ๊ป ์ฌ์ฉํ๋ ๊ฒ์ ๊ถ์ฅ๋์ง ์์ผ๋ฉฐ, ๋ฏธ๋ค์จ์ด ์ค์ ๋ง์ผ๋ก ์ถฉ๋ถํ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
- ํ์ผ ์ ๋ก๋ ๋ก์ง: ๋น๋์ค ํ์ผ์ ๋ฐ์ ์ง์ ๋ ๋๋ ํ ๋ฆฌ์ ์ ์ฅํ๊ณ , ๊ณ ์ ํ video_id๋ฅผ ์์ฑํ์ฌ ํด๋ผ์ด์ธํธ์ ๋ฐํํฉ๋๋ค.
์ด์ ํ๊ฒฝ์์์ ๋ณด์ ๊ณ ๋ ค ์ฌํญ
๊ฐ๋ฐ ๋จ๊ณ์์๋ ๋ชจ๋ ์ถ์ฒ๋ฅผ ํ์ฉํ๋ ๊ฒ์ด ํธ๋ฆฌํ ์ ์์ง๋ง, ์ด์ ํ๊ฒฝ์์๋ ๋ณด์์ ํฐ ์ํ์ ์ด๋ํ ์ ์์ต๋๋ค.
์ ๋ชจ๋ ์ถ์ฒ๋ฅผ ํ์ฉํ๋ฉด ์ ๋ ๊น?
- ๋ณด์ ์ํ ์ฆ๊ฐ: ๋ชจ๋ ๋๋ฉ์ธ์์ ์๋ฒ์ ์์ฒญ์ ๋ณด๋ผ ์ ์์ผ๋ฏ๋ก, ์ ์์ ์ธ ์ฌ์ดํธ์์ ์๋ฒ๋ฅผ ์ค์ฉํ ์ ์์ต๋๋ค.
- ํฌ๋ก์ค ์ฌ์ดํธ ์์ฒญ ์์กฐ(CSRF) ๊ณต๊ฒฉ ๊ฐ๋ฅ์ฑ: ์ธ์ฆ ์ ๋ณด๊ฐ ํ์ํ API์ ๊ฒฝ์ฐ, ๊ณต๊ฒฉ์๊ฐ ์ฌ์ฉ์์ ๋ธ๋ผ์ฐ์ ๋ฅผ ํตํด ์๋ฒ์ ์์์ ์์ฒญ์ ๋ณด๋ผ ์ ์์ต๋๋ค.
์ฌ๋ฐ๋ฅธ ์ ๊ทผ ๋ฐฉ๋ฒ
์ด์ ํ๊ฒฝ์์๋ ํ์ฉํ ์ถ์ฒ๋ฅผ ๋ช ์์ ์ผ๋ก ์ง์ ํด์ผ ํฉ๋๋ค:
origins = [
"<https://your-frontend-domain.com>",
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True, # ํ์ํ ๊ฒฝ์ฐ True๋ก ์ค์
allow_methods=["GET", "POST"], # ํ์ฉํ HTTP ๋ฉ์๋ ์ ํ
allow_headers=["Content-Type", "Authorization"], # ํ์ํ ํค๋๋ง ํ์ฉ
)
์ด๋ฅผ ํตํด ์๋ฒ๋ ์ง์ ๋ ๋๋ฉ์ธ์์ ์ค๋ ์์ฒญ๋ง ํ์ฉํ๊ฒ ๋ฉ๋๋ค.
์ถ๊ฐ์ ์ธ ๊ณ ๋ ค ์ฌํญ: ์๋ต ํค๋ ์๋ ์ค์ ํผํ๊ธฐ
์ฒ์์๋ ๊ฐ ์๋ํฌ์ธํธ์ ์๋ต ํค๋์ ์ง์ CORS ๊ด๋ จ ํค๋๋ฅผ ์ถ๊ฐํ๋ ค๊ณ ํ์ต๋๋ค:
response.headers["Access-Control-Allow-Origin"] = "*"
ํ์ง๋ง ์ด๋ ๊ถ์ฅ๋์ง ์๋ ๋ฐฉ๋ฒ์ ๋๋ค.
- ์ ์ง ๋ณด์ ์ด๋ ค์: ๊ฐ ์๋ํฌ์ธํธ๋ง๋ค ํค๋๋ฅผ ์๋์ผ๋ก ์ค์ ํ๋ฉด ์ฝ๋๊ฐ ๋ณต์กํด์ง๊ณ ์ค๋ฅ๊ฐ ๋ฐ์ํ ๊ฐ๋ฅ์ฑ์ด ๋์ต๋๋ค.
- ๋ฏธ๋ค์จ์ด ํ์ฉ: FastAPI์ CORSMiddleware๋ CORS ์ค์ ์ ์ค์์์ ๊ด๋ฆฌํ๋ฏ๋ก, ์ฝ๋์ ์ผ๊ด์ฑ๊ณผ ์ ์ง ๋ณด์์ฑ์ด ํฅ์๋ฉ๋๋ค.
CORS ์๋ฌ๋ ํ๋ก ํธ์๋์ ๋ฐฑ์๋์ ๋๋ฉ์ธ์ด ๋ค๋ฅผ ๋ ๋ฐ์ํ๋ ์ผ๋ฐ์ ์ธ ๋ฌธ์ ์ ๋๋ค. ์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์๋ ์๋ฒ์์ ์ ์ ํ CORS ์ค์ ์ ํด์ผ ํ๋ฉฐ, FastAPI์์๋ CORSMiddleware๋ฅผ ์ฌ์ฉํ์ฌ ์ฝ๊ฒ ์ด๋ฅผ ๊ด๋ฆฌํ ์ ์์ต๋๋ค.
ํต์ฌ ์์
- ๊ฐ๋ฐ ํ๊ฒฝ์์๋ ๋ชจ๋ ์ถ์ฒ๋ฅผ ํ์ฉํ๋, ์ด์ ํ๊ฒฝ์์๋ ๋ฐ๋์ ํ์ฉํ ์ถ์ฒ๋ฅผ ๋ช ์์ ์ผ๋ก ์ง์ ํด์ผ ํฉ๋๋ค.
- allow_credentials ์ค์ ์ ์ ์ํด์ผ ํ๋ฉฐ, allow_origins๊ฐ "*"์ผ ๋๋ allow_credentials๋ฅผ False๋ก ์ค์ ํด์ผ ํฉ๋๋ค.
- ์๋ต ํค๋๋ฅผ ์๋์ผ๋ก ์ค์ ํ๊ธฐ๋ณด๋ค๋ CORSMiddleware๋ฅผ ์ฌ์ฉํ์ฌ ์ค์์์ ๊ด๋ฆฌํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
์ฐธ๊ณ ์๋ฃ
Middleware - Starlette
Middleware Starlette includes several middleware classes for adding behavior that is applied across your entire application. These are all implemented as standard ASGI middleware classes, and can be applied either to Starlette or to any other ASGI applicat
www.starlette.io
Cross-Origin Resource Sharing (CORS) - HTTP | MDN
Cross-Origin Resource Sharing (CORS) is an HTTP-header based mechanism that allows a server to indicate any origins (domain, scheme, or port) other than its own from which a browser should permit loading resources. CORS also relies on a mechanism by which
developer.mozilla.org
CORS (Cross-Origin Resource Sharing) - FastAPI
FastAPI framework, high performance, easy to learn, fast to code, ready for production
fastapi.tiangolo.com