React를 프론트엔드, Spring Boot를 백엔드로 사용하는 구조의 프로젝트를 진행하던 중, 테이블 오더 기능에서 이상 동작이 발견되었다. 클라이언트가 주문 요청을 보낸 후, 서버는 정상적으로 세션을 생성하고 Set-Cookie: JSESSIONID=...
응답을 내려주었지만, 브라우저에서는 해당 쿠키가 Application 탭의 Cookie 저장소에 기록되지 않았다.
개발 환경에서 API 테스트 도구(Postman)를 통해 주문 생성과 주문 조회 요청을 테스트한 결과 모든 기능이 정상적으로 동작했다. 동일한 요청을 프론트엔드 애플리케이션에서 호출했을 때, 주문 생성은 문제없이 처리되지만 주문 조회 요청의 응답은 항상 빈 리스트로 반환되는 현상이 발생했다.
처음에는 백엔드 로직에 문제가 있는 것으로 의심되었다. 주문 생성과 조회에 사용된 계층 검토 결과, 비즈니스 로직에는 별다른 이상이 없었고, 실제로 Postman을 통해 호출하면 기대한 응답을 정상적으로 받을 수 있었다. API 경로, 파라미터, HTTP 메서드, 바디 구조 등 모든 조건이 동일함에도 불구하고 브라우저 환경에서만 세션 기반 조회가 실패하는 상황이었다.
해당 문제의 원인은 다음과 같았다.
브라우저가
JSESSIONID
쿠키를 저장하지도, 전송하지도 않기 때문이다.
Spring 기반의 백엔드는 클라이언트를 HttpSession
기반으로 식별하며 클라이언트는 서버가 발급한 세션 실별자(JSESSIONID
)를 이후 요청마다 Cookie
헤더로 전달해야 한다. 그러나 브라우저는 보안 정책 (CORS, SameSite 쿠키 정책 등)에 따라 쿠키의 저장과 전송을 자동으로 제한한다.
반면, Postman은 브라우저 보안 정책의 적용 대상이 아니다. 쿠키, 출처(Origin), SameSite 등과 관련한 제약 없이 모든 HTTP 요청을 그대로 처리하기 때문에 세션 쿠키가 정상적으로 동작하는 환경을 보장한다.
결론적으로 서버는 세션을 발급했음에도 브라우저는 이를 무시하고 후속 요청에 JSESSIONID
쿠키를 전송하지 않아 조회 시점에는 서버가 세션을 식별할 수 없는 상태가 된다. 이로 인해 동일한 요청임에도 불구하고 Postman에서는 정상 응답이 반환되고 브라우저에서는 비어 있는 응답이 반환되는 현상이 발생할 수 있다.
Spring 기반 백엔드에서 세션을 생성하고 클라이언트에 Set-Cookie: JSESSIONID=…
응답을 내려보내는 것은 흔한 구조이다. 그러나 실제로는 프론트엔드에서 다음 요청 시 JSESSIONID
쿠키가 누락되어 세션이 유지되지 않는 상황이 발생한다. 해당 문제의 원인은 브라우저의 SameSite 정책 변화에 따른 쿠키 저장 및 전송 제한이다.
2020년 2월 이후 Chrome 80 업데이트 이후 보안 강화를 위해 다음과 같은 정책을 적용하고 있다.
Chrome(및 대부분 모던 브라우저)은
SameSite
속성이 명시되지 않은 쿠키를 ‘기본값으로’SameSite=Lax
로 처리한다. 따라서 쿠키에SameSite=None
을 직접 지정하지 않는 한, 교차 출처(XHR·fetch·POST 등) 요청에서는 해당 쿠키가 저장되지 않거나 전송되지 않는다.
Spring은 별도 설정이 없을 경우 기본적으로 SameSite=Lax
또는 설정되지 않은 상태의 Set-Cookie
헤더를 반환한다. 이때 요청이 cross-origin 환경 (예시: localhost:5173 → localhost:8080)에서 발생하면 브라우저는 이를 차단한다. 즉, 서버는 세션 쿠키를 발급했지만 브라우저는 이를 저장조차 하지 않기 때문에, 다음 요청 시 JSESSIONID
가 포함되지 않고 새로운 세션이 생성된다.