서블릿 HTTP 세션의 세션 ID 등록 원리

김민우·2022년 8월 16일
0

잡동사니

목록 보기
5/22

서블릿이 제공하는 HttpSession을 이용하여 로그인을 처리했다.

일단, 세션의 동작 방식을 간단히 복습해보자.

  • 클라이언트가 서버로부터 로그인 요청을 한다.

  • 서버는 요청 로그인 id값마다 고유의 sessionId값을 만들어 세션 저장소에 저장한다.
  • 세션 저장소 : Map 형태
    • key : sessionId
    • value : Member 객체

  • 이후 서버는 클라이언트에게 sessionId를 쿠키에 담아 보낸다.
  • 클라이언트는 이렇게 받은 sessionId를 쿠키 저장소에 저장한다.
    (이때, 쿠키 저장소에는 mySessionId==zz0101xx...와 같은 key=value 형태로 저장된다.)

  • 클라이언트는 요청시 항상 sessionId를 쿠키에서 조회 후 서버에 전달한다.
  • 서버는 클라이언트가 전송한 sessionId 쿠키 정보로 세션 저장소를 조회해서 로그인시 세션 정보를 사용한다.

지금까지 로그인 요청시 세션이 어떻게 동작하는지 간단히 알아보았다.

중요한 것은 쿠키에는 sessionId가 저장된다는 것이고, 클라이언트가 서버로 로그인 요청을 보낼 때에는 이 값을 서버로 보내고 서버는 세션 저장소에sessionId를 뒤져서 이 값에 맞는 value(여기서는 Member객체)를 클라이언트한테 전송한다.

이를 코드로 간단히 살펴보자.

세션 저장소

private Map<String, Object> sessionStore = new ConcurrentHashMap<>();

세션 생성 로직

public void createSession(Object value, HttpServletResponse response) {

    // 세션 id를 생성하고, 값을 세션에 저장
    String sessionId = UUID.randomUUID().toString();
    sessionStore.put(sessionId, value);

    // 쿠키를 생성
    Cookie mySessionCookie = new Cookie(SESSION_COOKIE_NAME, sessionId);
    response.addCookie(mySessionCookie);
}
  • 위에서 설명했던 것이랑 정확히 일치하는 로직이다.

자, 위 내용을 상기한 상태에서 기존에 작성했던 로그인 요청을 처리하는 메소드를 보자.

@PostMapping("/login")
public String loginV4(@Validated @ModelAttribute LoginForm form,
                      BindingResult bindingResult,
                      @RequestParam(defaultValue = "/") String redirectURL,
                      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";
    }

    // 로그인 성공 처리

    // 세션이 있으면 있는 세션을 리턴, 없으면 신규 세션을 생성
    HttpSession session = request.getSession();

    // 세션에 로그인 회원 정보 보관
    session.setAttribute(LOGIN_MEMBER, loginMember);

    // redirectURL 적용
    return "redirect:" + redirectURL;
}

무언가 이상한 점을 발견할 수 있을 것이다. 분명히 HttpSession을 사용하면 session.setAttribute(), session.getAttribute() 를 사용하여 세션 저장소에 값을 넣고 꺼내온다고 학습했다.
기존 세션의 동작 원리를 생각한다면 파라미터로 sessionIdMember 객체를 넣어줘야 하는데 LOGIN_MEMBER이라는 상수가 들어가있다.

이렇게 상수를 넣어주면 클라이언트끼리 어떻게 구분을 할까? 아니 애초에 저 상수는 왜쓰는거지? 라는 생각도 든다. 두 가지를 살펴보자.


클라이언트끼리 구분할 수 있는 원리
실제로는 세션들을 보관하고 있는 세션 저장소가 하나 더 있다. 이 저장소는 sessionIdkey로, Mapvalue로 한다. 즉, sessionId를 가지고 특정 사용자만 사용하는 Map을 가져오게 되는 것이다. 따라서, 같은 key값이 같아도 특정 클라이언트의 요청을 구분할 수 있는 것이다.

참고
sessionId는 tomcat이 생성한다.

참고
예전에 웹 스코프에서 배운 request 스코프 빈의 HTTP request 구분 또한 이러한 원리이다. (request 스코프 빈들을 어떻게 구분하는지....)

상수를 쓰는 이유

Session 로직을 직접 구현한 SessionManagerHttpSession을 비교해보자.

  • 직접 만든 SessionManager : One Session for Multi User
  • HttpSession : One Session for One User

HttpSession은 하나의 클라이언트에 마다 하나의 세션 저장소를 제공한다.
이 세션 저장소는 바로 위에서 언급한 또 다른 세션 저장소이며keysessionId이며 valueMap타입이다.
그리고, 이 Map 타입의 key가 로그인 등의 세션의 사용 용도가 적힌 세션 상수라고 생각하면 된다!

따라서, 위 코드에서 setAttribute뒤의 상수는 다른 로직들과 구분을 위한 용도라 생각하면 된다.

0개의 댓글