복습을 위한

ArgumentResolver활용해보기 본문

SpringMVC

ArgumentResolver활용해보기

ho042479 2024. 2. 1. 11:22

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

'SpringMVC' 카테고리의 다른 글

스프링 인터셉터  (0) 2024.01.31
서블릿 필터  (0) 2024.01.31
세션타임아웃설정  (0) 2024.01.31
서블릿http세션  (0) 2024.01.30
쿠키보안문제와 세션  (0) 2024.01.30