Java - Spring &&n SpringBoot

Spring - ArgumentResolver (feat.커스텀 어노테이션, 세션)

TerianP 2022. 8. 17.
728x90

1. ArgumentResolver 개념 및 동작 과정

  • ArgumentResolver 는 Controller 에서 들어오는 데이터(파라미터)를 가공하여 필요한 데이터만 뽑는 로직이 필요할 때 사용한다.
  • 보통 @ModelAttribute 나 @Param 를 사용해서 이러한 처리를 하게 되는데 문제는 이렇게 받았을 때 내가 필요한 정보만 있는게 아니라 다른 정보가 함께 있어서 추가적으로 데이터를 파싱하여 필요한 정보를 뽑아내는 등 추가 작업이 필요한 경우도 있다.
  • 이를 위해서 사용하는 것이 바로 ArgumentResolver 이다! ArgumentResolver 는 HandlerMethodArgumentResolver 를 구현한 구현 클래스를 사용해서 애플리케이션에 맞는 새로운 Resolver 를 만들고, 실행 시 Resolver 리스트에 내가 만든 Resolver 을 추가함으로써 적용할 수 있다
  • ArgumentResolver 가 동작되기까지의 구체적인 순서는 다음과 같다.
    • 사실 ArgumentResolver 은 SpringIntercepter 보다 뒤에 실행되기 때문에 굳이 argumentResolver 을 사용하지 않고 Spring Intercepter 에서 처리해도 된다고 한다. 다만, 보다 구체적인 적용을 해야하는 경우에는 Resolver 이 사용하는게 원하는 로직을 구현하는데 더 좋다나 뭐라나…
사용자 요청 -> DispatcherServlet 동작 -> 요청한 URI 를 HandlerMapping 에서 검색 -> 
SpringIntercepter 처리 -> ArgumentResolver 처리 -> MessageConverter 처리

출처 : https://blog.neonkid.xyz/238

 

 

 

그래서 ArgumentResolver 가 정확히 뭘 위해 만드는건데?

ArgumentResolver 는 넘어오는 파라미터에서 내가 원하는 로직으로 내가 원하는 정보를 위해 사용한다.

즉 기존에 스프링에서 이미 만들어진 어노테이션을 사용하는 것뿐만 아니라 내가 직접 만든 커스텀 어노테이션을 사용해서 파라미터 안에서 데이터를 분리, 로직 처리가 가능하다!


ArgumentResolver 사용하기 : @Login 어노테이션 만들기, 로그인 세션 처리하기

기존에 사용하던 SessionAttribute 가 아닌 @Login 이라는 커스텀 어노테이션을 생성 후 세션 처리를 해보자!

 

1) Controller

  • 세션 처리를 위해 기존의 SessionAttribute 가 아닌 @Login 을 사용한다.
// ArgumentResolver 을 사용하면 @Login 어노테이션 뒤에 오는 Member 매개변수,
// 즉 Argument 에 대해서 해당 매개변수에 대한 정보가
// 세션에 담아져 있으면 로그인 사용자로 취급하여 로그인 사용자에 맞는 요청을 처리하고
// 아니면 로그인 사용자로 처리하지 않는다.
// 이때 @Login 어노테이션은 커스텀 어노테이션 - 사용자 정의 어노테이션 - 에 해당한다 => 직접 만들어야한다
@GetMapping(value = {"/home"}) // 스프링이 지원하는 세션 기능 : @SessionAttribute
public String LoginHomeV3ArgumentResolver(
        @Login Member loginMember, Model model) {

    // 로그인 안했을 시 => 세션이 없을 때
    // 스프링이 알아서 처리해줌 만세!

    /* 로그인 시도 시 */
    // 각각 로그인을 시도했으나 회원 코드가 없는 경우, 회원코드가 0 인 경우, 회원 코드가 0 이 아닌 경우
    if (loginMember == null) {

        return "newspringhome";

    } else if (loginMember.getMEMBERCODE() == 0) {

        model.addAttribute("member", loginMember);
        System.out.println("관리자 로그인 성공");

        return "newspringhome_admin";

    } else {
        model.addAttribute("member", loginMember);
        System.out.println("일반 회원 로그인 성공");
        return "newspringhome_login";
    }

}

 

2) Login : 커스텀 어노테이션 생성

package HJproject.Hellospring.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 {

}

 

3) LoginMemberArgumentResolver : HandlerMethodArgumentResolver 구현 클래스 생성

  • 여기서는 parameter 메서드를 사용해서 내가 원하는 어노테이션이 붙어있는지, 해당 파라미터 안에 있는 값이 내가 원하는 타입인지 확인 후 로직 처리를 실행한다 ⇒ hasLoginAnno && isMemberType 부분
  • 만약 두 가지 - hasLoginAnno && isMemberType - 가 모두 true 인 경우에만 resolveArgument 가 실행되며 이 안에서 HttpServletRequest 를 통해 확인했을 때 세션 정보가 있다면 해당 정보를 return 하고, 아니라면 null 을 return 한다.
package HJproject.Hellospring.argumentResolver;

import HJproject.Hellospring.Session.SessionConst;
import HJproject.Hellospring.domain.member.Member;
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("############# LoginMemberArgumentResolver 실행 ############");

        // hasParameterAnnotation 는 해당 메서드의 매개변수로 오는 어노테이션이 붙어있는지 여부를 반환
        // 붙어있으면 true, 아니면 false
        boolean hasLoginAnno = parameter.hasParameterAnnotation(Login.class);

        // 파라미터의 클래스 타입이 isAssignableFrom 클래스와 매개변수에 해당하는 arameter.getParameterType() 가
        // 동일한 클래스(타입)인지 여부 확인 => true, false
        boolean isMemberType = Member.class.isAssignableFrom(parameter.getParameterType());

        // hasLoginAnno 와 isMemberType 가 모두 true 일 때만 아래의 resolveArgument 가 실행됨
        return hasLoginAnno && isMemberType;
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
log.info("############# resolveArgument 실행 ############");

        // HttpServletRequest 를 가져와야함
        HttpServletRequest req = (HttpServletRequest)webRequest.getNativeRequest();

        // HttpServletRequest 에서 세션을 가져왔을 때 null 이면 그대로 null 을 return
        HttpSession session = req.getSession();
        if(session == null){
            return null;
        }
        // 세션이 있다면 해당 세션의 내용을 return
        return session.getAttribute(SessionConst.LOGIN_MEMBER);
    }
}

 

4) Config : Config 파일에 ArgumentResolver 를 등록

  • Config 클래스는 WebMvcConfigurer 를 구현한 구현 클래스여야 하며, addArgumentResolvers 를 어보라이드해서 ArgumentResolver 를 Bean 으로 등록 할 수 있다.
// ArgumentResolver 를 등록하기 위한 override
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(new LoginMemberArgumentResolver());
    }

- 코드 확인

ResolverArgument 실행 확인
관리자로 로그인 후 Model 에 로그인 내용이 담긴 것을 확인!!


- Reference

https://blog.neonkid.xyz/238

 

[Spring] Argument Resolver를 이용한 유연성 있는 파라미터 처리

서비스를 운영하다보면 다양한 종류의 데이터를 받게 됩니다. 그럴 때마다 Controller 부분에서 이를 전처리하게 되는데, 이렇게 되면 각 Controller에 전처리 해야 하는 코드를 함수화 하거나 Utils 클

blog.neonkid.xyz

 

댓글