FastAPI - CORS(Cross-Origin Resource Sharing)

KjjeddΒ·2026λ…„ 1μ›” 14일

FastAPI

λͺ©λ‘ 보기
15/16
post-thumbnail

🌐 CORS λ™μž‘ 방식 β€” λΈŒλΌμš°μ €κ°€ 응닡을 β€˜κ²€μ—΄β€™ν•˜λŠ” μˆœκ°„

CORSλŠ” μš”μ²­μ„ λ§‰λŠ” 기술이 μ•„λ‹ˆλ‹€.
μ •ν™•νžˆ λ§ν•˜λ©΄ λΈŒλΌμš°μ €κ°€ 응닡을 μ‚¬μš©ν•  수 μžˆλŠ”μ§€ νŒλ‹¨ν•˜λŠ” κ·œμΉ™μ΄λ‹€.

μ„œλ²„λŠ” μš”μ²­μ„ μ •μƒμ μœΌλ‘œ μ²˜λ¦¬ν•˜κ³  응닡을 보낸닀.
ν•˜μ§€λ§Œ λΈŒλΌμš°μ €κ°€ κ·Έ 응닡을 JavaScript에 λ„˜κ²¨μ€„μ§€ 말지λ₯Ό κ²°μ •ν•œλ‹€.

예λ₯Ό λ“€μ–΄,
ν”„λ‘ νŠΈμ—”λ“œλŠ” http://localhost:5500μ—μ„œ μ‹€ν–‰ 쀑이고,
λ°±μ—”λ“œλŠ” http://localhost:8000μ—μ„œ μ‹€ν–‰ 쀑이라고 κ°€μ •ν•˜μž.
이 λ‘˜μ€ port numberκ°€ λ‹€λ₯΄κΈ° λ•Œλ¬Έμ— μΆœμ²˜κ°€ λ‹€λ₯΄λ‹€.
β—οΈμ΄λ•Œ λΈŒλΌμš°μ €λŠ” λ³΄μ•ˆ μ •μ±… λ•Œλ¬Έμ— λ‹€λ₯Έ 좜처둜의 μš”μ²­μ„ 기본적으둜 μ°¨λ‹¨ν•œλ‹€.

πŸ‘‰ CORSλŠ” 이 문제λ₯Ό ν•΄κ²°ν•˜λŠ” 방법이닀.

πŸ”„ 전체 흐름 μš”μ•½

[1] ν”„λ‘ νŠΈμ—”λ“œ (λ‹€λ₯Έ Origin)
        |
        |  API μš”μ²­
        v
[2] λ°±μ—”λ“œ μ„œλ²„
        |
        |  정상 응닡 + CORS 헀더
        v
[3] λΈŒλΌμš°μ €
        |
        |  CORS 헀더 검사
        |
        |  β”œβ”€ ν—ˆμš©λœ Origin β†’ JavaScript에 응닡 전달
        |  └─ ν—ˆμš© μ•ˆ 됨 β†’ CORS μ—λŸ¬ λ°œμƒ
        v
[4] ν”„λ‘ νŠΈμ—”λ“œ JS
  • μ„œλ²„λŠ” μš”μ²­μ„ κ±°λΆ€ν•˜μ§€ μ•Šκ³ 
  • 응닡도 μ •μƒμ μœΌλ‘œ 200 OK둜 μ˜¨λ‹€
  • λΈŒλΌμš°μ €κ°€ JavaScript μ ‘κ·Όλ§Œ μ°¨λ‹¨ν•œλ‹€

🧠 λΈŒλΌμš°μ €λŠ” 뭘 보고 νŒλ‹¨ν• κΉŒ?

λΈŒλΌμš°μ €λŠ” 응닡 헀더에 ν¬ν•¨λœ CORS κ΄€λ ¨ 헀더λ₯Ό ν™•μΈν•œλ‹€.

Access-Control-Allow-Origin: http://localhost:5500
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: Content-Type
Access-Control-Allow-Origin

β€’ 이 응닡을 μ–΄λ–€ μΆœμ²˜μ—μ„œ μ‚¬μš©ν•΄λ„ λ˜λŠ”μ§€
β€’ λΈŒλΌμš°μ €μ—κ²Œ λͺ…ν™•ν•˜κ²Œ μ•Œλ €μ£ΌλŠ” μ—­ν• 

❌ μ™œ μ½˜μ†”μ—λ§Œ μ—λŸ¬κ°€ 뜰까?

CORS μ—λŸ¬λ₯Ό 처음 보면 ν—·κ°ˆλ¦¬λŠ” μ΄μœ κ°€ μžˆλ‹€.

  • Network νƒ­ β†’ 200 OK
  • Console νƒ­ β†’ CORS μ—λŸ¬

이건 λͺ¨μˆœμ΄ μ•„λ‹ˆλ‹€.

μš”μ²­μ€ μ„±κ³΅ν–ˆκ³ , 응닡도 λ„μ°©ν–ˆλ‹€.
단지 λΈŒλΌμš°μ €κ°€ JavaScript 접근을 μ°¨λ‹¨ν–ˆμ„ 뿐이닀

πŸ‘‰ κ·Έλž˜μ„œ Postman, curlμ—μ„œλŠ” 잘 λ˜λŠ”λ°
πŸ‘‰ λΈŒλΌμš°μ €μ—μ„œλ§Œ CORS μ—λŸ¬κ°€ λ‚œλ‹€.

πŸ“š 이둠과 핡심 λ‚΄μš©

πŸ”’ λΈŒλΌμš°μ €μ˜ λ³΄μ•ˆ μ •μ±…

⚠️ CORSλŠ” μ„œλ²„ λ¬Έμ œκ°€ μ•„λ‹ˆλ‹€

CORS μ—λŸ¬κ°€ λ‚˜λ©΄ μ„œλ²„κ°€ 잘λͺ»λλ‹€κ³  μƒκ°ν•˜κΈ° 쉽닀.
ν•˜μ§€λ§Œ CORSλŠ” λΈŒλΌμš°μ €μ˜ λ³΄μ•ˆ μ •μ±…!

μ„œλ²„λŠ” μ •μƒμ μœΌλ‘œ 응닡을 보낸닀.
λ‹€λ§Œ λΈŒλΌμš°μ €κ°€ κ·Έ 응닡을 JavaScriptμ—μ„œ μ‚¬μš©ν•˜μ§€ λͺ»ν•˜κ²Œ μ°¨λ‹¨ν•˜λŠ” 것

μ‹€μ œλ‘œ 개발자 λ„κ΅¬μ˜ Network 탭을 보면
응닡은 200 OK인데 Consoleμ—λŠ” CORS μ—λŸ¬κ°€ μ°ν˜€ μžˆλ‹€.

λΈŒλΌμš°μ €μ—λŠ” 동일 좜처 μ •μ±…(Same-Origin Policy)μ΄λΌλŠ” κ·œμΉ™μ΄ μžˆλ‹€.
μ›Ή νŽ˜μ΄μ§€μ˜ JavaScriptκ°€ λ‹€λ₯Έ 좜처의 데이터에 ν•¨λΆ€λ‘œ μ ‘κ·Όν•˜μ§€ λͺ»ν•˜κ²Œ λ§‰λŠ” λ³΄μ•ˆ μž₯μΉ˜λ‹€.

예λ₯Ό λ“€μ–΄ μ² μˆ˜κ°€ 은행 μ‚¬μ΄νŠΈμ— λ‘œκ·ΈμΈν•œ μƒνƒœλΌκ³  ν•΄λ³΄μž.
동일 좜처 정책이 μ—†λ‹€λ©΄, μ•…μ„± μ‚¬μ΄νŠΈκ°€ 철수의 λΈŒλΌμš°μ €λ₯Ό 톡해
은행 APIλ₯Ό ν˜ΈμΆœν•΄μ„œ κ³„μ’Œ 정보λ₯Ό 빼갈 μˆ˜λ„ μžˆλ‹€.

ν˜„λŒ€μ˜ μ›Ή μ„œλΉ„μŠ€ κ΅¬μ‘°λŠ” ν”„λ‘ νŠΈμ—”λ“œμ™€ λ°±μ—”λ“œκ°€ λΆ„λ¦¬λ˜μ–΄ μžˆλŠ” κ²½μš°κ°€ λ§Žλ‹€.

  • ν”„λ‘ νŠΈμ—”λ“œ β†’ 3000번 포트
  • λ°±μ—”λ“œ β†’ 8000번 포트

정상적인 μš”μ²­μΈλ°λ„ μΆœμ²˜κ°€ λ‹€λ₯΄λ‹€λŠ” 이유둜 λ§‰νžˆλ©΄ κ³€λž€ν•˜λ‹€.
이 문제λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ λ“±μž₯ν•œ 게 λ°”λ‘œCORS

πŸ‘‰ λ°±μ—”λ“œκ°€ λΈŒλΌμš°μ €μ—κ²Œ
β€œμ΄ μΆœμ²˜μ—μ„œ μ˜€λŠ” μš”μ²­μ€ ν—ˆμš©ν•œλ‹€β€λΌκ³  μ•Œλ €μ£ΌλŠ” 방식


πŸ” CORS λ™μž‘ 방식

[ν”„λ‘ νŠΈμ—”λ“œ]
      |
      |  API μš”μ²­
      v
[λ°±μ—”λ“œ]
      |
      |  응닡 + CORS 헀더
      v
[λΈŒλΌμš°μ €]
      |
      |  CORS 헀더 검사
      |  β”œβ”€ ν—ˆμš© β†’ JSμ—μ„œ 응닡 μ‚¬μš© κ°€λŠ₯
      |  └─ 차단 β†’ CORS μ—λŸ¬
      v
[ν”„λ‘ νŠΈμ—”λ“œ JS]

μ„œλ²„λŠ” 응닡 헀더에
β€œμ΄ μΆœμ²˜λŠ” ν—ˆμš©λ¨β€μ΄λΌλŠ” 정보λ₯Ό λ‹΄μ•„ 보낸닀.

λΈŒλΌμš°μ €λŠ” κ·Έ 헀더λ₯Ό 보고 νŒλ‹¨ν•œλ‹€.
ν—ˆμš©λœ 좜처면 응닡 데이터λ₯Ό JavaScriptμ—μ„œ μ‚¬μš©ν•  수 있게 ν•œλ‹€.


βš™οΈ FastAPIμ—μ„œ CORS μ„€μ •ν•˜κΈ°

FastAPIλŠ” CORSMiddlewareλ₯Ό κΈ°λ³Έ μ œκ³΅ν•œλ‹€.
κΈ°μ‘΄ λ°±μ—”λ“œ μ½”λ“œμ— λ―Έλ“€μ›¨μ–΄λ§Œ μΆ”κ°€ν•˜λ©΄ λœλ‹€.

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

# ν—ˆμš©ν•  좜처 λͺ©λ‘
origins = [
    "http://localhost:5500",  # ν”„λ‘ νŠΈμ—”λ“œ 개발 μ„œλ²„
    "http://localhost:3000",  # React λ“± ν”„λ‘ νŠΈμ—”λ“œ
]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,      # ν—ˆμš©ν•  좜처
    allow_credentials=True,     # μΏ ν‚€ 포함 μš”μ²­ ν—ˆμš©
    allow_methods=["*"],        # λͺ¨λ“  HTTP λ©”μ„œλ“œ ν—ˆμš©
    allow_headers=["*"],        # λͺ¨λ“  HTTP 헀더 ν—ˆμš©
)

@app.get("/")
async def root():
    return {"message": "CORS μ„€μ • μ™„λ£Œ"}

μ„œλ²„λ₯Ό μž¬μ‹œμž‘ν•˜κ³  λ‹€μ‹œ μš”μ²­ν•˜λ©΄
μ΄λ²ˆμ—λŠ” μ •μƒμ μœΌλ‘œ 응닡이 λ“€μ–΄μ˜¨λ‹€.

❗ λ°±μ—”λ“œλŠ” origins에 λ„£μ§€ μ•ŠλŠ”λ‹€

originsμ—λŠ” ν”„λ‘ νŠΈμ—”λ“œ 좜처만 μ λŠ”λ‹€.
λ°±μ—”λ“œ 자기 μžμ‹ μ€ ν¬ν•¨ν•˜μ§€ μ•ŠλŠ”λ‹€.
origins = [
    "http://localhost:5500",
    "https://myapp.com"
]

🧩 CORSMiddleware 핡심 μ˜΅μ…˜

μ˜΅μ…˜ μ„€λͺ…
allow_origins ν—ˆμš©ν•  좜처 λͺ©λ‘ (["*"] κ°€λŠ₯)
allow_credentials μΏ ν‚€Β·Authorization 헀더 포함 μš”μ²­ ν—ˆμš©
allow_methods ν—ˆμš©ν•  HTTP λ©”μ„œλ“œ
allow_headers ν—ˆμš©ν•  μš”μ²­ 헀더

⚠️ credentials + μ™€μΌλ“œμΉ΄λ“œ 주의

allow_credentials=True μƒνƒœμ—μ„œλŠ”
allow_origins=["*"]λ₯Ό μ‚¬μš©ν•  수 μ—†λ‹€.
(μΏ ν‚€λ‚˜ 인증 정보λ₯Ό ν¬ν•¨ν•˜λŠ” μš”μ²­μ€ 좜처λ₯Ό λͺ…ν™•νžˆ μ§€μ •ν•΄μ•Ό 함)

# ❌ 잘λͺ»λœ μ„€μ • (λ™μž‘ x)
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],					# μ™€μΌλ“œμΉ΄λ“œ
    allow_credentials=True,					# credentials ν™œμ„±ν™”
)

# βœ… μ˜¬λ°”λ₯Έ μ„€μ •
app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:3000"], # λͺ…μ‹œμ  좜처
    allow_credentials=True,
)

🧠 정리 및 μš”μ•½

  • Origin = ν”„λ‘œν† μ½œ + 도메인 + 포트
  • CORSλŠ” μ„œλ²„ λ¬Έμ œκ°€ μ•„λ‹ˆλΌ λΈŒλΌμš°μ € μ •μ±…
  • λΈŒλΌμš°μ €κ°€ 응닡 μ‚¬μš© μ—¬λΆ€λ₯Ό κ²°μ •
  • FastAPIμ—μ„œλŠ” CORSMiddleware둜 ν•΄κ²°
  • credentials μ‚¬μš© μ‹œ μΆœμ²˜λŠ” λ°˜λ“œμ‹œ λͺ…μ‹œ
  • μ„œλ²„λŠ” β€œν—ˆμš© μ—¬λΆ€λ₯Ό ν—€λ”λ‘œ μ„ μ–Έβ€λ§Œ ν•œλ‹€
  • μ΅œμ’… νŒλ‹¨μ€ 항상 λΈŒλΌμš°μ €κ°€ ν•œλ‹€
profile
Gongbuhaja

0개의 λŒ“κΈ€