ArgumentResolver로 로그인 세션 관리
스프링의 ArgumentResolver를 이용해 로그인 세션을 확인하는 중복 로직을 줄여보자.
기존 로직
1
2
3
4
5
6
7
8
9
10
11
12
13
@GetMapping("/")
public String homeController(
@SessionAttribute(name = SessionConst.LOGIN_MEMBER, required = false)
Member loginMember,
Model model) {
//세션에 회원 데이터가 없으면 home
if (loginMember == null) {
return "home";
}
//세션이 유지되면 로그인으로 이동
model.addAttribute("member", loginMember);
return "loginHome";
}
위와 같이 Session에 @SessionAttribute을 붙이고 Session에 loginMember를 확인하는 로직이다.
이런 로직이 로그인 유저를 확인해야하는 여러 컨트롤러에서 중복적으로 이뤄지고 있었다.
@LoginUser 어노테이션 적용
1
2
3
4
5
6
7
8
9
@GetMapping("/")
public String homeController(
@LoginUser Member loginMember,
Model model) {
//세션이 유지되면 로그인으로 이동
model.addAttribute("member", loginMember);
return "loginHome";
}
@LoginUser 어노테이션을 지정한 뒤 ArgumentResolver를 통해 Session 관련 로직을 처리하자.
1
2
3
4
@Target(ElementType.PARAMETER) // 파라미터에서 사용됨
@Retention(RetentionPolicy.RUNTIME) // Runtime에서 사용
public @interface LoginUser {
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@RequiredArgsConstructor
@Component
public class LoginUserArgumentResolver implements HandlerMethodArgumentResolver {
private final HttpSession session;
@Override
public boolean supportsParameter(MethodParameter parameter) {
boolean isLoginUserAnno = parameter.getParameterAnnotation(LoginUser.class) != null;
boolean isMemberDtoClass = MemberDto.class.equals(parameter.getParameterType());
return isLoginUserAnno && isMemberDtoClass;
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
HttpSession session = request.getSession();
return session.getAttribute("loginUser");
}
}
문제 코드
1
2
3
4
5
6
7
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
HttpSession session = request.getSession();
return session.getAttribute("loginUser");
}
위와 같이 session을 꺼내올 때 request.getSession()을 실행하면 session이 있을 땐 정상 작동하지만 session == null일 시 로그인한 유저가 없음에도 session을 생성해준다. (Session의 특징)
또한 supportsParameter 메서드에서 class 타입을 확인하는 부분에서 equals보다는 isAssignableFrom이 더 적절해 리팩토링이 필요했다.
그리고 HttpSession을 필드에 선언한 뒤 DI 받을 시 세션이 싱글톤으로 생성되어 여러 문제가 발생 할 수 있었다.
이런 문제를 해결해보았다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@RequiredArgsConstructor
@Component
public class LoginUserArgumentResolver implements HandlerMethodArgumentResolver {
// private final HttpSession session;
@Override
public boolean supportsParameter(MethodParameter parameter) {
boolean isLoginUserAnno = parameter.getParameterAnnotation(LoginUser.class) != null;
boolean isMemberDtoClass = MemberDto.class.isAssignableFrom(parameter.getParameterType());
return isLoginUserAnno && isMemberDtoClass;
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
HttpSession session = request.getSession(false);
if (session == null) {
return null;
}
return session.getAttribute("loginUser");
}
}
webRequest에서 HttpSevletRequest를 꺼낸 뒤 Session을 메서드 안에서 꺼내주었다. 또 getSession(false)로 false 값을 넘겨주면 session == null이라도 새로 session을 생성하지 않아 불필요한 session 생성을 방지해주었다.
마지막으로 WebConfig에 등록해주면 끝이다.
1
2
3
4
5
6
7
8
9
@Configuration
public class WebConfig implements WebMvcConfigurer {
private final LoginUserArgumentResolver loginUserArgumentResolver;
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(loginUserArgumentResolver);
}
}
This post is licensed under CC BY 4.0 by the author.