@PostMapping("/login")
public String loginV3(@Valid @ModelAttribute LoginForm form, BindingResult bindingResult, HttpServletRequest request) {
if (bindingResult.hasErrors()) {
return "login/loginForm";
}
Member loginMember = loginService.login(form.getLoginId(), form.getPassword());
if (loginMember == null) {
bindingResult.reject("loginFail", "아아디 또는 비밀번호가 맞지 않습니다.");
return "login/loginForm";
}
/**
* 이 아래 두개의 코드는 내부적으로 코드가 많이 복잡하다.
* 전체적인 흐름만 알자.
*
* public class StandardSession implements HttpSession {
* private final String id; // UUID 같은 세션 ID
* private final Map<String, Object> attributes; // 세션 속성 저장소
* // … 그 밖의 메타데이터(생성시간, 만료시간 등)
* }
*/
//로그인 성공 처리
//세션이 있으면 있는 세션 반환, 없으면 신규 세션 생성
HttpSession session = request.getSession(); // 없으면 세션 생성 + UUID 발급
log.info("session.getId()={}", session.getId());//발급한 UUID HttpSession 안에 보관.
//세션에 로그인 회원 정보 보관-> , 자동으로 Set-Cookie: JSESSIONID=<UUID> 헤더 추가
session.setAttribute(SessionConst.LOGIN_MEMBER, loginMember);
return "redirect:/";
}
HttpSession 구현체 내부에는 대략 이런 필드가 있어요:
// 톰캣(StandardSession)의 내부 예시
public class StandardSession implements HttpSession {
private final String id; // UUID 같은 세션 ID
private final Map<String, Object> attributes; // 세션 속성 저장소
// … 그 밖의 메타데이터(생성시간, 만료시간 등)
}
String)session.setAttribute(name, value) 가 하는 일session.setAttribute(SessionConst.LOGIN_MEMBER, loginMember);
실제로는 이렇게 동작합니다:
SessionConst.LOGIN_MEMBER → 컴파일 타임에 "loginMember" 라는 문자열 리터럴로 인라인
내부 attributes.put("loginMember", loginMember) 호출
이제 이 HttpSession 객체의 속성 맵에
key: "loginMember"
value: <loginMember 도메인 객체>
가 저장된 상태가 됩니다.
요점: 이 문자열 자체는 “속성 맵의 key”로만 쓰입니다.
session.getAttribute(name) 가 하는 일Member member = (Member) session.getAttribute(SessionConst.LOGIN_MEMBER);
"loginMember" 를 키로 넘기면attributes.get("loginMember") 이 실행돼서loginMember 객체가 반환됩니다.정확히 한 세션 객체(HttpSession)당 하나의 세션 ID만 존재합니다.
public class StandardSession implements HttpSession {
private final String id; // <— 이 한 번 생성된 문자열이 곧 세션 ID
private final Map<String, Object> attributes; // <— 이 안에 키–값으로 여러분이 setAttribute한 데이터가 저장됩니다.
// … 그 밖에 생성 시간, 최종 접근 시간, 만료 시간 같은 메타데이터가 있어요.
}
id 필드(StandardSession.id)는 세션이 처음 만들어질 때 한 번만 부여되고(new UUID()), 변경되지 않습니다.session.getId() 를 호출하면 됩니다.JSESSIONID)로 저장했다가 이후 요청 때마다 보내 주고,StandardSession 객체를 조회하죠.참고:
- Servlet 3.1 이상부터는
HttpServletRequest#changeSessionId()메서드를 통해 기존 속성은 유지하면서 새 ID를 발급할 수 있는데, 이건 세션 고정 공격(session fixation) 방지를 위해 옵션으로 제공되는 기능입니다.- 그 외에는 “한 세션 = 한 ID”라는 관계가 1:1로 유지된다고 보시면 됩니다.
@PostMapping("/login")
public String loginV3(@Valid @ModelAttribute LoginForm form, BindingResult bindingResult, HttpServletRequest request) {
if (bindingResult.hasErrors()) {
return "login/loginForm";
}
Member loginMember = loginService.login(form.getLoginId(), form.getPassword());
if (loginMember == null) {
bindingResult.reject("loginFail", "아아디 또는 비밀번호가 맞지 않습니다.");
return "login/loginForm";
}
/**
* 이 아래 두개의 코드는 내부적으로 코드가 많이 복잡하다.
* 전체적인 흐름만 알자.
*
* public class StandardSession implements HttpSession {
* private final String id; // UUID 같은 세션 ID
* private final Map<String, Object> attributes; // 세션 속성 저장소
* // … 그 밖의 메타데이터(생성시간, 만료시간 등)
* }
*/
//로그인 성공 처리
//세션이 있으면 있는 세션 반환, 없으면 신규 세션 생성
//여기는 로그인이므로 기존에 없을것이다.(그래서 True)로 해놔야한다.
//HttpSession 내부에 UUID 를 넣는 필드인 id 가 존재할것이데 거기에 값이 들어간다.
HttpSession session = request.getSession(); // 없으면 세션 생성 + UUID 발급
log.info("session.getId()={}", session.getId());//발급한 UUID HttpSession 안에 보관.
//세션에 로그인 회원 정보 보관-> , 자동으로 Set-Cookie: JSESSIONID=<UUID> 헤더 추가
session.setAttribute(SessionConst.LOGIN_MEMBER, loginMember);
return "redirect:/";
}
/@GetMapping("/")
public String homeLoginV3(HttpServletRequest request, Model model) {
//세션 자체가 없으면 home ( 굳이 세션을 생성할 필요없다.)
//false 인게 중요하다 ( Cookie 에 있는 UUID 로 비교후 같은 UUID 를 가지는 HttpSession이 있으면 그것을 가져온다)
// 만약에 UUID 와 비교했을때 해당하는 HttpSession이 없으면 null 을 반환한다.
// true 일때와 다르게 UUID 가 일치하는게 없다고 새로생성하지않는다.
HttpSession session = request.getSession(false);
if (session == null) {
return "home";
}
Member loginMember = (Member)session.getAttribute(SessionConst.LOGIN_MEMBER);
//세션에 회원 데이터가 없으면 home
if(loginMember == null) {
return "home";
}
//세션이 유지되면 로그인홈화면 으로 이동
model.addAttribute("member", loginMember);
return "loginHome";
}