[Next.js x NestJS] 프론트엔드와 백엔드의 도메인 분리, 깨부순 아키텍처 오해들
The Problem: '프론트와 백엔드 분리'가 가져온 보안과 네트워크의 함정
프로덕션 레벨에서 두 개의 다른 도메인을 연동하려 하자, 제가 얕게 알고 있던 지식들이 연쇄적으로 문제를 일으켰습니다.
- 쿠키와 도메인의 엇갈림: CORS에서
credentials: true만 설정하면 백엔드가 구워준 쿠키를 브라우저가 알아서 저장할 줄 알았습니다. 하지만 서로 다른 도메인(A.com ↔ B.com) 간의 쿠키 설정은 최신 브라우저에서 '써드 파티 쿠키(Third-Party Cookie)' 추적으로 간주되어 강제 차단되었습니다. - 개발 환경(npm start)의 배신: React의 로컬 개발 환경에서 사용하던 Proxy 기능이, 배포 후(SPA 빌드 결과물)에는 동작하지 않았습니다. 개발 환경의 프록시는 Webpack Dev Server가 대신해 주던 기능일 뿐, 빌드된 정적 파일 덩어리에는 서버 기능이 전무했습니다.
- 무의미한 클라이언트 암호화 시도:
HttpOnly를 설정해도 개발자 도구에는 쿠키가 보이니 보안이 약하다고 착각했습니다. 그래서 클라이언트에서 대칭키로 암호화를 시도하려 했고, 프론트엔드.env파일에 비밀키를 두면 안전할 거라 믿었습니다. 하지만 프론트엔드의 환경변수 처리는 단순 '단어 치환'일 뿐이라 브라우저 소스 보기에 키가 그대로 노출되는 치명적인 문제가 있었습니다.
The Solution: 'BFF 아키텍처 도입'과 '웹 표준 보안'의 적용
이 모든 네트워크/보안 이슈를 해결하기 위해, 프론트엔드 단에도 진짜 서버 역할을 해줄 계층을 두고 보안의 책임을 재분배했습니다.
1. Next.js를 활용한 BFF (Backend For Frontend) 및 프록시 구축
클라이언트가 백엔드(A.com)를 직접 찌르는 대신, Next.js(B.com)의 Rewrites 기능을 활용해 프록시 계층을 구축했습니다. 브라우저는 자신과 같은 도메인인 B.com/api로 요청을 보내고, Next.js 서버 런타임이 뒤단에서 백엔드로 토스해 줍니다. 이를 통해 써드 파티 쿠키 차단을 우회하고, 쿠키를 퍼스트 파티(First-Party) 로 안전하게 인식시켰습니다.
2. 브라우저 생태계에 맞는 올바른 보안(HttpOnly, HTTPS) 통제
어설픈 클라이언트 암호화를 과감히 버렸습니다. 브라우저는 '공공장소'이므로 코드를 숨기는 것은 불가능합니다.
대신, 토큰은 무조건 자바스크립트 접근을 막는 HttpOnly 쿠키로 서버에서 구워주어 XSS 공격을 원천 차단했습니다. 또한 네트워크 스니핑은 전송 구간을 통째로 암호화하는 HTTPS 프로토콜에 온전히 맡겼으며, 진짜 민감한 비밀키는 오직 백엔드나 Next.js의 Server API 메모리에서만 다루도록 수정했습니다.
The Result & Retrospective
이러한 설계 과정을 거치며
- Next.js (B.com) 는 API 중계와 퍼스트 파티 쿠키 형성을 담당하는 BFF 역할을 수행하고,
- NestJS (A.com) 는 신뢰할 수 있는 서버의 요청만 받아 철저한 권한 검증을 수행하는 안전한 아키텍처를 완성했습니다.
이번 경험을 통해 "프론트엔드 생태계 역시 서버와 네트워크 환경(HTTP 통신, 브라우저 보안 정책, 빌드 라이프사이클)에 대한 이해 없이는 반쪽짜리" 라는 것을 뼈저리게 느꼈습니다. 이제는 "왜 에러가 나지?"라며 단편적인 구글링에 의존하기보다, "어떤 원리로 동작하는가?" 를 먼저 묻고 기반 지식을 바탕으로 견고한 구조를 설계하는 아키텍트로 성장해 나가겠습니다.