DevLog ๐ถ
[Spring] ์๋ธ๋ฆฟ ํํฐ, ์คํ๋ง ์ธํฐ์ ํฐ, ์ด๋ ธํ ์ด์ ์ผ๋ก ๋ก๊ทธ์ธ ๊ฒ์ฆํ๊ธฐ ๋ณธ๋ฌธ
[Spring] ์๋ธ๋ฆฟ ํํฐ, ์คํ๋ง ์ธํฐ์ ํฐ, ์ด๋ ธํ ์ด์ ์ผ๋ก ๋ก๊ทธ์ธ ๊ฒ์ฆํ๊ธฐ
dolmeng2 2022. 8. 28. 16:28๊น์ํ ๋์ '์คํ๋ง MVC 2ํธ - ๋ฐฑ์๋ ์น ๊ฐ๋ฐ ํ์ฉ ๊ธฐ์ '์ ๋ณด๊ณ ์ ๋ฆฌํ ๊ธ์ ๋๋ค ๐
์ง๋ ํฌ์คํ ๊ณผ ์ด์ด์ง๋๋ค :D
| ์๋ธ๋ฆฟ ํํฐ ์ ์ฉํ๊ธฐ
- ์ง๋ ํฌ์คํ ๊น์ง ์งํํ ์ํ ๊ด๋ฆฌ ์น ์ฌ์ดํธ๋ ์ธ์ ์ ํตํด ๋ก๊ทธ์ธ, ๋ก๊ทธ์์์ด ๋งค์ฐ ์ ๋์ํ๋ค.
- ๊ทธ๋ฌ๋, ๋ก๊ทธ์ธํ์ง ์์ ์ฌ์ฉ์์ด๋๋ผ๋ 'URL'์ ํตํด์ ์ํ ๋ฆฌ์คํธ์ ์ ๊ทผํ ์ ์๋ค๋ ๋จ์ ์ด ์๋ค.
- ์ด๋ฌํ '๊ณตํต ๊ด์ฌ์ฌ'๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์, ์๋ธ๋ฆฟ ํํฐ๋ฅผ ์ฌ์ฉํด๋ณด์.
๐ฉ HTTP ์์ฒญ -> WAS -> ํํฐ -> ์๋ธ๋ฆฟ -> ์ปจํธ๋กค๋ฌ
- ํํฐ๋ฅผ ์ ์ฉํ๋ฉด ํํฐ ํธ์ถ ํ ์๋ธ๋ฆฟ์ด ํธ์ถ๋๊ธฐ ๋๋ฌธ์, ํํฐ์ ํน์ URL ํจํด์ ์ ์ฉํ์ฌ ํํฐ๋ฅผ ์ ์ฉํ ์ ์๋ค.
- ํํฐ์์ ์์ฒญ์ด ์ ์ ํ์ง ์๋ค๊ณ ํ๋จํ๋ฉด ์๋ธ๋ฆฟ๊น์ง ํธ์ถ์ ์ ์ํฌ ์ ์๋ค!
- ๋ํ, ํํฐ๋ ์์ ๋กญ๊ฒ ์ถ๊ฐํ ์ ์๋ค!
- โญ์ฐธ๊ณ ๋ก, ํํฐ์ ๊ฒฝ์ฐ ์ธํฐ์ ํฐ์ ๋ค๋ฅด๊ฒ ์์ Spring Context์ ๋ฐ๊นฅ๋ถ๋ถ์์ ์คํ๋๋ค!
โ Filter
- init() : ํํฐ ์ด๊ธฐํ ๋ฉ์๋. ์๋ธ๋ฆฟ ์ปจํ ์ด๋ ์์ฑ ์ ํธ์ถ
- doFilter() : ์์ฒญ์ด ๋ค์ด์ฌ ๋๋ง๋ค ํธ์ถ. ํํฐ์ ์ค์ง์ ์ธ ๋ก์ง ๊ตฌํ๋ถ.
- destroy() : ํํฐ ์ข ๋ฃ ๋ฉ์๋. ์๋ธ๋ฆฟ ์ปจํ ์ด๋ ์ข ๋ฃ ์ ํธ์ถ.
[LoginCheckFilter.java]
- ๋ก๊ทธ์ธ ์ธ์ฆ ํํฐ ๊ตฌํํ๊ธฐ
public class LoginCheckFilter implements Filter {
private static final String[] whiteList
= {"/login/", "/login/members/add", "/login/members/login", "/login/members/logout", "/css/*"};
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String requestURI = httpRequest.getRequestURI();
try {
// whiteList ์ ์ธํ ๋๋จธ์ง url์์ ๊ฒ์ฆ ์์
if(!PatternMatchUtils.simpleMatch(whiteList, requestURI)) {
HttpSession session = httpRequest.getSession(false);
if(session == null || session.getAttribute("loginMember") == null) {
httpResponse.sendRedirect("/login/members/login?redirectURL="+ requestURI);
return; // ๋ ์ด์ ์งํ X
}
}
// ๋ค์ ์ฒด์ธ์ผ๋ก ์ด๋ (์์ผ๋ฉด ์๋ธ๋ฆฟ์ผ๋ก)
chain.doFilter(request, response);
} catch (Exception e) {
throw e;
}
}
}
- ๋ฉ์ธ ํ๋ฉด, ๋ก๊ทธ์ธ, ํ์๊ฐ์ ํ๋ฉด์์๋ ๋ก๊ทธ์ธ์ด ์ ๋์ด ์์ด๋ ๋ณด์ฌ์ ธ์ผ ํ๊ธฐ ๋๋ฌธ์ whiteList์ ์ถ๊ฐํ์๋ค.
- ๋ง์ฝ session์ด ์๊ฑฐ๋, session ๋ด๋ถ์ loginMember ๊ฐ์ฒด๊ฐ ์์ผ๋ฉด ๋ก๊ทธ์ธ ํ๋ฉด์ผ๋ก ๋ฆฌ๋ค์ด๋ ํธํ๋ค.
- โญ ์ด๋, httpResponse.sendRedirect("") ์ ์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ๋ก redirectURL์ ๋ฃ์ด์ฃผ์ด์, ๋ก๊ทธ์ธ ์ดํ ์ฌ์ฉ์๊ฐ ๋ค์ ์ ์ํ๋ ํ์ด์ง๋ก ๋ค์ด์ฌ ์ ์๋๋ก ํจ๊ป ๋ณด๋ด์ค๋ค! (๋ฌผ๋ก ๋ฐ๋ก ์ด์ ๋ํด์ ์ฒ๋ฆฌ๋ฅผ ํด์ค์ผ ํ๋ค)
- ๋ํ, return;์ ๊ฑธ์ด์ฃผ์ด์ ๋ ์ด์ ์งํ๋์ง ์๋๋ก ํ๋ค. (๋ฌผ๋ก ํ์ chain.doFilter๋ฅผ else ๋ฌธ์ผ๋ก ๊ฑธ์ด๋ ๋์ผํ๊ธด ํ์ง๋ง ์ด๊ฒ ๋ ๊น๋ํ๋ค)
[WebMvcConfig.java]
@Bean
public FilterRegistrationBean loginCheckFilter() {
FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(new LoginCheckFilter());
filterRegistrationBean.setOrder(1);
filterRegistrationBean.addUrlPatterns("/*");
return filterRegistrationBean;
}
- ๋ชจ๋ ์์ฒญ์ ๋ํด์ ํํฐ๊ฐ ์ ์ฉ๋๋๋ก /*๋ก ๊ฒฝ๋ก๋ฅผ ์ง์ ํ๊ณ , ์์ ์ญ์ ๊ฐ์ฅ ๋จผ์ ์งํํด์ฃผ์๋ค.
(๊ฐ์์์๋ ๋ก๊ทธ ํํฐ๋ฅผ ์ ์ฉํ์ง๋ง, ๋ก๊ทธ๋ ๋์ค์ AOP๋ฅผ ํตํด ์ค์ ํ๋ ๊ฒ ๋์ ๊ฒ ๊ฐ์์)
[LMemberController.java]
@PostMapping("/login")
public String login(@Valid @ModelAttribute("loginForm") LMemberLoginRequest loginRequest,
@RequestParam(defaultValue = "/login/") String redirectURL,
BindingResult bindingResult, HttpServletRequest request) {
if(bindingResult.hasErrors()) {
return "login/members/loginForm";
}
LMember loginMember = service.login(loginRequest);
if(loginMember == null) {
bindingResult.reject("loginFail", "์์ด๋ ๋๋ ๋น๋ฐ๋ฒํธ๊ฐ ์ผ์นํ์ง ์์ต๋๋ค.");
return "login/members/loginForm";
}
HttpSession session = request.getSession();
session.setAttribute("loginMember", loginMember);
return "redirect:" + redirectURL;
}
- ํํฐ์์ ๋ณด๋ธ ์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ(redirectURL) ๊ฐ์ ๋ฐ์์ ๋ก๊ทธ์ธ ์ ํด๋น url๋ก ๋์ด๊ฐ๋๋ก ์งํํด์ค๋ค.
- ๋ฌผ๋ก , ๊ทธ๋ฅ ๋ฐ๋ก ๋ก๊ทธ์ธํ ์ฌ์ฉ์์ ๊ฒฝ์ฐ ์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ ๊ฐ์ด ์์ ํ ๋ defaultValue์ธ /login/๋ก ์ค์ ๋์ด ํ ํ๋ฉด์ผ๋ก ์ด๋ํ๋ค.
| ์คํ๋ง ์ธํฐ์ ํฐ
๐ฉ HTTP ์์ฒญ -> WAS -> ํํฐ -> ์๋ธ๋ฆฟ -> ์คํ๋ง ์ธํฐ์ ํฐ -> ์ปจํธ๋กค๋ฌ
- ์คํ๋ง ์ธํฐ์ ํฐ๋ ์๋ธ๋ฆฟ๊ณผ(์คํ๋ง์์๋ DispatcherServlet) ์ปจํธ๋กค๋ฌ ์ฌ์ด์์ ํธ์ถ๋๋ค.
- ์ธํฐ์ ํฐ๋ ์ฌ์ฉ์์ ์์ฒญ์ด ์ ์ ํ์ง ์๋ค๊ณ ํ๋จํ๋ฉด ์ปจํธ๋กค๋ฌ๋ก ์์ฒญ์ ๋ณด๋ด์ง ์๊ณ ์ธํฐ์ ํฐ์์ ๋์ ๋ธ๋ค.
- ๋ง์ฐฌ๊ฐ์ง๋ก ์์ ๋กญ๊ฒ ์ธํฐ์ ํฐ ์ฌ๋ฌ ๊ฐ๋ฅผ ์ถ๊ฐํ ์ ์๋ค.
- ์ธํฐ์ ํฐ ์ฌ์ฉ์ ์ํด์๋ HandlerInterceptor ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๋ค.
-โญ preHandle (์ปจํธ๋กค๋ฌ ํธ์ถ ์ ), postHandler (์ปจํธ๋กค๋ฌ ํธ์ถ ํ), afterCompletion(์์ฒญ ์๋ฃ ์ดํ)
- ์๋ธ๋ฆฟ ํํฐ๋ HttpServletRequest/Response๋ง ์ ๊ณตํ์ง๋ง, ์ธํฐ์ ํฐ๋ Handler ์ ๋ณด๋ ๋ฐ์ ์ ์๋ค.
- ๋ํ, ์ถ๊ฐ์ ์ผ๋ก ModelAndView๋ Exception ์ ๋ณด๋ ๋ฐ์ ์ ์๋ค!
โ ์ฝ๋ ํ์ธ
public interface HandlerInterceptor {
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
}
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable Exception ex) throws Exception {
}
}
- postHandle์์๋ ModelAndView๋ฅผ, afterCompletion์์๋ Exception ์ ๋ณด๋ฅผ ๋ฐ์ ์ ์๋ค.
๐ cf) ํํฐ์ ์ธํฐ์ ํฐ์ ์ฐจ์ด๋ ๋ค์ ๋ธ๋ก๊ทธ์ ์ ๋ง ์ ์ ๋ฆฌ๋์ด ์๋ค...!
โ ํธ์ถ ํ๋ฆ
- ์ฌ์ฉ์์ ์์ฒญ -> Dispatcher Servlet
-> preHandle() ํธ์ถ -- (true ๋ฐํ) // false๋ผ๋ฉด ๋ค์ ๋จ๊ณ ์งํ X
--> ํธ๋ค๋ฌ ์ด๋ํฐ
-> ํธ๋ค๋ฌ -- (ModelAndView ๋ฐํ)
-> ๋ฐ์๋ modelAndView, ๊ทธ๋ฆฌ๊ณ ํธ๋ค๋ฌ ์ ๋ณด์ ํจ๊ป postHandle() ํธ์ถ
-> view๋ฅผ ํตํ ๋ ๋๋ง ์งํ (render(model))
-> afterCompletion() ํธ์ถ
โ ์์ธ๊ฐ ๋ฐ์ํ์ ๊ฒฝ์ฐ
- ์์ฒญ -> Dispatcher Servlet
-> preHandle() ํธ์ถ -- (true ๋ฐํ) // false๋ผ๋ฉด ๋ค์ ๋จ๊ณ ์งํ X
--> ํธ๋ค๋ฌ ์ด๋ํฐ
-> ํธ๋ค๋ฌ -- ์์ธ ๋ฐ์
-> ๋ค์ ํธ๋ค๋ฌ ์ด๋ํฐ๋ก ๋์์ค๋ฉด์ ์์ธ๊ฐ ํจ๊ป ์ ๋ฌ
-> ์ด๋, Dispatcher Servlet์ ์์ธ๊ฐ ๋ค์ด์ค๋ฉด postHandle ํธ์ถ X
-> afterCompletion ํธ์ถ (์์ธ ์ ๋ฌด๋ ์๊ด X) -- ๊ทธ๋์ ํ๋ผ๋ฏธํฐ๋ก ์์ธ ์ ๋ณด๋ฅผ ๋ฐ์์ ์ถ๋ ฅ ๊ฐ๋ฅ
์ธํฐ์ ํฐ๋ addPathPatterns, excludePathPatterns์ผ๋ก ์ธํฐ์ ํฐ๋ฅผ ๋ฑ๋กํ ํจํด, ์ ์ธํ ํจํด์ ๋ฑ๋กํ ์ ์๋ค.
- ํํฐ๋ addUrlPatterns์ผ๋ก ์ถ๊ฐํด์ค ์ ์์๋๋ฐ, ์ด๋ ํจํด์ ๋ฑ๋กํ๋ ๊ท์น ์ญ์ ๋ค๋ฅด๋ค.
ํํฐ๋ ์ง์ญ๋ณ์๋ฅผ ์ ์ธํ์ฌ ๊ณตํต์ ์ผ๋ก ๊ฐ์ ์ ๊ทผํ ์ ์์ง๋ง, ์ธํฐ์ ํฐ๋ ์์ฒญ๋ง๋ค ์ ์ฉ๋๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ ์ง์ญ๋ณ์ ์ฌ์ฉ์ด ์ ๋๋ค.
- ๊ทธ๋์ preHandle <=> postHandle ๋ฑ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ ๋๋ request.setAttribute / getAttribute๋ฅผ ํตํด ์์ฒญ์ ๋ฃ์ด๋๊ณ ์๋ก ์ ๊ทผํ๋ฉด ๋๋ค.
[LoginCheckInterceptor.java]
public class LoginCheckInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String requestURI = request.getRequestURI();
HttpSession session = request.getSession(false);
if(session == null || session.getAttribute("loginMember") == null) {
response.sendRedirect("/login/members/login?redirectURL="+ requestURI);
return false;
}
return true;
}
}
- ํํฐ์ ๋ค๋ฅด๊ฒ whiteList ๊ฒ์ฆ ๊ฐ์ ๊ฑธ ์ธํฐ์ ํฐ ๋จ์์์ ํ์ง ์์๋ ๋๋ค.
- ๋์ค์ ์ธํฐ์ ํฐ๋ฅผ ๋ฑ๋กํ ๋ ์ด์ ๋ํด ์ ์ฉํด์ฃผ๋ฉด ๋๋ค.
- ๋ก๊ทธ์ธ์ ์ปจํธ๋กค๋ฌ๋ก ์์ฒญ์ด ๋ค์ด๊ฐ๊ธฐ ์ ์ ํด์ผ ๋๋ ๊ฒ์ด๋๊น preHandle๋ง ๊ตฌํํด์ค๋ค.
[WebMvcConfig.java]
@Override
public void addInterceptors(InterceptorRegistry registry) {
...
registry.addInterceptor(new LoginCheckInterceptor())
.addPathPatterns("/login", "/basic/items")
.excludePathPatterns("/login/", "/login/members/add",
"/login/members/login", "/login/members/logout", "/css/*");
}
- ๋ค์๊ณผ ๊ฐ์ด ์ธํฐ์ ํฐ๋ฅผ ์ถ๊ฐํด์ค๋ค. excludePathPattern์ whiteList์ ์๋ ๊ฒฝ๋ก๋ค์ ์ ์ด์ฃผ๋ฉด ๋๋ค.
| ArgumentResolver๋ฅผ ํ์ฉํ ๋ก๊ทธ์ธ ํ์ธ ๊ธฐ๋ฅ
[Login.java] - ์ปค์คํ ์ด๋ ธํ ์ด์
@Target(value = ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface Login {
}
[LoginMemberArgumentResolver.java]
public class LoginMemberArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
boolean hasParameterAnnotation = parameter.hasParameterAnnotation(Login.class);
boolean hasMemberType = LMember.class.isAssignableFrom(parameter.getParameterType());
return hasParameterAnnotation && hasMemberType;
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest request = (HttpServletRequest) webRequest;
HttpSession session = request.getSession(false);
if(session == null) {
return null;
}
return session.getAttribute("loginMember");
}
}
- ๋ง๋ค์ด์ค ์ด๋ ธํ ์ด์ ์ ๋ํ ๋ฆฌ์กธ๋ฒ๋ฅผ ์์ฑํ๋ค.
- supportParameter๋ ํ๋ผ๋ฏธํฐ์ ๋ถ์ ์ด๋ ธํ ์ด์ ๊ณผ ์ด๋ ธํ ์ด์ ์ ๋ถ์ ํ๋ผ๋ฏธํฐ ํ์ ์ ๋ํ ๊ฒ์ฆ์ ์งํํ๊ณ ,
resolveArgument๋ ์ค์ง์ ์ผ๋ก ํด๋น ์ด๋ ธํ ์ด์ ์ด ์ฒ๋ฆฌํ ์ผ์ ๋ช ์ํด์ค๋ค. (๋ง์ฐฌ๊ฐ์ง๋ก ๊ทธ๋ฅ ์ธ์ ๊ฒ์ฆ์ ์งํํ๋ค)
[LHomeController.java]
@Controller
@RequiredArgsConstructor
@RequestMapping("/login")
public class LHomeController {
@GetMapping("/")
public String home(
@Login LMember loginMember,
Model model) {
if (loginMember == null) {
return "login/home";
}
model.addAttribute("member", loginMember);
return "login/loginHome";
}
}
- ๋ง๋ค์ด์ค ์ด๋ ธํ ์ด์ ์ ์ปจํธ๋กค๋ฌ์ ํ๋ผ๋ฏธํฐ์ ๋ถ์๋ค.
- ์ปจํธ๋กค๋ฌ์ ์ฝ๋๊ฐ ์๋นํ ๊ฐ๋จํด์ง๋ ๊ฑธ ๋ณผ ์ ์๋ค.
- ์ด๋ฌ๋ฉด ํด๋น loginMember์ ํ์ ์ด LMemberํ์ด๋ผ๋ฉด ์ธ์ ์์ "loginMember"์ ๊ฐ์ ๊ฐ์ ธ์์ loginMember์ ๋ฃ์ด์ค๋ค.
[WebMvcConfig.java]
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new LoginMemberArgumentResolver());
}
- ๋ค์๊ณผ ๊ฐ์ด ๋ง๋ค์ด์ค argumentResolver๋ฅผ ์ถ๊ฐํด์ค๋ค.- ์ด๋ ๊ฒ ๋ก๊ทธ์ธ ์ฒ๋ฆฌ๋ฅผ ์งํํ๋ ๋ค์ํ ๋ฐฉ๋ฒ์ ๋ชจ์ํด๋ณด์๋ค.
- ๋ค์์๋ ์์ธ ์ฒ๋ฆฌ ๋ฐ ์ค๋ฅ ํ์ด์ง ์์ฑ์ ๋ํด์ ์์๋ณด์.