스프링

[Spring] 세션(Session)

gilssang97 2021. 9. 25. 22:39

세션이란?

세션(Session)이란 일정 시간동안 같은 사용자(브라우저)로 부터 들어오는 일련의 요구를 하나의 상태로 보고 그 상태를 일정하게 유지시키는 기술이다. 여기서, 일정 시간이란 사용자가 브라우저를 통해 웹 서버에 접속한 시점으로부터 브라우저를 종료할 때 까지의 시간을 말한다.

정리하자면, 사용자가 웹 서버에 접속해 있는 상태를 하나의 단위로 보고 일정 시간동안 유지시키는 것을 세션이라고 말한다.

세션은 흔히 우리가 브라우저를 사용하면서 많이 듣게 되는 쿠키와 비교된다.

쿠키는 (Key, Value) 쌍으로 로컬에 저장되어 사용되는 작은 데이터 파일이다.

여기서 쿠키와 세션의 차이점에 대해서 간단하게 살펴보고 넘어가자.


이제는 세션에 대해서 잘 알겠는데 이를 어떤 경우에 사용할까?

우리가 브라우저를 사용할 때를 생각해보자.

우리가 어떤 페이지에서 로그인을 하고 해당 웹 서비스에서 제공하는 여러가지 서비스를 돌아다닐 때 로그인이 풀리는가?

정답은 No다.

바로, 위에서 말했던 것처럼 로그인한 상태를 일정하게 유지시킨다.

바로 여기서, 세션의 개념이 사용된다. 그러면 실제로 어떻게 세션이 연결되고 지속되는지 로그인으로 자세한 이해를 해보자.

  • 먼저, 아이디 비밀번호를 올바르게 치고 로그인 요청을 보냈다.
  • 서버에서는 로그인 처리에 대한 비지니스 로직 처리뿐만 아니라 로그인에 대한 상태를 유지하기 위한 Session을 생성한다.
  • 그리고 이 값을 응답시에 사용자에게 같이 넘겨준다.
  • 만약, 추후에 다른 요청이 있을 경우 사용자는 세션 값을 가지고 있으므로 이 값을 통해 로그인한 상태를 유지한다.

우리는 이와 같이 세션을 유지하는데 실제로 이를 어떻게 구현할지 살펴보자.

세션 구현

세션 구현은 이 전에 구현했던 회원가입의 연장선으로 로그인까지 구현해보려고 한다.

현재, 로그인과 회원가입을 진행할 수 있는 홈 페이지가 존재하고, 로그인과 회원가입을 위한 페이지도 따로 존재한다.

로그인 페이지에서 로그인을 완료하게 되면, 메인 페이지로 이동하게 된다.

만약, 로그인을 한 상태라면 홈 페이지 URL을 입력했을 때에도 메인 페이지로 이동하려고 한다.

이 요구사항들에 대한 예제를 통해 세션에 대해 알아보자.

(1) 아이디, 비밀번호 입력 및 검증

@PostMapping("/login")
public String doLogin(@Validated @ModelAttribute LoginForm loginForm, BindingResult bindingResult, HttpServletRequest request) {
    Student student = loginService.login(loginForm);
    if (student == null) {
        bindingResult.reject("inCorrectLogin", null);
    }

    if (bindingResult.hasErrors()) {
        return "login";
    }
}
@Getter
@Setter
public class LoginForm {
    @NotBlank
    private String email;
    @NotBlank
    private String password;
}
  • 로그인을 진행할 때 필요한 정보는 실제 Student객체와의 정보와 일치하지 않는다.(오직 아이디, 비밀번호만 필요)
  • 그에 따라, DTO를 추가적으로 만들어 정보를 담고 이를 통해 처리해준다.
  • 빈 검증을 통해 아이디와 비밀번호를 필히 입력하도록 만든다.

(2) 아이디, 비밀번호 확인

public Student login(LoginForm loginForm) {
    return studentRepository.findByEmail(loginForm.getEmail())
                .filter(student -> student.getPassword().equals(loginForm.getPassword()))
                .orElse(null);
}

public Optional<Student> findByEmail(String email) {
    return findAll().stream()
                .filter(m -> m.getEmail().equals(email))
                .findAny();
}
  • 먼저, 서버에서 해당 아이디가 존재하는지 찾는다. 존재한다면 해당 아이디에 대한 비밀번호가 일치하는지도 찾아준다.
  • 해당 아이디가 존재하지 않거나 비밀번호가 일치하지 않는다면 null을 반환하여 로그인에 대한 요청을 거절한다.

(3) 세션 생성

@PostMapping("/login")
public String doLogin(@Validated @ModelAttribute LoginForm loginForm, BindingResult bindingResult, HttpServletRequest request) {
    Student student = loginService.login(loginForm);
    if (student == null) {
        bindingResult.reject("inCorrectLogin", null);
    }

    if (bindingResult.hasErrors()) {
        return "login";
    }
    HttpSession session = request.getSession();
    session.setAttribute(SessionConst.LOGIN_STUDENT, loginForm);
    return "redirect:/main";
}
public abstract class SessionConst {
    public static final String LOGIN_STUDENT = "loginStudent";
}
  • 세션 ID를 위한 추상 클래스를 하나 생성한 뒤, 이를 사용한다.

  • HttpServletReqeustgetSession()을 통해 간단하게 세션을 생성해주고 해당 세션에 대한 ID값 데이터 값을 설정해준다.

  • 로그인에 성공하였으므로 로그인 후의 페이지인 메인 페이지로 이동한다.


(4) 로그인 상태 유지

@GetMapping("/")
public String home(@SessionAttribute(name =SessionConst.LOGIN_STUDENT, required = false) LoginForm loginForm ){
    if (loginForm == null) {
        log.info("ss"+loginForm);
        return "home";
    }

    return "main";
}
  • @SessionAttribute라는 애노테이션을 통해 세션을 쉽게 가져올 수 있다.
  • 로그인한 상태라면 메인 페이지로, 로그인이 되어 있지 않은 상태라면 홈 페이지로 가서 로그인을 하도록 한다.

(5) 세션 만료

@PostMapping("/logout")
public String logout(HttpServletRequest request) {
    HttpSession session = request.getSession(false);
    if (session != null) {
        session.invalidate();
    }

    return "home";
}
  • 로그아웃을 한다면 HttpServletRequestinvalidate()를 통해 해당 세션을 무효화시켜준다.

구현 결과

(1) 홈 페이지


(2) 회원가입


(3) 회원가입 성공


(4) 로그인


(5) 로그인 성공 후 메인 페이지


(6) 살펴보기를 통한 회원들 조회


(7) 로그아웃


자 다음과 같이 세션을 알아보기 위해 전체적인 동작을 살펴보았다.

하지만, 홈 페이지, 회원가입 페이지, 로그인 페이지 등에서 살펴보기 페이지를 URL을 통해 현재는 접근할 수 있다.

이를 막기 위해 로그인이 되어있는지 사전에 확인해야 한다.

그런데 모든 페이지에서 이를 처리하는 로직을 진행한다면 중복이 생길 것이므로 이를 공통적으로 처리할 수 있도록 하면 좋을 것이다.

이를 가능하게 만들어주는 필터 혹은 인터셉터를 다음에 알아보고자 한다.

참고문헌

https://hahahoho5915.tistory.com/32

https://88240.tistory.com/190

https://chrisjune-13837.medium.com/web-쿠키-세션이란-aa6bcb327582

https://www.inflearn.com/course/스프링-mvc-2/dashboard