HTTP 세션에 대해 알아보고 코드로 구현해보자.
세션은 일정 시간 동안 사용자의 상태를 일정하게 유지시키는 기술이다.
사용자는 로그인할 때 개인 정보를 입력하는데 이 데이터는 서버에 임시로 저장된다. 아무런 설정을 해주지 않으면 이때 저장된 데이터는 페이지를 새로 고침하면 만료된다. 따라서 페이지가 새로 고침될 때마다 로그인해야 하는 불편함이 있다. 이때 세션을 활용하면 로그인 정보를 유지시킬 수 있다.
여기서 세션은 언제 만들어질까? 모든 요청에 대해 세션을 만드는 것이 아니라 세션을 사용할 때 생성한다. 스프링에서는 세션을 관리하기 위해 HttpSession 인터페이스를 제공한다. HttpSession은 setAttribute 혹은 getAttribute 같은 api를 호출하는 시점에 요청/생성한다.
HttpSession 인터페이스는 둘 이상의 page request에서 사용자를 식별하거나, 웹 사이트를 방문하고 해당 사용자에 대한 정보를 저장하는 방법을 제공한다.
Servlet container는 HttpSession를 사용하여 HTTP client - HTTP server 간의 세션을 생성한다. 이 때, 세션은 한 명의 사용자에 해당한다. 서버는 Cookie, rewriting URL와 같은 방법으로 세션을 유지하면서 관리할 수 있다. 객체를 세션에 바인딩하여 사용자 정보를 유지할 수 있다.
예시 코드를 통해 사용자 데이터를 세션에 어떻게 저장하고 가져오는지 확인해보자.
아래 코드는 MVC 구조에서 서비스 단인데 로그인 후 사용자 정보를 httpSession.setAttribute 를 통해 저장한다.
..
public class CustomOAuth2UserService implements OAuth2UserService<OAuth2UserRequest, OAuth2User> {
private final UserRepository userRepository;
private final HttpSession httpSession;
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
...
//OAuthAttributes 의 속성값을 가져옴.
OAuthAttributes attributes = OAuthAttributes.of(registrationId, userNameAttributeName, oAuth2User.getAttributes());
//가져온 속성값을 user 객체로 변환
User user = saveOrUpdate(attributes);
//세션에 사용자 정보를 저장하기 위해 user 객체를 SessionUser DTO로 변환
httpSession.setAttribute("user", new SessionUser(user));
...
}
}
...
아래 코드는 ‘/’ 페이지를 호출하는 컨트롤러이다. 로그인에 성공했다면(= 세션에 사용자 데이터가 저장되어 있다면) 사용자 데이터를 모델에 저장한다.
httpSession.getAttribute("user") 의 반환값이 있다면 세션 정보를 저장한다. 즉, 로그인 성공 시에만 httpSession.getAttribute("user")에서 값을 가져올 수 있다.
if문에서는 model.addAttribute("key", "value")
를 이용해서 view로 key, value 쌍을 데이터로 전달한다.
@RequiredArgsConstructor
@Controller
public class IndexController {
private final PostsService postsService;
private final HttpSession httpSession;
@GetMapping("/")
public String index(Model model) {
model.addAttribute("posts", postsService.findAllDesc());
SessionUser user = (SessionUser) httpSession.getAttribute("user");
if (user != null) {
model.addAttribute("userName", user.getName());
}
return "index";
}
}
컨트롤러에서 model에 저장된 "userName"는 view에서 사용할 수 있다. 세션에 저장된 값이 없다면 model에는 아무 값이 없는 상태이기 때문에 로그인 버튼이 보인다.
...
{{#userName}}
Logged in as: <span id="user">{{userName}}</span>
<a href="/logout" class="btn btn-info active" role="button">Logout</a>
{{/userName}}
{{^userName}}
<a href="/oauth2/authorization/google" class="btn btn-success active" role="button">Google Login</a>
<a href="/oauth2/authorization/naver" class="btn btn-secondary active" role="button">Naver Login</a>
{{/userName}}
...
참고로 SessionUser 클래스 구성은 다음과 같다.
...
@Getter
public class SessionUser implements Serializable {
private String name;
private String email;
private String picture;
public SessionUser(User user) {
this.name = user.getName();
this.email = user.getEmail();
this.picture = user.getPicture();
}
}
httpSession.setAttribute("Key", Value)
로 세션을 생성하고, httpSession.getAttribute**(**"Key")
로 세션에서 객체를 가져온다.model.addAttribute(String name, Object value)
model addAttribute(Object value)