1. 개요
레거시 어드민 시스템에서 신규 어드민 시스템으로 마이그레이션을 진행하면서 브라우저의 보안 정책과 관련된 예상치 못한 문제에 직면했습니다.
신규 어드민은 example.co.kr 도메인에 배포되었고, API 서버는 기존과 동일한 api-server.net 도메인을 사용하고 있었습니다.
기존 레거시 시스템의 private API를 호출하기 위해서는 사용자 로그인을 통해 인증 정보를 쿠키에 저장하고, 이후 private API 호출 시 해당 쿠키가 자동으로 전송되어 인증이 처리되는 방식을 사용해야 했습니다.
백엔드 팀에서 제안한 방식은 Google OAuth 로그인 완료 후 내부 login API를 호출하여 인증 쿠키를 설정하고, 후속 어드민 API 호출에서 이 쿠키가 자동으로 포함되어 전송되길 기대했습니다.
그러나 실제 구현 과정에서 크로스 도메인 환경으로 인해 쿠키가 설정되지 않는 문제가 발생했고, 이로 인해 브라우저별로 다른 동작을 보이는 예상치 못한 이슈에 직면하게 되었습니다.
2. 문제 상황
개발을 진행하던 중 특이한 현상을 발견했습니다. Firefox에서는 모든 기능이 정상적으로 작동하는데, Chrome에서는 로그인 후 API 호출이 실패하는 것이었습니다.
동일한 코드임에도 불구하고 브라우저에 따라 다른 결과가 나타났습니다.
// example.co.kr에서 api-server.net API 호출
fetch("https://api-server.net/admin/users", {
method: "GET",
});
// Chrome: 401 Unauthorized
// Firefox: 200 OKChrome 개발자 도구의 Network 탭을 통해 원인을 분석한 결과, Request Headers에서 Cookie 헤더가 누락되어 있음을 확인했습니다.
하지만 Application 탭에서는 쿠키가 분명히 존재하고 있었고, 쿠키의 Domain 속성이 example.co.kr로 설정되어 있었습니다.
쿠키는 설정된 도메인에서만 자동으로 전송된다는 브라우저의 기본 보안 정책이 적용되고 있었습니다. 이를 통해 쿠키의 도메인 바인딩 개념을 이해하게 되었습니다.
3. 근본 원인 분석
Same-Origin Policy(SOP)와 서드파티 쿠키 정책
문제를 더 깊이 분석한 결과, 브라우저의 가장 기본적인 보안 메커니즘인 Same-Origin Policy가 작동하고 있음을 확인했습니다.
Origin은 Protocol + Domain + Port의 조합으로 정의되며, https://example.co.kr:443와 https://api-server.net:443는 서로 다른 Origin으로 판단됩니다.
브라우저의 기본 정책은 도메인이 다르면 쿠키 설정과 공유를 원천적으로 차단하는 것입니다. example.co.kr에서 api-server.net으로의 API 호출은 서드파티 쿠키로 판단되어 브라우저가 차단하고 있었습니다.
이는 사용자 개인정보 보호를 위한 서드파티 쿠키 차단 정책 때문입니다.
credentials: 'include' 옵션의 실제 기능
가장 혼동이 있었던 부분이 바로 credentials: 'include' 옵션의 실제 의미였습니다.
처음에는 이 옵션이 '새로운 쿠키를 설정한다'는 의미로 이해했으나, 실제로는 이미 존재하는 쿠키를 요청에 포함하여 전송한다는 의미였습니다.
// 잘못된 이해
fetch("https://api-server.net/login", {
credentials: "include", // 이 옵션으로 쿠키 설정이 가능할 것으로 예상
});
// 실제 의미
fetch("https://api-server.net/user-info", {
credentials: "include", // api-server.net 쿠키가 이미 있다면 요청에 포함
});
// 하지만 처음에는 api-server.net 쿠키가 존재하지 않음SameSite 정책과 쿠키 보안 설정
문제 해결 과정에서 쿠키의 보안 설정에 대해서도 상세히 학습하게 되었습니다.
Secure속성은HTTPS에서만 쿠키를 전송하도록 하는 설정이며SameSite정책은 언제 쿠키를 전송할지를 결정하는 중요한 설정입니다.SameSite정책은 3가지 값을 가질 수 있습니다.Strict는 같은 사이트에서만 쿠키를 전송하며 다른 도메인에서 오는 모든 요청을 차단합니다.Lax는 링크 클릭은 허용하지만 AJAX나 POST 요청은 차단하며None은 모든 요청에서 쿠키 전송을 허용합니다.
- 브라우저별 기본값에서 차이가 발생했습니다.
Chrome은SameSite=Lax를 기본값으로 사용하여 AJAX 요청 시 크로스 도메인 쿠키를 엄격하게 차단했고,Firefox는 일부 버전에서SameSite=None을 기본값으로 사용하여 크로스 도메인 쿠키를 허용했습니다.
// Chrome 기본값 시나리오 (현재 상황)
Set-Cookie: session=abc123; SameSite=Lax
// → 링크 클릭은 허용, AJAX는 차단
// 크로스 도메인 허용을 위한 이론적 설정
Set-Cookie: session=abc123; SameSite=None; Secure
// → 모든 요청에 쿠키 전송, 하지만 HTTPS 필수개발자 도구를 통한 검증에서도 이를 확인할 수 있었습니다.
example.co.kr 콘솔에서 document.cookie를 실행하면 example.co.kr 쿠키만 표시되었고, Network 탭에서 api-server.net 요청을 확인해보면 Request Headers에 Cookie 헤더가 없어 쿠키 설정 자체가 차단됨을 확인했습니다.
Sources 탭의 Snippets에서 dev-admin.example.co.kr에서 직접 API를 호출해봐도 동일한 결과가 나타났으며, 이는 브라우저 보안 정책이 얼마나 철저하게 적용되는지 보여주는 확인하게 되었습니다.
4. 해결 방안 탐색
핵심 문제는 도메인이 다르다는 것 자체였습니다. 아무리 쿠키 설정을 조정해도 브라우저의 기본 보안 정책을 우회할 수는 없었습니다. 현재 구조에서는 Frontend가 example.co.kr에, Backend API가 api-server.net에 위치하여 브라우저가 서드파티 쿠키로 판단하여 차단하고 있었습니다.
실현 가능한 해결책들을 탐색한 결과, 첫 번째 방법은 프록시 사용이었습니다. nginx 설정으로 example.co.kr/api/\*를 api-server.net으로 프록시하여 Same-Origin으로 만드는 방법입니다.
두 번째는 토큰 기반 인증으로의 전환으로, 쿠키 대신 JWT 토큰을 프론트에 저장하여 사용하는 방법입니다. 세 번째는 iframe을 이용한 우회 방법으로 api-server.net iframe을 생성하여 iframe 내에서 쿠키를 설정하는 방법이지만, 이는 복잡하고 사용자 경험 저하가 우려된다고 생각합니다.
브라우저 보안 정책은 계층적으로 작동합니다. 먼저 Same Origin인지 확인하고, 다르다면 서드파티 쿠키를 허용하는지 검사합니다. 서드파티 쿠키가 허용된다면 SameSite 정책에 따라 Strict면 같은 사이트만, Lax면 Navigation만, None이면 모든 요청을 허용하는 구조입니다.
5. 해결 과정 요약
이번 문제를 통해 여러 중요한 개념들을 정확히 이해하게 되었습니다.
SOP는 다른 Origin 간 리소스 접근을 기본적으로 차단하며, 서드파티 쿠키 차단은 브라우저의 개인정보 보호 정책입니다.
백엔드 팀과의 협업을 통한 문제 해결 과정에서 가장 중요했던 것은 정확한 원인 분석과 결과 공유였습니다. 단순히 "안 된다"가 아니라 브라우저별 테스트 결과, 개발자 도구를 통한 구체적 증거, 그리고 브라우저 보안 정책에 대한 이해를 바탕으로 논의할 수 있었습니다. 이를 통해 문제의 본질을 이해하고 적절한 해결책을 모색할 수 있었습니다.
최종적으로는 백엔드 팀에서 어드민 시스템을 API 서버와 동일한 도메인에 배포하는 방식으로 문제를 해결했습니다. 이를 통해 Same-Origin 정책을 만족시켜 브라우저의 서드파티 쿠키 차단 문제를 근본적으로 해결할 수 있었고, 기존 레거시 시스템의 private API를 그대로 활용하면서도 안정적인 인증 시스템을 구축할 수 있었습니다.
문제 해결 과정에서 잘못된 이해들도 바로잡을 수 있었습니다. credentials: 'include'는 쿠키 설정이 아닌 전송 옵션이며, SameSite=None은 만능 해결책이 아님을, CORS 설정은 쿠키 차단을 근본적으로 해결하지 못함을 깨달았습니다.
핵심 발견은 도메인이 다르면 쿠키 불가능, credentials는 쿠키 전송 옵션일 뿐 이었습니다.
Firefox에서 작동하고 Chrome에서 작동하지 않는 이유도 명확해졌습니다. Chrome은 서드파티 쿠키를 기본적으로 더 엄격하게 차단하고, Firefox는 일부 버전에서 상대적으로 관대한 정책을 적용하여 동일한 코드도 브라우저마다 다른 동작을 보였습니다.
6. 회고 및 개선 방향
이번 경험을 통해 웹 발전과 함께 사용자 개인정보 보호가 더욱 중요해졌다는 것을 이해했습니다. 서드파티 쿠키 차단은 광고 추적 방지와 개인정보 보호를 위한 필수적인 조치였으며, 개발자로서 이런 보안 정책의 배경과 목적을 이해하게 되었습니다.
용어와 개념의 정확한 이해가 얼마나 중요한지도 깨달았습니다. credentials: 'include'의 실제 의미, SOP와 CORS의 차이점, SameSite 정책의 구체적인 동작 방식 등 기본 개념들을 정확히 이해하지 못해서 문제 해결이 더 어려워졌다는 것을 실감했습니다.
또한 도메인 분리로 인한 문제는 도메인 통합으로만 완전히 해결될 수 있었습니다.
향후 개선 방향으로는 인증 시스템의 현대화를 계획하고 있습니다. 레거시 쿠키 기반 시스템에서 JWT 기반 토큰 인증으로의 점진적 전환을 통해 도메인 제약에서 벗어나고, 더 나은 보안성과 확장성을 확보할 수 있을 것입니다.
개발팀 프로세스 개선을 위해 사전 기술 검토 체크리스트를 도입할 예정입니다. 도메인 구조 검토, 브라우저 보안 정책 영향 분석, 인증/인가 방식 결정, 다양한 브라우저에서의 동작 검증 계획을 포함하여 이런 문제를 미연에 방지하고자 합니다. 또한 백엔드와의 협업을 개선하여 API 설계 단계에서 Cross-Origin 이슈를 사전에 논의하고, 보안 정책 변경사항을 공유하며, 기술 검토 단계에서 브라우저 정책 고려사항을 포함할 계획입니다.