2025.11.14
example.com(프론트엔드)에서 api.example.com(백엔드)으로 인증 요청을 보낼 때:
domain: ".example.com" 설정 필요sameSite: "lax" 충분 (Cross-Site 아니므로)credentials: "include" 필수핵심: 쿠키 정책(브라우저)과 CORS(서버) 둘 다 맞춰야 성공..
Same-Site
├── Same-Origin
│ └── example.com → example.com
│ (protocol, domain, port 모두 일치)
│
└── Cross-Origin
└── example.com → api.example.com
(subdomain이 다름)
Cross-Site (무조건 Cross-Origin)
└── Cross-Origin
└── example.com → google.com
(완전히 다른 도메인)
IF Same-Site:
├─ Same-Origin 가능
└─ Cross-Origin 가능
IF Cross-Site:
└─ Cross-Origin 무조건
IF Same-Origin:
└─ Same-Site 무조건
IF Cross-Origin:
├─ Same-Site 가능 (subdomain만 다를 때)
└─ Cross-Site 가능 (완전히 다른 도메인)
| From | To | Site | Origin |
|---|---|---|---|
example.com | example.com | Same-Site | Same-Origin |
example.com | api.example.com | Same-Site | Cross-Origin |
https://example.com | http://example.com | Same-Site | Cross-Origin |
example.com | google.com | Cross-Site | Cross-Origin |
쿠키에 domain 옵션을 명시하지 않으면, 쿠키는 **설정된 정확한 호스트(subdomain 포함)**에만 유효!!
// example.com → example.com
cookieStore.set('token', jwt, {
// domain 생략 가능
path: "/",
sameSite: "strict", // 가장 엄격한 보안
secure: true,
httpOnly: true,
});
쿠키 전송 범위:
example.com → example.com사용 사례: Next.js API Routes, 모놀리식 아키텍처
// example.com → api.example.com
cookieStore.set('token', jwt, {
domain: ".example.com", // ⭐ 점(.) 필수! 모든 subdomain 공유
path: "/",
sameSite: "lax", // Same-Site이므로 충분
secure: true,
httpOnly: true,
maxAge: 30 * 24 * 60 * 60, // 30일
});
쿠키 전송 범위:
example.com → example.comexample.com → api.example.com (전송됨!)example.com → admin.example.com사용 사례: 마이크로서비스, API 서버 분리, SSO
// example.com → external.com
cookieStore.set('token', jwt, {
domain: ".example.com",
sameSite: "none", // ⚠️ 필수!
secure: true, // ⚠️ 필수!
httpOnly: true,
});
주의사항:
사용 사례: 외부 인증 제공자
1️⃣ CORS (서버 측 정책) → 서버: "이 origin 요청 받을까?"
2️⃣ Cookie Policy (브라우저 정책) → 브라우저: "이 쿠키를 보낼까?"
둘 다 통과해야 성공임..!
// 프론트엔드: example.com
// 백엔드: api.example.com
// 1️⃣ 쿠키 설정 (브라우저 정책)
cookieStore.set('token', jwt, {
domain: ".example.com", // ✅
sameSite: "lax", // ✅
secure: true,
httpOnly: true,
});
// 2️⃣ Apollo Client 설정
const httpLink = createHttpLink({
uri: 'https://api.example.com/v1/graphql',
credentials: 'include', // ✅ 필수!
});
// 3️⃣ 백엔드 CORS 설정 (서버 정책)
// api.example.com 환경 변수
CORS_ORIGIN: "https://example.com" // ✅
// 또는 Hasura의 경우
HASURA_GRAPHQL_CORS_DOMAIN: "https://example.com"
결과: 🎉 요청 성공! 인증 성공!
// 1️⃣ 쿠키 설정: ✅ OK
cookieStore.set('token', jwt, {
domain: ".example.com",
sameSite: "lax",
});
// 2️⃣ CORS 설정: ❌ 없음
// api.example.com에 CORS 설정 안 함
브라우저 콘솔 에러:
Access to fetch at 'https://api.example.com/v1/graphql'
from origin 'https://example.com' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present.
결과: 🔴 요청 자체가 차단됨
// 1️⃣ 쿠키 설정: ❌ domain 없음
cookieStore.set('token', jwt, {
// domain 없음 → example.com만
sameSite: "lax",
});
// 2️⃣ CORS 설정: ✅ OK
HASURA_GRAPHQL_CORS_DOMAIN: "https://example.com"
Network 탭:
Request Headers:
(Cookie 헤더 없음) ❌
Response:
200 OK ✅
{ "x-hasura-role": "guest" } ⚠️ 인증 실패
결과: 요청은 성공하지만 인증 실패 ㅜㅜ
Cross-Origin 인증 요청이 성공하려면:
백엔드 (api.example.com)
✅ CORS 설정
├─ Access-Control-Allow-Origin: https://example.com
├─ Access-Control-Allow-Credentials: true
├─ Access-Control-Allow-Methods: POST, GET, OPTIONS
└─ Access-Control-Allow-Headers: Content-Type, Authorization
프론트엔드 (example.com)
✅ 쿠키 설정
├─ domain: ".example.com"
├─ sameSite: "lax"
├─ secure: true
└─ httpOnly: true
✅ HTTP Client 설정
└─ credentials: "include" (fetch API)
└─ credentials: "include" (Apollo Client)
하나라도 빠지면 실패입니다..
Site ≠ Origin
쿠키는 domain 옵션이 핵심
domain: ".example.com" → 모든 subdomain 공유CORS는 별개 정책
실무에서는 Same-Site/Cross-Origin이 일반적
domain: ".example.com" + sameSite: "lax" + CORS 설정CORS, Site, Origin, 쿠키 정책은 각각 다른 보안 레이어!
개념을 명확히 이해하고, 각 레이어를 올바르게 설정해야 Cross-Origin 인증이 성공!