(4편) BFF 패턴 & Proxy Handler — Next.js를 API 게이트웨이로 ← 현재 글
(4) BFF 패턴 & Proxy Handler — Next.js를 API 게이트웨이로
앞의 세 편에서 CORS, 쿠키 보안, withCredentials의 복잡한 조건들을 살펴봤다.
"이 모든 걸 다 맞춰야 동작한다고 생각하면, 너무 복잡한 것 같다."
맞다. 복잡하다.
그래서 Next.js는 이 복잡함을 아키텍처 수준에서 해결하는 방법을 제공한다.
바로 BFF(Backend for Frontend) 패턴과 Proxy 핸들러다.
이 글에서는 Next.js를 단순 프론트엔드 서버가 아닌 API 게이트웨이로 활용하는 방법과,
각 기능의 책임과 적합한 사용 시나리오를 정리합니다.
예시는 Next.js 앱을 기준으로 들지만, **"프론트엔드 서버를 BFF 레이어로 세운다"**는 관점은
다른 SPA 프레임워크(React, Vue, Svelte 등)에서도 그대로 응용할 수 있습니다.
1. BFF(Backend for Frontend) 패턴이란
BFF는 "프론트엔드를 위한 전용 백엔드"라는 의미입니다.
프론트엔드와 실제 백엔드 API 사이에 중간 레이어를 두는 아키텍처 패턴입니다.
기존 구조의 문제점
[브라우저] ─────────────► [백엔드 API 서버]
Client Component api.example.com
axios로 직접 호출
문제:
- CORS 설정 복잡함
- API Key가 클라이언트에 노출됨(NEXT_PUBLIC_)
- 여러 API를 조합하는 로직이 클라이언트에 존재할 수 있음
- 인증 토큰이 브라우저에 노출될 수 있음
중간에 BFF 레이어로써 작용하는 Next.js 서버가 들어오는 것만으로 CORS 문제가 구조적으로 사라질 수 있습니다.
1) 브라우저 ↔ Next.js: Same-Origin → CORS 없음
브라우저는 자신이 속한 Next.js 서버(my-app.com)와 통신하므로 Same-Origin이므로 CORS가 없습니다.
2) Next.js ↔ 외부 API: 서버 간 통신 → CORS 없음
실제 외부 API 호출은 서버에서 서버로 일어나므로 역시 CORS가 없습니다.
② 민감한 정보 은닉
서버를 거치기에 API Key, 내부 서버 주소, 토큰 등이 클라이언트에 전혀 노출되지 않습니다.
ts
// ✅ API Key가 서버에만 존재const res =awaitfetch("https://api.example.com/data",{ headers:{ Authorization:`Bearer ${process.env.API_SECRET}`,// process.env.API_SECRET → 서버 전용, 클라이언트 번들에 포함 안 됨},});
③ 데이터 집계 및 변환
클라이언트가 필요한 형태로 여러 API 응답을 조합하거나 불필요한 필드를 제거할 수 있습니다.
// app/api/user/route.ts (예시)import{ cookies }from"next/headers";exportasyncfunctionGET(){const cookieStore =awaitcookies();const sessionId = cookieStore.get("session_id")?.value;if(!sessionId){return Response.json({ error:"Unauthorized"},{ status:401});}// 서버 간 통신 → CORS 없음, API Key 안전하게 사용 가능const backendResponse =awaitfetch(`${process.env.INTERNAL_API_BASE_URL}/user`,{ headers:{"X-Session-Id": sessionId,"X-API-Key": process.env.API_SECRET!,},});if(!backendResponse.ok){return Response.json({ error:"Failed to fetch user"},{ status: backendResponse.status });}const user =await backendResponse.json();// 민감한 필드는 제거하고 응답return Response.json({ id: user.id, name: user.name, email: user.email,});}
살펴보시면, 직접 클라이언트에서 외부 API 서버에 요청을 할 때의 설정에 비해, 훨씬 간단합니다.
클라이언트에서 SameSite: 'lax'와 같은 옵션을 사용할 수 있습니다.
브라우저와 Next.js 서버가 Same-Origin이기 때문에 CORS 이슈가 없습니다.
3편에서 설명한 SameSite=None; Secure의 복잡한 조건이 필요 없습니다.
(추가) 기존 클라이언트 → API 구조에서 BFF로 옮길 때, 백엔드에 요청해야 할 쿠키 옵션들
지금까지 예시는 Next.js의 Route Handler가 직접 쿠키를 설정하는 구조였습니다.
하지만 현실 세계에서는 이미 백엔드(API 서버)가 Set-Cookie로 세션/토큰을 내려주고,
프론트는 단순히 클라이언트 → API 서버로 직접 붙던 구조에서 출발하는 경우가 훨씬 많습니다.
이런 상황에서 **“이제는 프론트가 BFF(Next.js)를 거쳐서 API 서버로 가게 바꾸자”**라고 결정하면,
브라우저 입장에서는 라우팅 구조와 도메인이 바뀌기 때문에 쿠키 설정도 함께 정리해 줘야 합니다.
특히, 아래 옵션들은 백엔드 팀과 합의해서 변경을 요청해야 할 가능성이 높은 부분입니다.
저의 최근 경험입니다.
1) Domain — 브라우저가 실제로 붙는 도메인 기준으로
과거 구조
브라우저가 api.example.com에 직접 요청을 보내고, API 서버가 Set-Cookie를 내려줬을 수 있습니다.
이때는 Domain=api.example.com 이거나, 아예 생략해서 “해당 호스트 한정”으로 두어도 문제가 없습니다(저는 생략했으며, 오히려 더 안전할 수 있습니다).
BFF 도입 후 구조
브라우저는 이제 app.example.com / my-app.com 처럼 BFF가 올라간 도메인으로만 요청을 보냅니다.
쿠키도 이 도메인 기준으로 설정되어야, 브라우저가 BFF 요청에 쿠키를 붙여서 보냅니다.
실무에서 백엔드 팀에 요청할 때는 보통 이렇게 정리해서 전달하면 좋습니다.
프론트/BFF와 동일 호스트에서만 쓰는 경우
예: 서비스가 app.example.com 하나에서만 동작하고, BFF도 여기에 올라가 있다면
Domain=app.example.com 또는 Domain 옵션을 생략(호스트 한정) 해도 됩니다.
여러 서브도메인에서 공유해야 하는 경우
예: app.example.com, admin.example.com 등 여러 앱에서 같은 세션을 쓰고 싶다면
Domain=.example.com 처럼 루트 도메인 기준으로 지정합니다.
중요한 포인트는 하나입니다.
여전히 Domain=api.example.com으로 고정해 두면,
브라우저가 app.example.com(BFF)으로 요청을 보낼 때 그 쿠키를 전송하지 않을 수 있다.
그래서 BFF로 마이그레이션할 때는,
“이제 쿠키를 기준으로 인증을 처리하는 주체는 api.example.com이 아니라
브라우저가 실제로 붙는 app.example.com(BFF)다.”
“그러니 쿠키의 Domain도 그에 맞춰 조정해 달라.”
라는 메시지를 백엔드/인프라 쪽에 꼭 전달해 주는 게 좋습니다.
2) Path — BFF 경로 구조에 맞게 풀어주기
기존에는 API 서버의 라우트 구조에 맞춰 Path=/api/auth 같이 좁은 범위로 설정되어 있을 수 있습니다.
하지만 BFF를 도입하면, 브라우저 입장에서의 경로 구조가 바뀝니다.
혹은 https://app.example.com/dashboard/... 와 같은 일반적인 페이지 접근 경로와 같이 인증과 전혀 다른 경로일 수도 있음(하지만 쿠키를 전달해야 함)
이때 가장 안전한 기본값은 Path=/ 입니다.
특별히 경로를 제한해야 할 이유가 없다면,
“브라우저가 BFF에 보내는 모든 요청에 세션 쿠키가 붙어야 한다”는 쪽이 보통 더 자연스럽고,
그럴 경우 Path=/로 완화해 달라고 요청하는 편이 좋습니다.
특정 엔드포인트에서만 쿠키가 전송되어야 하는 정당한 이유(보안 정책 등)가 있다면
그 정책을 BFF 경로 구조에 맞게 다시 설계해야 합니다.
3) Secure — 운영은 무조건 유지, 개발 환경 전략은 분리
운영 환경에서는 고민할 게 없습니다. HTTPS + Secure 를 유지하는 것이 정석입니다.
다만, BFF를 도입하면 로컬/개발 환경에서의 개발 경험이 살짝 꼬일 수 있습니다.
브라우저는 표준상 Secure 쿠키를 HTTPS 요청에만 보내지만,
대부분의 현대 브라우저는 개발 편의를 위해 http://localhost 를 예외적으로 “secure context”로 취급합니다(이전 글 참고).
그래서 http://localhost:3000 에서도 Secure 쿠키가 전송되는 경우가 많습니다.
하지만 실제로 문제되는 구간은 보통 로컬이 아닌 개발/스테이징 도메인입니다.
예: http://dev.example.com 처럼 HTTPS가 아닌 도메인,
혹은 https://dev.example.com 이지만 인증서가 불완전하거나, 프록시/환경에 따라 브라우저가 신뢰하지 않는 경우 등.
이런 환경을 설계할 때 선택지는 대략 세 가지 정도가 있습니다.
로컬/개발 환경도 가능하면 HTTPS로 띄우는 구조를 만든다. (예: 로컬 리버스 프록시, mkcert 등)
https://dev.example.com 같은 개발용 도메인에서 테스트하고, 항상 유효한 HTTPS만 사용한다.
정말 불가피할 때만 **“개발용 프로파일에서만 Secure를 끈다”**는 옵션을 둔다.
문서/팀 합의 차원에서는 이렇게 정리해 두면 좋습니다.
운영(Production): Secure 항상 켜둔다.
개발/스테이징:
“우리는 로컬/스테이징에서 어떤 방식으로 HTTPS를 확보할 것인가?”
“정말 안 되면 개발용 설정에서만 Secure를 끌 것인가?”를 명시적으로 합의.
4) HttpOnly — 그대로 유지 (오히려 BFF와 더 잘 맞음)
인증/세션 쿠키는 HttpOnly를 유지하는 쪽이 훨씬 안전합니다.
BFF 패턴의 장점도 사실 여기서 나옵니다.
프론트 JS는 쿠키 값을 직접 읽지 않습니다.
대신 서버(Next.js BFF) 가 cookies() API를 통해서만 쿠키를 읽고,
그 정보를 가지고 백엔드 API를 호출하거나 응답을 가공합니다.
그래서 백엔드 팀에게는 이렇게 요청하면 됩니다.
“인증/세션 쿠키의 HttpOnly는 기존처럼 그대로 유지해 달라.”
“토큰을 프론트 JS가 읽어서 로컬스토리지 등에 넣는 패턴은 지양하고,
BFF 레이어에서만 쿠키를 읽도록 설계하겠다.”
5) SameSite — 인증/리다이렉트 플로우에 맞춰 선택
SameSite는 **“이 쿠키를 크로스 사이트 요청에도 보낼 것인가?”**를 결정하는 옵션입니다.
BFF 도입 후 구조에서는 기본적으로 브라우저 ↔ BFF는 Same-Origin이라 비교적 단순하지만,
외부 OAuth/SSO, 결제 리다이렉트 등이 있으면 이야기가 달라집니다.
덧붙여서, BFF를 도입했다고 해서 보안 위협 자체가 사라지는 것은 아니라는 점도 같이 기억해 두면 좋습니다.
쿠키 기반 세션을 유지하는 한, 특히 SameSite=None 을 쓰는 경우에는 CSRF 위협 모델이 여전히 존재합니다.
BFF는 액세스 토큰을 프론트 JS로부터 숨길 수 있지만, XSS가 발생하면 BFF를 향한 요청 자체를 위조할 수 있기 때문에 XSS 방어는 별도의 과제로 남습니다.
즉, BFF는 주로 CORS/비밀 노출/엔드포인트 복잡성을 줄이는 역할이고, CSRF·XSS 같은 웹 보안 기본기는 여전히 그대로 챙겨야 합니다.
4. Hybrid BFF — Server Component + Route Handler
방금까진 완전한 handler 역할의 집중된 예시를 보았습니다.
이제는 Server Component와 Route Handler를 상황에 맞게 조합할 수 있는 예시를 보겠습니다. Next.js는 이 둘을 조합할 수 있는 유연한 아키텍처를 제공합니다.
Server Component에서 직접 데이터 패칭
ts
// app/dashboard/page.tsx (Server Component, 예시)import{ cookies }from"next/headers";exportdefaultasyncfunctionDashboardPage(){const cookieStore =awaitcookies();const sessionId = cookieStore.get("session_id")?.value;if(!sessionId)redirect("/login");// Route Handler를 거치지 않고 직접 내부 API 호출// 서버 컴포넌트 → 서버 간 통신 → CORS 없음const user =awaitfetch(`${process.env.INTERNAL_API_BASE_URL}/user`,{ headers:{"X-Session-Id": sessionId },// Next.js의 fetch 캐싱 설정 next:{ revalidate:60},// 60초마다 갱신}).then((res)=> res.json());return<Dashboard user={user}/>;}
주의: 왜 Server Component에서 Route Handler를 호출하면 안 될까요?
공식 문서에서 명시적으로 주의를 주는 부분입니다.
❌ 나쁜 패턴: 금지는 아니지만, 최대한 피하는 것이 좋습니다.
Server Component → fetch('/api/user') → Route Handler → 외부 API
추가 HTTP 왕복 발생:
- Server Component에서 자기 서버의 Route Handler로 HTTP 요청
- 같은 서버 안에서 불필요한 네트워크 레이어 추가(같은 서버에서 또 다시 요청을 보내는 것)
- 빌드 타임에는 HTTP 서버가 실행 중이지 아닐 수 있으므로 빌드 자체가 실패할 수도 있음
- 무엇보다, Next.js의 Server Component 설계 철학인 'Server Component는 서버에서 직접 데이터를 가져온다'라는 점에 어긋납니다.
✅ 좋은 패턴:
Server Component → 직접 fetch(내부 API URL 또는 DB)
Route Handler → 클라이언트 요청을 받아서 → 내부 API
언제 어떤 것을 쓸까
데이터를 어디서 가져오는가?
초기 페이지 렌더링에 필요한 데이터
→ Server Component에서 직접 패칭
→ SEO에 유리, 초기 로딩 빠름
→ 빌드 타임 캐싱 가능
사용자 인터랙션에 반응하는 데이터 (버튼 클릭, 폼 제출 등)
→ Client Component + axios → Route Handler
→ 동적으로 필요할 때만 호출
주기적으로 업데이트가 필요한 데이터
→ Client Component + SWR/React Query → Route Handler
→ 폴링, 실시간 업데이트
5. Proxy 핸들러 — 요청의 첫 번째 관문
Next.js 16 변경사항
Next.js 16에서는 proxy.ts가 기존 middleware.ts를 대체하는 새로운 컨벤션으로 도입되었습니다.(난 15 사용 중임...)
이름만 바뀐 것이 아니라, 네트워크 경계(프록시 레이어)를 더 명확히 표현하는 역할을 하고, 기본적으로 Node.js 런타임에서 실행됩니다. middleware.ts는 Edge 용도로는 여전히 동작하지만, 점차 proxy.ts 중심으로 옮겨가라는 메시지에 가깝습니다.
(이 글에서는 앞으로 proxy.ts 기준으로 설명하지만, Next 15에서는 middleware.ts / export function middleware() 패턴으로 읽어도 됩니다.)
3단계의 레이어에 대해 설명했듯이, Proxy는 모든 요청이 라우트에 도달하기 전에 실행되는 코드입니다.
일반적으로 페이지든 API든 정적 파일이든, 요청이 들어오면 Proxy를 먼저 통과하도록 설정할 수 있지만,
실제 서비스에서는 matcher로 어떤 경로들에만 적용할지를 신중하게 제한해 주는 편이 좋습니다.
ts
// proxy.ts (프로젝트 루트 또는 src/ 하위)import{ NextResponse }from"next/server";importtype{ NextRequest }from"next/server";exportfunctionproxy(request: NextRequest){// 모든 /dashboard 경로 요청이 여기를 통과const sessionId = request.cookies.get("session_id");if(!sessionId){// 로그인 페이지로 리다이렉트return NextResponse.redirect(newURL("/login", request.url));}// 다음 단계(Route Handler 또는 Page)로 진행return NextResponse.next();}// 어떤 경로에서 실행할지 지정exportconst config ={ matcher:["/dashboard/:path*","/admin/:path*"],// next.js 공식 문서 제안: api, _next/static, _next/image, favicon.ico 제외한 모든 경로// matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],};
Proxy에서 할 수 있는 것들
① 인증 체크 후 리다이렉트
위 예시처럼 로그인 여부를 확인하고 미인증 사용자를 로그인 페이지로 보낸다.
모든 보호된 페이지마다 인증 체크 코드를 넣지 않아도 된다.
② 요청 헤더 변조
ts
exportfunctionproxy(request: NextRequest){const requestHeaders =newHeaders(request.headers);// 요청에 사용자 정보를 헤더로 추가 (downstream에서 활용)const userId =getUserIdFromSession(request.cookies.get("session_id")); requestHeaders.set("X-User-Id", userId ??"");return NextResponse.next({ request:{ headers: requestHeaders },});}
exportfunctionproxy(request: NextRequest){const country = request.geo?.country ??"US";if(country ==="KR"&&!request.nextUrl.pathname.startsWith("/ko")){return NextResponse.redirect(newURL(`/ko${request.nextUrl.pathname}`, request.url));}return NextResponse.next();}
Proxy에서 하면 안 되는 것들
❌ 느린 데이터 패칭
Proxy는 설정에 따라 매우 많은 요청마다 실행된다.
DB 조회나 외부 API 호출을 여기서 하면 해당 구간을 지나는 모든 요청이 느려진다.
❌ 무거운 연산
같은 이유로 성능에 민감한 작업은 금물.
❌ 완전한 인증/인가 시스템
Proxy는 낙관적 체크(Optimistic Check)용으로만 사용하는 편이 좋다.
쿠키가 있는지 정도는 확인하지만, 쿠키가 실제로 유효한지 DB에서 검증하는 건
Route Handler나 Server Component에서 해야 한다.
예: '매 요청마다 유저 인증 여부 확인해야지' 한다면, 쿠키 여부 정도만 체크하고, 실제 유저를 DB에서 조회하는 것은 지양하자. 이 부분은 Server Component나 Route Handler에서 처리하자.
6. 전체 아키텍처 — 3단계 레이어의 역할 분담
지금까지 다룬 내용을 하나의 다이어그램으로 정리하면 다음과 같다.
mermaid
다이어그램 정의(mermaid):
flowchart TD
A[Client Request]
subgraph L1["Layer 1. Proxy (proxy.ts)"]
B["쿠키 존재 여부로 인증 낙관적 체크<br/>
미인증 시 /login 리다이렉트<br/>
A/B 테스트 라우팅<br/>
요청 헤더 변조"]
end
subgraph L2A["Layer 2a. Page (page.tsx) - Server Component"]
C["내부 API 호출<br/>
초기 HTML 렌더링<br/>
SEO 처리"]
end
subgraph L2B["Layer 2b. Route Handler (route.ts)"]
D["클라이언트 요청 처리<br/>
외부 API 프록시<br/>
쿠키 설정 / 삭제<br/>
데이터 집계 / 변환"]
end
subgraph L3["Layer 3. Backend API Server"]
E["서버 간 통신<br/>
CORS 없음"]
end
A --> B
B -->|인증 통과| C
B -->|API 요청| D
C --> E
D --> E
각 레이어의 책임 요약
레이어
파일
역할
특징
Proxy
proxy.ts
요청 필터링, 리다이렉트
빠르고 가벼운 작업만
Server Component
page.tsx
초기 데이터 패칭 + 렌더링
직접 내부 API 호출
Route Handler
route.ts
클라이언트 API 엔드포인트
BFF 역할, 쿠키 조작 가능
Client Component
*.tsx
동적 UI, 사용자 인터랙션
axios로 Route Handler 호출
실제 운영 관점에서 보면, BFF를 도입하는 것은 다음과 같은 트레이드오프도 함께 가져옵니다. 그래서 무작정 BFF로 모든 요청을 처리하는 것은 좋지 않습니다.
요청 한 번당 한 홉이 추가되므로, 레이턴시와 서버 비용을 함께 고려해야 합니다.
Next.js의 fetch 캐시, revalidate 옵션, CDN 등을 활용해 캐싱 전략을 잘 짜지 않으면 BFF 레이어에서 병목이 생길 수 있습니다.
백엔드 API 호출과 BFF 응답을 함께 추적할 수 있도록, 로그/트레이싱 상관관계 ID를 설계해 두면 운영·디버깅이 훨씬 수월합니다.
권한 모델은 여전히 백엔드에서 최종 결정하고, BFF는 “대리 호출자”라는 사실을 명확히 해 두는 편이 좋습니다.
7. 실전 시나리오 — 로그인 플로우 전체 흐름
거의 다 왔습니다.
지금까지 배운 내용을 종합해 로그인부터 인증된 API 호출까지의 전체 흐름을 예시 시나리오로 정리해보겠습니다.
[1] 로그인 페이지 접근
브라우저 GET /login
→ Proxy: 쿠키 없음 → 통과 (로그인 페이지는 matcher에서 제외)
→ page.tsx 렌더링
[2] 로그인 폼 제출
브라우저 POST /api/auth/login (axios, Same-Origin)
→ Proxy: 통과
→ Route Handler /api/auth/login
→ 백엔드 인증 API 호출 (서버 간 통신)
→ 성공 시 Set-Cookie: session_id=abc; HttpOnly; Secure; SameSite=Lax
→ 브라우저가 쿠키 저장
[3] 대시보드 접근
브라우저 GET /dashboard
→ Proxy: session_id 쿠키 존재 확인 → 통과
→ page.tsx (Server Component)
→ cookies()로 session_id 읽기
→ 내부 API 직접 호출 (서버 간 통신)
→ 데이터 패칭 후 HTML 렌더링
[4] 대시보드에서 데이터 갱신 (버튼 클릭)
브라우저 PUT /api/user/profile (axios, Same-Origin)
→ Proxy: 쿠키 존재 확인 → 통과
→ Route Handler /api/user/profile
→ cookies()로 session_id 읽기
→ 백엔드 API 호출 + 응답 반환
[5] 세션 만료 후 갱신
브라우저 axios 요청 → 401 응답
→ 인터셉터: /api/auth/refresh 호출
→ Route Handler: refresh_token 쿠키로 새 session_id 발급
→ 새 쿠키 설정
→ 원래 요청 재시도
8. 정리 — 시리즈 마무리
네트워크 이야기를 했다가, Next.js 아키텍처 이야기로 넘어가네요. 읽느라 고생하셨습니다.
마지막으로 이 시리즈에서 다룬 내용의 큰 그림을 정리해봅시다.
1편: CORS의 뿌리
브라우저가 강제하는 SOP가 있고, CORS는 그 예외를 허용하는 메커니즘입니다.
CORS 에러는 서버가 허용 헤더를 보내지 않아서 브라우저가 차단하는 것입니다.
2편: 쿠키 보안
HTTP의 Stateless 문제를 쿠키가 해결합니다.
HttpOnly는 XSS, Secure는 MITM, SameSite는 CSRF를 방어합니다.
각 속성은 특정 공격 시나리오에 대한 방어막입니다.
3편: 크로스오리진 쿠키 인증
브라우저는 크로스오리진 요청에 기본적으로 쿠키를 포함하지 않습니다. withCredentials: true와 서버의 Access-Control-Allow-Credentials: true,
그리고 SameSite=None; Secure가 모두 맞아야 동작합니다.
와일드카드 Origin과 credentials는 함께 사용할 수 없습니다.
4편: Next.js 아키텍처와 BFF 패턴
BFF 패턴으로 CORS 문제를 구조적으로 해결합니다.
Proxy는 요청 필터링, Route Handler는 BFF API, Server Component는 초기 렌더링에 사용합니다.
각 레이어의 책임을 명확히 하면 복잡한 인증 플로우도 깔끔하게 구현됩니다.
9. BFF 도입 체크리스트 (실전용 메모)
마지막으로, 실제 프로젝트에서 BFF를 도입하거나 마이그레이션할 때 체크해 보면 좋은 항목들을 간단히 정리해 둡니다.
환경 변수 이름을 통일했는가?
예: 내부 API 베이스 URL을 INTERNAL_API_BASE_URL 하나로 통일해서 Server Component와 Route Handler에서 함께 사용.
Route Handler의 fetch에 캐싱 정책을 명시했는가?
cache, next: { revalidate } 등을 통해, 언제까지 캐시/언제 다시 패칭할지 의도를 드러낸다.
Proxy/미들웨어의 matcher가 과도하게 넓지 않은가?
/api, /_next/static, /_next/image 등은 기본적으로 제외하고, 보호가 필요한 경로 위주로 한정.
쿠키의 Domain/Path가 “브라우저가 실제로 붙는 호스트” 기준으로 설계되어 있는가?
기존 api.example.com 기준 설정이 남아 있지 않은지 다시 한 번 점검.
OAuth/결제 리다이렉트가 있다면 SameSite=None; Secure + CSRF 방어 전략을 함께 설계했는가?
Server Component가 자기 서버의 /api/...를 거치지 않고, 가능한 한 직접 내부 API/DB를 호출하도록 정리했는가?
이 정도만 점검해도, “클라이언트에서 온갖 CORS/쿠키 설정으로 싸우던 것”보다 훨씬 예측 가능한 구조로 정리할 수 있습니다.
물론 이 글이 제안하는 것은 무조건적인 정답은 아닐 수 있습니다.
하지만 이 시리즈를 통해서 여러분들이 몰랐거나 놓쳤던 부분을 다시 한 번 생각해보고, 현재 상황에 맞게 적용해보는 계기가 되었으면 좋겠습니다.