2 분 소요

 인프런 스프링 MVC 2편 - 백엔드 웹 개발 활용 기술편을 학습하고 정리한 내용 입니다.

ArgumentResolver 활용

mvc1편 - 요청 매핑 핸들러 어뎁터 구조에서 ArgumentResolver를 학습했다.

이 기능을 사용해서 로그인 회원을 조금 편리하게 찾아 보자.

HomeController 추가

1
2
3
4
5
6
7
8
9
@GetMapping("/")  
public String homeLoginV3ArgumentResolver(@Login Member loginMember, Model model) {  
  
    // 세션에 회원 데이터가 없으면 그냥 홈  
    if (loginMember == null) return "home";  
  
    model.addAttribute("member", loginMember);  
    return "loginHome";  
}

@Login이라는 커스텀 애노테이션이 생겼다.

이 애노테이션은 직접 만든 ArgumentResolver가 동작해서 자동으로 세션에 있는 로그인 회원을 찾아주고, 만약 세션에 없다면 null을 반환하도록 개발해 보자.

@Login 애노테이션 생성

다음과 같이 패키지 구성했고, Annotation을 만들었다.

hello.login.web.argumentresolver.Login

1
2
3
4
@Target(ElementType.PARAMETER)  
@Retention(RetentionPolicy.RUNTIME)  
public @interface Login {  
}

처음 인터페이스를 만들어 봐서 공부 해봤다.

1. @Target(ElementType.PARAMETER)

  • 어노테이션이 생성 될 수 있는 위치 지정
  • PARAMETER 로 지정하였으므로 메서드에 파라미터로 선언된 객체에서만 사용 가능
  • 이 외에도 클래스 선언문에서 쓸 수 있는 TYPE 등이 있음.

2. @interface

  • 이 파일을 어노테이션 클래스로 지정
  • @Login 애노테이션이 생겼다고 보면 됨.

3. @Retention

  • 애노테이션의 라이프 사이클
  • 즉 애노테이션이 언제까지 살아 있을지 결정하는 것.
  • SOURCE : 소스 코드 (.java) (ex. lombok - getter / 컴파일 하면 사라지는 이유!)
  • CLASS : 클래스 파일(.class) = 바이트 코드 (이거 ) (만약에 내가 애노테이션을 jar 배포해서 애노테이션을 잠재적으로 더 활용 가능하게 하기 위해.. )
  • RUNTIME : 런타임 까지 (= 즉 안사라짐) (ex. @Controller, @Service, @Autowired 등 스프링이 컴포넌트 스캔을 하는 방법(Reflection)이 애노테이션을 찾아서 사용.)

이제 이 애노테이션을 활용할 LoginMemberArgumentResolver을 만들어 보자.

LoginMemberArgumentResolver 구현

hello.login.web.argumentresolver.LoginMemberArgumentResolver

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
@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("resolveArgument 실행");  
  
        HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();  
        HttpSession session = request.getSession(false);  
        if (session == null) {  
            return null;  
        }  
        return session.getAttribute(SessionConst.LOGIN_MEMBER);  
    }  
}
  • supportsParameter() :
    • parameter.hasParameterAnnotation(Login.class) : 파라미터가 Login 애노테이션 인지 확인
    • Member.class.isAssignableFrom(parameter.getParameterType()) : 이게 Member 객체인지 확인
    • 두 개가 다 true여야 다음 resolveArgument()로 넘어감
  • resolveArgument()
    • 세션이 없으면 비회원 이기 때문에 null을 반환
    • 멤버 관련 세션이 있으면 회원이기 때문에 멤버 객체를 반환

LoginMemberArgumentResolver 등록

등록은 WebConfig에서 하겠다.

1
2
3
4
@Override  
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {  
    resolvers.add(new LoginMemberArgumentResolver());  
}

addArgumentResolvers을 오버라이드 해서

resolvers.add(new LoginMemberArgumentResolver()) 해주면 등록이 된다.

이제 결과를 보자.

최초 들어갈 때 LoginMemberArgumentResolver 가 판단해서 지금 멤버 정보가 없으므로 null을 반환했고,

1
2
3
4
5
6
7
8
9
@GetMapping("/")  
public String homeLoginV3ArgumentResolver(@Login Member loginMember, Model model) {  
  
    // 세션에 회원 데이터가 없으면 그냥 홈  
    if (loginMember == null) return "home";  
  
    model.addAttribute("member", loginMember);  
    return "loginHome";  
}

loginMember가 null이기 때문에 비회원 홈으로 간다.

그 다음 로그인 했을 때는, 이제 resolveArgument에서 멤버 객체 세션에 있기 때문에 그걸 반환했다.

그래서 회원 페이지로 이동했다.

참고로 supportsParameter는 캐시 기능이 있어서 @Login 검증을 스킵 했다.

댓글남기기