쿠키는 웹 클라이언트, 즉 브라우저에 저장되는 작은 데이터 조각이다. 쿠키는 사용자의 로그인 상태나 최근 본 상품 등의 상태를 클라이언트가 직접 저장하고 매 요청마다 서버에 전달함으로써 서버가 사용자의 상태를 식별할 수 있도록 한다. 서버는 클라이언트에게 HTTP 응답 시 Set-Cookie
헤더를 포함해 쿠키를 설정하고 클라이언트는 이를 브라우저에 저장한 후 동일한 도메인으로의 요청 시 자동으로 해당 쿠키를 Cookie
요청 헤더에 포함시켜 전송한다.
아래는 간단한 HTTP 요청-응답 흐름 예시다.
# 서버 응답
HTTP/1.1 200 OK
Set-Cookie: sessionId=abc123; Path=/; HttpOnly
# 이후 클라이언트 요청
GET /mypage HTTP/1.1
Host: example.com
Cookie: sessionId=abc123
쿠키는 주로 자동 로그인, 최근 본 상품 목록, 광고 트래킹 등에 사용된다. 하지만 브라우저에 저장되는 정보인 만큼 사용자가 직접 쿠키를 조작하거나 탈취할 수 있으므로 민감한 정보를 직접 담아서는 안 된다. 예를 들어 사용자 비밀번호, 카드 번호 등의 민감 정보는 절대 담으면 안 되며, 토큰도 가능하면 HttpOnly로 설정하는 것이 있다. 보안 강화를 위해 HttpOnly, Secure, SameSite 등의 속성을 적절히 설정하는 것이 중요하다.
세션은 사용자별 상태 정보를 서버에 저장하는 방식이다. 서버는 사용자가 로그인하는 등의 이벤트가 발생했을 때 고유한 세션 ID를 생성하고 해당 ID를 기준으로 메모리나 DB에 사용자 상태를 저장한다. 이후 서버는 이 세션 ID를 쿠키로 클라이언트에 전달하고 클라이언트는 이를 저장하여 모든 요청에 포함시킨다. 서버는 요청에 포함된 세션 ID를 기반으로 사용자 정보를 식별하고 로그인 상태 유지 등 인증 처리를 할 수 있게 된다.
세션의 구조는 다음과 같다.
[Client]
↑↓ JSESSIONID=abc123 (Cookie)
[Server]
sessionId: abc123 → 사용자 ID, 권한, 장바구니 등 저장
Spring Boot에서는 기본적으로 세션 ID가 JSESSIONID
라는 이름으로 쿠키에 담기며 서버 측에서는 HttpSession
객체로 세션을 관리한다.
구분 | 쿠키 | 세션 |
---|---|---|
저장 위치 | 클라이언트(브라우저) | 서버 메모리/스토리지 |
보안성 | 낮음 (사용자 수정 가능) | 높음 (서버 관리) |
속도 | 빠름 | 느림 |
저장 용량 | 4KB 이하 | 서버 용량 제한에 따름 |
만료 방식 | 쿠키에 명시 (max-age , expires ) | 서버 설정 (session timeout ) |
라이프 사이클 | 브라우저 종료해도 만료시점 지나지 않으면 삭제 X | 만료 시간 정할 수 있지만 브라우저 종료되면 삭제 |
사용 목적 | 자동 로그인, 최근 본 상품 등 | 로그인 상태 유지, 사용자 인증 |
쿠키는 가벼운 정보 저장에 적합하며 세션은 민감하고 중요한 사용자 상태 유지에 적합하다. 실무에서는 이 둘을 조합하여 사용하는 경우가 많다. 예를 들어 세션 ID를 쿠키에 저장하고 서버는 해당 ID를 바탕으로 사용자 정보를 조회한다.
로그인 시 쿠키와 세션이 어떻게 함께 동작하는지 간단한 흐름으로 정리해보면 다음과 같다.
JSESSIONID
)를 클라이언트에 쿠키로 전달JSESSIONID
쿠키를 저장// 로그인 성공 시 세션에 저장
session.setAttribute("userId", user.getId());
// 이후 요청에서 세션 정보 꺼내기
@GetMapping("/me")
public UserResponse getMyInfo(HttpSession session) {
Long userId = (Long) session.getAttribute("userId");
return userService.findUserInfo(userId);
}
프론트엔드와 백엔드가 분리된 경우 세션 쿠키(JSESSIONID
)가 자동 전송되기 위해 다음과 같은 CORS 설정이 반드시 필요하다.
// CORS 설정 예시
registry.addMapping("/**")
.allowedOrigins("http://localhost:3000")
.allowCredentials(true); // 필수
Spring Boot에서는 HttpSession
객체를 통해 세션을 쉽게 관리할 수 있으며 세션 전략을 변경할 수 있는 설정도 지원한다. 예를 들어 spring.session.store-type=redis
로 설정하면 Redis 기반 세션 스토리지도 가능하다.
또한 Spring Security를 함께 사용할 경우 내부적으로 세션에 인증 정보를 저장하고 SecurityContextHolder
를 통해 꺼내 쓸 수 있도록 구성되어 있다. 따라서 커스텀 로그인 로직이 아닌 경우 세션이 자동으로 처리되므로 별도 세션 코드를 작성하지 않아도 인증 상태를 유지할 수 있다.
쿠키와 세션은 각각 장단점이 있는 만큼 보안 설정이 필수적이다. 쿠키는 HttpOnly
속성을 지정하여 자바스크립트로 접근하지 못하게 막고 Secure
속성을 통해 HTTPS에서만 전송되도록 설정한다. 또한 SameSite=Strict
또는 Lax
설정은 CSRF 공격을 방지하는 데 매우 효과적이다.
세션의 경우에도 탈취를 막기 위해 다음과 같은 설정을 고려해야 한다.
session-timeout
)session.invalidate()
)이러한 설정을 통해 XSS, CSRF, Session Hijacking 등의 위협으로부터 시스템을 보호할 수 있다.
SameSite=Strict 또는 Lax란?
SameSite
는 쿠키의 교차 사이트 요청 동작을 제한하여 CSRF 공격을 방지하기 위한 보안 속성이다.
SameSite=Strict
이 설정을 하면 다른 사이트에서 유입된 모든 요청에는 쿠키가 전송되지 않는다. 예를 들어 A 사이트에서 로그인한 사용자가 B 사이트에서 A 사이트로 POST 요청을 보내더라도 쿠키가 전송되지 않아 세션 인증이 되지 않는다. 보안은 가장 강력하지만 UX에는 영향을 줄 수 있다.
SameSite=Lax
조금 더 유연한 설정이다. GET 요청과 같은 안전한 방식은 쿠키를 전송하지만 POST/PUT 등 상태를 바꾸는 요청은 쿠키를 보내지 않는다. 대부분의 경우 이 설정만으로도 CSRF를 방지하는 데 충분하다.
SameSite=None
이 설정은 모든 요청에서 쿠키를 보내도록 허용하지만 반드시 Secure와 함께 설정해야 한다. 즉 HTTPS 환경이 아니면 작동하지 않는다.예시
Set-Cookie: sessionId=abc123; SameSite=Strict; Secure; HttpOnly
이 설정들은 CSRF 공격을 막기 위한 핵심적인 방어선이기 때문에 실무에서는 쿠키 설정 시 반드시 명시적으로 지정해주는 것이 좋다.
이번에 쿠키와 세션에 대해 정리하면서 단순히 "어디에 저장되느냐"의 차원을 넘어서 보안성, 서버 자원, 실무 적용 방식, CORS와의 관계, Spring에서의 처리 방식까지 폭넓게 이해할 수 있었다. 특히 세션은 클라이언트가 아닌 서버가 상태를 책임지고 관리하는 구조라는 점에서 사용자 인증과 같이 신뢰성이 중요한 기능에서 왜 더 많이 쓰이는지 체감할 수 있었다. 반면 쿠키는 클라이언트가 관리하는 만큼 설정을 잘못하면 보안 문제가 생기기 쉽다는 것도 알게 되었다. 또한 프론트엔드와 백엔드가 분리된 환경에서는 JSESSIONID
쿠키가 요청에 포함되도록 하기 위해 credentials
설정이 반드시 필요하며 그렇지 않으면 아무리 세션이 잘 저장되어도 인증이 되지 않는 상황이 발생한다는 점도 중요한 포인트였다. Spring Security가 세션을 자동으로 관리해주는 구조도 이번 정리를 통해 더 깊이 이해할 수 있었다.
내가 생각하는 가장 큰 차이점은 "어디에 상태 정보를 저장하느냐"라고 생각한다. 쿠키는 상태 정보를 클라이언트(브라우저)에 저장하고 세션은 상태 정보를 서버에 저장하며 클라이언트는 오직 세션 ID만 들고 있는 구조다. 이 구조적인 차이 때문에 보안, 용량, 신뢰성 측면에서 완전히 다른 전략이 필요하다.
참고