[์๋ก ]
์์ด๋น์ค๋น ํด๋ก ํ๋ก์ ํธ๋ฅผ ์งํํ๋ฉฐ, ๋ก๊ทธ์ธ ํ์๋ง '์์ ๋ฑ๋ก' ๊ณผ '์์ ์์ฝ' ์ ํ ์ ์๊ธฐ ์ํด(์ธ๊ฐ๋ฅผ ์ฃผ๊ธฐ ์ํด) ๋ก๊ทธ์ธํ ํ์์ ๋์์ผ๋ก ์ธ์ ์ ์ฌ์ฉํด์ stateful ํ๊ฒ ๊ตฌํ ํ์ต๋๋ค.
@PostMapping("/login")
public ApiResponse<Void> login(@RequestBody @Valid MemberLoginRequest request,
HttpSession httpSession) {
memberService.login(request.toServiceRequest()); // ๋ก๊ทธ์ธ์ ์ฑ๊ณตํ๋ฉด
httpSession.setAttribute(LOGIN_MEMBER, true); // ์ธ์
๋ถ์ฌ
return ApiResponse.ok();
}
๊ทธ๋ฆฌ๊ณ , DispatcherServlet ์ด HandlerAdapter(ํธ๋ค๋ฌ ์ด๋ํฐ) ๋ฅผ ํธ์ถํ๊ธฐ ์ ์ Interceptor ์ preHandle ์ ๋จผ์ ํธ์ถํ๋๋ก ํด์, preHandle ๋ด๋ถ์์ ๋ก๊ทธ์ธ์ฉ ์ธ์ ์ด ์๋์ง ์ฒดํฌํ์์ต๋๋ค.
public class LoginCheckInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
HttpSession session = request.getSession(false);
if (session == null) {
response.setStatus(400);
return false;
}
Object loginMember = session.getAttribute(LOGIN_MEMBER);
return loginMember != null;
}
}
[ArgumentResolver ๋ก ๋ฆฌํฉํ ๋ง ํ ์ด์ ]
1. Interceptor ๋ก๋ Restful API ๋ฅผ ์๋ณํ์ง ๋ชปํ๊ธฐ ๋๋ฌธ์ ๋๋ค.
์๋ฅผ ๋ค์ด, ํน์ ์์์ ์ ๋ณด๋ฅผ ํ์ธํ๋ GET /stay/{stayId} ๋ ๋ก๊ทธ์ธ์ด ํ์ํ์ง ์์ต๋๋ค.
ํ์ง๋ง, ํน์ ์์๋ฅผ ์ญ์ ํ๋ DELETE /stay/{stayId} ๋ ๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค.
์ด์ ๋ํด Interceptor ๋ฅผ ๋ฑ๋กํ ๋, addPathPatterns() ๋ก๋ ํํํ ์ ์์ต๋๋ค.
๋ฌผ๋ก , preHandle ์์์ request method ๋ฅผ ํ์ธํด์ ํ ์๋ ์๊ฒ ์ง๋ง, ๋ ๋ณต์กํด์ง๊ฒ์ด๋ผ๊ณ ํ๋จํ์ต๋๋ค.
2. Controller ์ ์ ๋ณด๋ง์ผ๋ก ํด๋น API ๊ฐ ๋ก๊ทธ์ธ์ด ํ์ํ ์์ฒญ์ธ์ง ์ ์ ์๊ธฐ ๋๋ฌธ์ ๋๋ค.
ํด๋น API ๊ฐ ๋ก๊ทธ์ธ์ด ํ์ํ API ์ธ์ง ์จ๊ฒจ์ ธ ์๋ค๊ณ ํ๋จํด์, ์ด๋ฅผ ์ฝ๋์์ผ๋ก ๋ช ํํ ๋๋ฌ๋ด๊ณ ์ถ์์ต๋๋ค.
[๊ฒฐ๊ณผ]
HandlerAdapter ๊ฐ ArgumentResolver ๋ฅผ ํธ์ถํด์ ์ปจํธ๋กค๋ฌ ๋ฉ์๋ ํ๋ผ๋ฏธํฐ์ ๊ฐ์ด ๋ง๋ค์ด ์ค ์ ์๋๋ก ํ์์ต๋๋ค.
- custom ์ด๋ ธํ ์ด์ → (1) @Login
- HandlerMethodArgumentResolver ๋ฅผ Override ํ์ฌ ArgumentResolver ๊ฐ ์๋ํ๋ ์กฐ๊ฑด ์ค์ → (2) LoginArgumentResolver
- ์กฐ๊ฑด ์ค์ ํ ArgumentResolver ๋ฅผ ๋ฑ๋ก → (3) addArgumentResolvers(...)
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface Login {
}
(2) LoginArgumentResolver
public class LoginArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
boolean hasLoginAnnotation = parameter.hasParameterAnnotation(Login.class);
boolean hasMemberId = Long.class.isAssignableFrom(parameter.getParameterType());
return hasLoginAnnotation && hasMemberId;
}
@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) {
throw new BusinessException(ErrorCode.NOT_LOGIN);
}
return session.getAttribute(SessionConst.LOGIN_MEMBER);
}
}
(3) addArgumentResolvers(...)
@Configuration
public class WebConfig implements WebMvcConfigurer {
(...์ค๋ต)
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new LoginArgumentResolver());
}
}
๋ก๊ทธ์ธ ์ฌ๋ถ๊ฐ ํ์ํ API ์์ Session ์ ํ์ธํ๋ ์ค๋ณต ๋ก์ง์ ์ ๊ฑฐํ๊ณ , ์ฝ๋์ ๋ช ํ์ฑ์ ๋์ผ ์ ์์์ต๋๋ค.
@PostMapping("/reservation")
public ApiResponse<ReservationResponse> reserve(
@Login Long userId,
@RequestBody @Valid ReservationAddRequest request) {
ReservationResponse response = reservationService.reserve(request.toServiceRequest(userId));
return ApiResponse.ok(response);
}