콘텐츠로 이동

URL, 헤더, 쿠키, 세션, CORS

URL(Uniform Resource Locator)은 네트워크 상의 리소스 위치를 표현하는 문자열입니다. 복잡해 보이지만 각 부분은 명확한 역할을 가집니다.

https://api.mymodel.com:8443/v1/predict?model=bert&lang=ko#result
│ │ │ │ │ │
scheme host port path query string fragment
구성 요소예시설명
schemehttps통신 프로토콜 (http, https, ws, wss)
hostapi.mymodel.com서버 도메인 또는 IP
port8443포트 번호 (생략 시 https=443, http=80)
path/v1/predict서버 내 리소스 경로
query stringmodel=bert&lang=ko?로 시작, &로 구분하는 파라미터
fragmentresult페이지 내 특정 위치 (서버로 전송 안 됨)

쿼리 파라미터 vs 경로 파라미터

섹션 제목: “쿼리 파라미터 vs 경로 파라미터”

FastAPI에서는 두 가지 방식으로 파라미터를 받습니다.

# 경로 파라미터: 리소스 식별에 사용
@app.get("/models/{model_id}/predict")
async def predict(model_id: str):
...
# 쿼리 파라미터: 필터링, 정렬, 옵션에 사용
@app.post("/predict")
async def predict(lang: str = "ko", top_k: int = 5):
...
# GET /predict?lang=ko&top_k=5

경로 파라미터는 “어떤 리소스인지”를, 쿼리 파라미터는 “어떻게 처리할지”를 표현합니다.

헤더는 요청과 응답에 대한 메타데이터를 전달합니다. AI 서빙에서 자주 쓰이는 헤더들입니다.

바디의 데이터 형식을 알립니다.

Content-Type: application/json # JSON
Content-Type: multipart/form-data # 파일 업로드
Content-Type: image/jpeg # 이미지
Content-Type: text/event-stream # SSE 스트리밍

LLM 스트리밍 응답에는 text/event-stream을 사용합니다. Content-Type이 맞지 않으면 클라이언트가 바디를 올바르게 파싱하지 못합니다.

API 인증에 사용합니다. Bearer 토큰 방식이 가장 일반적입니다.

Authorization: Bearer hf_aBcDeFgHiJkL...
# FastAPI에서 헤더 읽기
from fastapi import Header, HTTPException
async def predict(authorization: str = Header(None)):
if not authorization or not authorization.startswith("Bearer "):
raise HTTPException(status_code=401, detail="인증 필요")
token = authorization.split(" ")[1]
...
헤더방향역할
Accept요청원하는 응답 형식 (application/json)
User-Agent요청클라이언트 식별
X-Request-Id양방향요청 추적용 UUID
Cache-Control양방향캐싱 동작 제어
X-RateLimit-Remaining응답남은 API 호출 횟수

X-로 시작하는 헤더는 비표준 커스텀 헤더입니다. OpenAI와 HuggingFace는 X-RateLimit-* 헤더로 요청 한도 정보를 제공합니다.

상태 저장: 쿠키, localStorage, sessionStorage

섹션 제목: “상태 저장: 쿠키, localStorage, sessionStorage”

HTTP는 기본적으로 무상태(stateless) 프로토콜입니다. 각 요청은 독립적이라 서버는 이전 요청을 기억하지 않습니다. 로그인 상태 유지 같은 기능을 위해 클라이언트 측에 상태를 저장합니다.

저장소만료서버 전송용량주 용도
쿠키설정 가능자동 (같은 도메인)~4KB세션 ID, 인증 토큰
localStorage영구수동~5MB사용자 설정, 캐시
sessionStorage탭 닫을 때수동~5MB임시 폼 데이터
# 서버 응답: 쿠키 설정
Set-Cookie: session_id=abc123; HttpOnly; Secure; SameSite=Strict
# 이후 브라우저 요청: 자동으로 쿠키 포함
Cookie: session_id=abc123

HttpOnly: JavaScript에서 쿠키 접근 불가 (XSS 방어). Secure: HTTPS 연결에서만 전송. SameSite=Strict: 다른 사이트에서 온 요청에는 쿠키 미포함 (CSRF 방어).

API 키 저장: localStorage (편리하지만 XSS에 취약)
세션 토큰: HttpOnly 쿠키 (보안상 권장)
추론 결과 캐시: localStorage (재사용 가능)
현재 업로드 중인 파일 상태: sessionStorage

React 개발 서버(http://localhost:5173)에서 FastAPI(http://localhost:8000)로 요청을 보내면 이런 오류를 만납니다.

Access to fetch at 'http://localhost:8000/predict' from origin
'http://localhost:5173' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.

브라우저는 동일 출처 정책(Same-Origin Policy) 을 따릅니다. 출처(Origin)는 스킴 + 호스트 + 포트의 조합입니다.

http://localhost:5173 ≠ http://localhost:8000 (포트 다름 → 다른 출처)
https://myapp.com ≠ https://api.myapp.com (호스트 다름 → 다른 출처)

다른 출처로 JavaScript fetch 요청을 보내면 브라우저가 차단합니다. 악성 사이트가 사용자의 쿠키를 이용해 다른 서버에 요청을 보내는 것을 막기 위한 보안 정책입니다.

Content-Type: application/json 같은 커스텀 헤더가 있는 요청은 브라우저가 먼저 OPTIONS 메서드 로 예비 요청(preflight)을 보냅니다.

OPTIONS /predict HTTP/1.1
Origin: http://localhost:5173
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, Authorization

서버가 이 preflight에 올바르게 응답해야 실제 POST 요청이 전송됩니다.

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=[
"http://localhost:5173", # 개발 환경
"https://myapp.com", # 프로덕션
],
allow_credentials=True,
allow_methods=["GET", "POST", "PUT", "DELETE"],
allow_headers=["Content-Type", "Authorization"],
)

allow_origins=["*"]는 모든 출처를 허용합니다. 개발 중에는 편하지만 프로덕션에서는 명시적으로 허용할 도메인을 지정해야 합니다.

중요한 점: CORS는 브라우저에서만 적용 됩니다. curl이나 Python requests로 보내는 요청은 CORS 제한을 받지 않습니다. 그래서 curl 테스트는 성공하는데 브라우저에서만 실패하는 상황이 발생합니다.

Terminal window
# 이건 성공: CORS 없음
curl -X POST http://localhost:8000/predict -d '{"text": "test"}'
# 브라우저 JS에서는 CORS 오류 발생 가능
fetch('http://localhost:8000/predict', { ... })
  • URL은 스킴://호스트:포트/경로?쿼리#프래그먼트 구조다. 프래그먼트는 서버로 전달되지 않는다.
  • Content-Type은 바디 형식을, Authorization은 인증을 담당하는 핵심 헤더다.
  • 쿠키는 서버가 설정하고 자동으로 전송된다. localStorage는 JavaScript가 직접 관리하고 서버로 자동 전송되지 않는다.
  • CORS는 브라우저의 동일 출처 정책에서 비롯된다. React(5173)에서 FastAPI(8000)를 호출하면 다른 포트라 CORS 오류가 발생한다.
  • FastAPI에서는 CORSMiddleware로 허용할 출처를 명시적으로 등록해 해결한다.
  • curl 테스트가 성공해도 브라우저에서 실패한다면 CORS 설정을 먼저 확인하라.