Spring/Spring MVC

ArgumentResolver 활용

gunjoon98 2023. 4. 17. 15:50

ArgumentResolver

ArgumentResolver(정확히 HandlerMethodArgumentResolver)는 핸들러 메서드에서 사용되는 다양한 인자를 생성해 제공해주는 역할을 맡는다. 스프링이 기본으로 제공해주는 ArgumentResolver도 있지만 프로그래머가 직접 커스텀 ArgumentResolver를 만들어 핸들러 메서드에서 자신이 원하는 인자를 받을 수 있다.

 

아래는 세션 키를 받아 키에 해당하는 세션 데이터가 있으면(로그인 된 사용자이면) loginHome 뷰를, 세션 데이터가 없으면(로그인 되지 않은 사용자이면) Home 뷰를 응답하는 간단한 핸들러 메서드이다. 핸들러 메서드의 인자가 살짝 길어 가독성이 떨어진다. 커스텀 ArgumentResolver를 만들어 이를 해결해보자.

@GetMapping("/")
public String homeLoginV3Spring(
        @SessionAttribute(name = SessionConst.LOGIN_MEMBER, required = false) Member loginMember, Model model) {

    if(loginMember == null) {
        return "home";
    }

    model.addAttribute("member", loginMember);
    return "loginHome";
}

 

1. 핸들러 메서드 인자에서 사용할 로그인 애노테이션 생성

package hello.login.web.argumentResolver;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface Login {

}

 

2. 커스텀 ArgumentResolver 구현

package hello.login.web.argumentResolver;

import hello.login.domain.member.Member;
import hello.login.web.SessionConst;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

@Slf4j
public class LoginMemberArgumentResolver implements HandlerMethodArgumentResolver {
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        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 request = (HttpServletRequest) webRequest.getNativeRequest();
        HttpSession session = request.getSession(false);
        if(session == null) {
            return null;
        }

        return session.getAttribute(SessionConst.LOGIN_MEMBER);
    }
}

스프링은 resolveArgument()에서 반환한 객체를 핸들러 메서드의 인자로 전달한다.

 

3. 커스텀 ArgumentResolver 등록

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(new LoginMemberArgumentResolver());
    }
}

 

4. 로그인 애노테이션 사용

@GetMapping("/")
public String homeLoginV3ArgumentResolver(@Login Member loginMember, Model model) {

    if(loginMember == null) {
        return "home";
    }

    model.addAttribute("member", loginMember);
    return "loginHome";
}

위의 homeLoginV3Spring 핸들러보다 인자 부분이 간단해졌고 Login 애노테이션을 재사용할 수 있게 되었다.