“세션은 어디에 생기고, 쿠키는 언제 내려가고, HttpSession은 정확히 무엇인가”
세션을 공부하다 아래와 같은 궁금증이 들었다.
- session.setAttribute("loginUser", id)를 하면 Set-Cookie: JSESSIONID=...가 내려가는 건가?
- req.getSession()은 “세션 저장소”를 만드는 건가, “JSESSIONID 값”을 만드는 건가?
- 새 세션 공간이 HttpSession 자체인가, 아니면 저장소 안의 한 칸이 따로 있는 건가?
쿠키(JSESSIONID)와 세션(HttpSession)은 같은 게 아니다
세션 기반 로그인에서 항상 같이 등장하는 두 개념은 역할이 다르다.
- JSESSIONID(쿠키의 값): 브라우저가 들고 다니는 “열쇠(식별자)”이다
- 세션 저장소(서버): 서버(톰캣) 메모리에 있는 “사물함 보관소”이다
- 세션 한 칸(개별 세션): 사물함 보관소 안의 “사용자별 칸”이다
- HttpSession: 개발자가 자바 코드에서 그 “한 칸”을 다루도록 주어지는 핸들(표준 인터페이스)이다
즉, 쿠키에 로그인 정보를 직접 넣는 게 아니라
쿠키에는 “세션ID(열쇠 번호)”만 담고,
실제 로그인 정보는 서버 세션 저장소에 담는다.
session.setAttribute("loginUser", id)는 쿠키를 만드는 코드가 아니다
session.setAttribute("loginUser", id);
이 코드는 쿠키를 내려주는 코드가 아니다.
이 코드는 딱 한 가지 동작만 한다.
“서버의 세션 저장소에서 내 세션 한 칸에 loginUser=id를 저장한다”
즉, setAttribute는 세션 내부에 데이터를 넣는 동작이다.
쿠키(JSESSIONID)를 만들거나 내려주는 역할은 아니다.
그럼 쿠키는 언제 내려가나?
그 트리거가 바로 req.getSession()이다.
req.getSession()은 정확히 뭘 하냐: (있으면 찾고, 없으면 만든다)
req.getSession()은 한 문장으로 요약하면 이렇다.
“요청이 들고 온 JSESSIONID로 기존 세션을 찾아서 돌려주고, 없으면 새 세션을 만들고 새 JSESSIONID까지 발급한다”
즉 “찾기 + 만들기”가 같이 들어있다.
단, 세션이 없는 경우에만 만들기 로직이 동작한다.
“세션이 없다”의 의미는 뭔가
톰캣은 요청을 받으면 먼저 확인한다.
- 요청 헤더에 Cookie: JSESSIONID=...가 있는가?
- 그 값으로 서버 세션 저장소에서 세션을 실제로 찾을 수 있는가?
- 만료/삭제된 세션이면 못 찾는다
둘 중 하나라도 실패하면 “세션이 없다”로 본다.
케이스 A: JSESSIONID가 없거나 유효하지 않을 때
HttpSession session = req.getSession();
이 한 줄로 톰캣은 두 가지를 같이 한다.
- 서버 세션 저장소에 새 세션 한 칸을 만든다
- 그 한 칸을 찾기 위한 세션ID를 만들고(=JSESSIONID 값), 응답에 Set-Cookie로 내려줄 준비를 한다
즉 이때는
- 세션 저장소 “한 칸”이 새로 생기고
- 그 칸을 가리키는 “열쇠 번호”가 새로 생긴다
그리고 응답이 나갈 때 브라우저로 이런 헤더가 내려간다.
- Set-Cookie: JSESSIONID=새값; Path=/; ...
케이스 B: JSESSIONID가 이미 있고 유효할 때
HttpSession session = req.getSession();
이번에는 “만드는 것”이 아니라 “찾는 것”이다.
- 브라우저가 Cookie: JSESSIONID=기존값을 붙여서 보낸다
- 톰캣이 그 값으로 세션 저장소에서 세션 한 칸을 찾는다
- 찾은 세션을 HttpSession 형태로 돌려준다
- 새로 만들 게 없으니 Set-Cookie를 굳이 다시 내릴 필요가 없다(일반적인 흐름)
그럼 “새 세션 공간”은 HttpSession 자체인가?
정답은 이렇다.
- 세션 저장소 안에는 “세션 한 칸(세션 엔트리)”이 실제로 존재한다
- 개발자가 자바 코드에서 만지는 것은 그 한 칸을 조작하기 위한 표준 인터페이스인 HttpSession이다
즉 구조는 이렇게 이해하면 된다.
- 세션 저장소(Map 같은 구조)
- key = 세션ID (쿠키 JSESSIONID의 값)
- value = 세션 한 칸(Session 엔트리)
그리고 개발자에게는
- 그 value(세션 한 칸)를 조작할 수 있는 핸들로 HttpSession이 제공된다
“같냐?”라고 물으면 엄밀히는 구현체가 따로 있지만, 학습 관점에서는
세션 한 칸(서버 엔트리) 1개 ↔ HttpSession 1개가 대응된다
라고 생각해도 된다.
로그인 흐름을 타임라인으로 보면 완전히 정리된다
1. 첫 로그인 요청(브라우저에 JSESSIONID 없음)
브라우저:
- POST /login (Cookie 없음)
서버 코드:
HttpSession session = req.getSession(); // ① 세션 없으니 새 세션 생성 + 새 세션ID 발급 준비
session.setAttribute("loginUser", id); // ② 서버 세션 한 칸에 loginUser 저장
resp.sendRedirect("/home"); // ③ 응답 전송
응답이 나갈 때:
- Set-Cookie: JSESSIONID=...가 내려간다 (①에서 새 세션을 만들었으니까)
브라우저는:
- JSESSIONID 쿠키를 저장한다
2. 다음 요청부터(브라우저가 JSESSIONID를 자동 전송)
브라우저:
- GET /home 요청
- 요청 헤더에 Cookie: JSESSIONID=... 자동 첨부
서버는:
- JSESSIONID로 세션 저장소에서 세션 한 칸을 찾는다
- session.getAttribute("loginUser")로 로그인 상태를 확인할 수 있다
getSession() vs getSession(false)가 중요한 이유
- getSession()
- 세션이 없으면 새로 만든다 (세션 한 칸 + 세션ID + Set-Cookie 준비)
- getSession(false)
- 세션이 없으면 null을 반환한다 (새로 만들지 않는다)
로그인 체크 같은 곳에서 getSession()을 무심코 쓰면
로그인 안 한 사용자에게도 세션을 쓸데없이 만들어서 서버 메모리를 낭비할 수 있다.
그래서 “체크”는 보통 getSession(false)가 더 안전하다.
정리
- session.setAttribute(...)는 세션 데이터 저장이지, 쿠키 발급이 아니다
- req.getSession()은
- 있으면 기존 세션을 찾고,
- 없으면 서버에 세션 한 칸을 만들고 + 새 세션ID(JSESSIONID 값)를 발급한다
- 세션 저장소 안의 “개별 세션 한 칸”이 실제 세션이고
HttpSession은 그 한 칸을 자바 코드에서 다루기 위한 핸들(인터페이스)이다 - 세션ID는 쿠키(JSESSIONID)로 브라우저가 들고 다니는 “열쇠” 역할이다
'Web > Web Basics' 카테고리의 다른 글
| [상태 유지와 공통 처리] 4. 필터(Filter)란? (0) | 2026.03.30 |
|---|---|
| [상태 유지와 공통 처리] 3. SESSIONID이란? (0) | 2026.03.30 |
| [상태 유지와 공통 처리] 2. 세션이란? (0) | 2026.03.30 |
| [상태 유지와 공통 처리] 1. 쿠키란? (0) | 2026.03.30 |
| [MVC와 웹 흐름 제어] 7. WEB-INF에 JSP를 두는 이유: “JSP 직접 호출”을 구조적으로 막는다 (0) | 2026.03.29 |