일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 26 | 27 | 28 |
29 | 30 | 31 |
- must revalidate
- supportParameter
- max age
- 검증헤더
- 300
- 프록시 캐시 서버
- www-Authenticate
- no cache
- 프록시객체
- 양쪽 모두 값 설정
- 세션타임아웃설정
- 조건부요청
- Could not find or load main class worker.org.gradle.process.internal.worker.GradleWorkerMain
- http
- 서블릿http세션
- 서블릿필터
- HTTP상태코드
- 쿠키보안문제
- 세션만들어보기
- gradle오류
- 인증체크
- etag
- Expires
- 캐시
- HTTP API
- 쿠키생명주기
- hikaricp
- Not Modified
- resolveArgument
- UrlResource
- Today
- Total
복습을 위한
ArgumentResolver활용해보기 본문
ArgumentResolver
스프링에서 애노테이션 기반의 컨트롤러는 매우 다양한 파라미터를 사용할 수 있다. HttpServletRequest , Model 은 물론이고, @RequestParam , @ModelAttribute 같은 애노테이션 그리고 @RequestBody , HttpEntity 같은 HTTP 메시지를 처리하는 부분까지 매우 큰 유연함을 보여준다. 그럼 그걸 누가처리해줄까? 수많은 파라미터를 입맛에 맞게 누가 처리해줄까?
이렇게 다양한 파라미터를 유연하게 처리할 수 있는 이유가 바로 ArgumentResolver 덕분이다. 애노테이션 기반 컨트롤러를 처리하는 RequestMappingHandlerAdapter 는 바로 이 ArgumentResolver 를 호출해서 컨트롤러(핸들러)가 필요로 하는 다양한 파라미터의 값(객체)을 생성한다. 그리고 이렇게 파리미터의 값이 모두 준비되면 컨트롤러를 호출하면서 값을 넘겨준다. 스프링은 30개가 넘는 ArgumentResolver 를 기본으로 제공한다.
@ModelAttribute를 처리하는 ModelAttributeMethodProcessor
@PathVariable을 처리하는 PathVariableMethodArgumentResolver
@RequestParam을 처리하는 RequestParamMethodArgumentResolver
@RequestBody를 처리하는 RequestResponseBodyMethodProcessor
HttpServletRequest를 처리하는 ServletRequestMethodArgumentResolver
......
....등등...........
쉽게 말하자면 RequestMapping핸들러 어댑터가 핸들러(컨트롤러)를 보고 어떤 타입의 파라미터가 필요한지 인지한다.
예를들어 HttpServletRequest파라미터가 필요하다싶으면 이젠 ArgumentResolver에게 부탁을 하는 것이다. 그 타입에 맞는 파라미터를 가져와달라고 부탁을한다. 그럼 ServletRequestMethodArgumentResolver 는 어댑터의 부탁대로 요청메세지에서 입맛에 맞게 해당 타입 파라미터를 뽑아준다. 그것을 받고 준비가 다 된 핸들러어댑터는 이젠 파라미터 값을 핸들러(컨트롤러)로 넘겨준다. 비로소 객체를 전달하는 것이다. 그렇게 컨트롤러가 실행이 되는 것이다.
동작 방식
ArgumentResolver 의 supportsParameter() 를 호출해서 해당 파라미터를 지원하는지 체크하고, 지원하면 resolveArgument() 를 호출해서 실제 객체를 생성한다. 그리고 이렇게 생성된 객체가 컨트롤러 호출시 넘어가는 것이다.
이 ArgumentResolver를 사용해서 서블릿http세션에서 포스트했던 리다이렉트 홈화면 코드를 좀 더 줄여보자
@GetMapping("/")//스프링어노테이션 세션 활용..코드짧아진다 알아서 가져와서 찾아준다. //어노테이션이 세션생성은 안함
public String homeLoginV3Spring(
@SessionAttribute(name=SessionConst.LOGIN_MEMBER,required = false)Member loginMember, Model model){
이렇게말이다.
@GetMapping("/")//ArgumentResolver활용
public String homeLoginV3ArgumentResolver(@Login Member loginMember, Model model)
@Login 애노테이션이 있으면 직접 만든 ArgumentResolver 가 동작해서 자동으로 세션에 있는 로그인 회원을 찾아주고, 만약 세션에 없다면 null 을 반환하도록 개발해보자. 직접@Login애노테이션을 만드는 것이다.
먼저 Login인터페이스를 만들어주자.
@Target({ElementType.PARAMETER})//어노테이션을 어디에 사용할지 --파라미터에만사용
@Retention(RetentionPolicy.RUNTIME)//어노테이션이 얼마나 오랫동안 유지될지--리플렉션등을 활용할 수 있도록 런타임까지 에노테이션 정보가 남아있음
public @interface Login {
}
가장먼저 @Target과 @Retention을 추가해주자.
Java 어노테이션을 정의할 때 사용되는 두 가지 기본적인 메타 어노테이션(Meta-Annotation)이다.
어노테이션이 사용될 곳과 생명을 설정한다.
이젠 LoginMemberArgumentResolver클래스를 만들어주자
@Slf4j
public class LoginMemberArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
//핸들러어댑터에게 파라미터를 지원할지 말지 결정하는 메소드. 그러므로 검사를해야한다
// hasLoginAnnotation은 파라미터에 @Login 어노테이션이 붙어있는지 여부를 확인.
//hasMemberType은 파라미터의 타입이 Member 클래스나 그의 하위 클래스인지를 확인.
log.info("supportsParameter 실행");
boolean hasLoginAnnotation = parameter.hasParameterAnnotation(Login.class);//파라미터에 로그인 에노테이션이 붙어있는가물어봄
boolean hasMemberType = Member.class.isAssignableFrom(parameter.getParameterType());
return hasLoginAnnotation && hasMemberType;
//둘 다 해당되면 참을 반납한다. 그러면 아래 메소드가 실행된다.
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
log.info("resolverArgument 실행");
//HttpServletRequest 획득: webRequest.getNativeRequest()를 통해 현재 요청에 대한 HttpServletRequest 객체를 획득
HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
//세션 검사: request.getSession(false)를 호출하여 현재 요청의 세션을 가져옴, false를 인자로 주어 세션이 없으면 null을 반환.
HttpSession session = request.getSession(false);
if(session==null){
return null;
}
//세션에 LOGIN_MEMBER 속성 확인: 세션이 존재하고, 세션에 LOGIN_MEMBER라는 속성이 있다면 해당 속성의 값을 반환. 만약 세션이나 LOGIN_MEMBER 속성이 없다면 null을 반환
return session.getAttribute(SessionConst.LOGIN_MEMBER);
//반환된 파라미터는 이젠 핸들러어댑터거쳐서 컨트롤러 메서드 파라미터로 전달됨.
}
}
첫번째 supportParameter메소드에서 어댑터의 부탁대로 파라미터를 뽑아줄지 말지를 결정한다. 그러기 위해서 @Login어노테이션이 붙어있나 확인을 하고 파라미터 타입을 확인한다. 둘 다 참이면 resolveArgument메소드에서 세션을 검사하고 로그인처리를 해서 성공하면 실제 객체를 뽑아주고 핸들러 어댑터에게 넘긴다. 반환된 member객체는 컨트롤러의 메소드가 호출되면서 전달된다.
WebMvcConfigurer에 설정 추가
@Override//login어노테이션, argumentResolver활용을 위한 설정
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new LoginMemberArgumentResolver());
}
앞서 개발한 LoginMemberArgumentResolver 를 등록해주자.
사실 @SessionAttribute만써도 코드가 아주 짧아지지만 직접Login애노테이션을 만들어쓰니 더 짧고 간편하고 직관적으로 이해가 된다. . 이렇게 ArgumentResolver 를 활용하면 공통 작업이 필요할 때 컨트롤러를 더욱 편리하게 사용할 수 있다.
참고
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-2/dashboard