SpringBoot Custom Annotation 생성하기 - (1) 파라미터에 부여

단비·2023년 5월 18일
0

학습

목록 보기
54/66

Argument Resolver

  • API 엔드포인트로 인입된 데이터를 가공 및 바인딩 할 때 사용하는 객체
  • http body 또는 url 파라미터로 넘어오는 데이터들은, @ReqeustBody와 @RequestParam 등으로 바인딩 할 수 있지만,
    http 헤더, 쿠키, 세션 등으로 전달되는 데이터인 경우에는 Argument Resolver를 이용할 수 있음
  • 대표적으로 세션에서 로그인한 사용자의 정보를 얻거나, 헤더로 전달되는 토큰에서 사용자의 정보를 얻을 때 사용

Argument Resolver가 호출되는 시기

컨트롤러의 메소드 찾기 → Argument Resolver 처리 → 컨트롤러 메소드 실행




커스텀 어노테이션 만들기


1. 어노테이션 선언

  • 메소드 변수를 통해 세팅값을 정의할 수 있음
@Inherited
@Retention(value = RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface AccessRole {
	// 사용 시 @AccessRole(firValue = "", secValue = "")
    String firValue();
    String secValue();
}

@Inherited

  • 부모 클래스에 Annotation이 선언될 경우 자식클래스에게도 상속시킴

@Retention

이름설명
RUNTIME컴파일 이후에도 참조 가능
CLASS클래스를 참조할 때까지 유효
SOURCE컴파일 이후 어노테이션 정보 소멸
(컴파일러에 의해 폐기)

@Target

이름설명
PACKAGE패키지 선언 시
TYPE타입(클래스, 인터페이스, enum) 선언 시
CONSTUCTOR생성자 선언 시
FIELDenum 상수를 포함한 멤버변수 선언 시
METHOD메소드 선언시
ANNOTATION_TYPE어노테이션 타입 선언 시
LOCAL_VARIABLE지역변수 선언 시
PARAMETER파라미터 선언 시
TYPE_PARAMETER파라미터 타입 선언 시



2. Resolver 클래스 구현

  • yml에 선언된 key값을 가져오기 위해 ApplicationSetting 를 읽어왔으며
    이를 위해 component로 선언함.
@Component
@RequiredArgsConstructor
public class AccessResolver implements HandlerMethodArgumentResolver {
    private final ApplicationSetting setting;
    private String key;

    @PostConstruct
    protected void init() {
        key = setting.getJwt_key();
    }

    // 컨트롤러의 메서드에 존재하는 파라미터들에 대해서 검사하여, 적용 여부를 판단
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        // 파라미터 타입이 String 인지 확인
        return parameter.getParameterType().equals(HashMap.class);
    }

    // supportsParameter() 가 true값을 return하면 실행되는 메소드
    @Override
    public HashMap<String, String> resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) webRequest.getNativeRequest();
        String token =
                httpServletRequest.getHeader("Authorization")
                        .split("Bearer ")[1];

        String role = Jwts.parser()
                .setSigningKey(key.getBytes("UTF-8")) // Set Key
                .parseClaimsJws(token).getBody().getSubject();

        return new HashMap<String, String>(){{
            put("role", role);
            put("token", token);
        }};
    }
}

supportsParameter()

  • 주어진 메소드의 파라미터가 이 Argument Resolver에서 지원하는 타입인지 검사
    (지원한다면 true 를, 그렇지 않다면 false 를 반환)

resloveArgument()

  • supportsParameter() 가 true값을 return하면 실행되는 메소드
  • 파라미터에 전달할 객체를 return



3. Configuration 클래스 구현

  • WebMvcConfigurer의 구현체를 만들고,
    addArgumentResolvers 메소드의 List에 구현체를 추가
@Configuration
@RequiredArgsConstructor
public class WebConfig implements WebMvcConfigurer {
    private final AccessResolver accessResolver;

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(accessResolver);
    }
}



4. Annotation 사용

@PostMapping("/read")
@ResponseBody
public ApiDTO read(
	@AccessRole HashMap<String, String> jwt,
	@RequestBody ReadRequest request
){
	...








💡 TIPS!


커스텀 어노테이션에서 발생한 Exception을 넘기는 방법


나의 경우 컨트롤러에서 모든 Exception을 처리하게 되어있는데,

Resolver에서 발생한 Exception을 Response 데이터로 활용하기 위해선
컨트롤러로 Exception을 넘겨주어야 했다.

그냥 throw new exception종류(); 또는 catch 문에 throw e; 를 해도
컨트롤러에는 넘어가지 않고 Resolver에서 Exception 처리가 완료되었다.


여러 방법을 시도 해보다가 Exception 값을 return으로 넘겨주는 것으로 성공했다!

Resolver 클래스

@Override
public HashMap<String, Object> resolveArgument(

	...
    
	} catch (MalformedJwtException e){
		// 토큰을 확인할 수 없을 때
		return new HashMap<String, Object>() {{
			put("exception", e);
		}};
	} catch (ExpiredJwtException e) { // 토큰이 만료되었을 경우
		return new HashMap<String, Object>() {{
			put("exception", e);
		}};
	}
}

컨트롤러

@PostMapping("/read")
@ResponseBody
public ApiDTO read(
	@AccessRole HashMap<String, Object> jwt,
	@RequestBody ReadRequest request
) throws Throwable {
	try{
		if(jwt.get("exception") != null)
			throw (Throwable) jwt.get("exception");
    
    ...
    // 캐치문 처리





참고사이트

[JWT] 세션 의존성 제거하기 - 커스텀 어노테이션 활용 - LJH
스프링에서 Argument Resolver 사용하기 - hudi.blog

profile
tistory로 이전! https://sweet-rain-kim.tistory.com/

0개의 댓글