카카오 로그인 구현하기(4) - 커스텀 어노테이션을 통해 사용자 정보, Token 정보 가져오기

1. 커스텀 어노테이션 도입

 

모든 API에서 동일한 코드가 반복되는걸 막기 위해, 커스텀 어노테이션을 만들어서 파라미터에서 사용자 정보 또는 토큰 정보를 가져올 수 있도록 하는 방법

 

2. Argument Resolver

 

2.1 Argument Resolver란?

 

Argument Resolver는 API 엔드포인트로 인입된 데이터를 가공 및 바인딩 할 때 사용하는 객체

 

  • http body 또는 url parameter로 넘어오는 데이터들은, @ReqeustBody와@RequestParam 등으로 바인딩이 가능하지만, http header, 쿠키, 세션 등으로 전달되는 데이터인 경우에는 Argument Resolver를 이용할 수 있음.

 

💡 Argument Resolver는 대표적으로 세션에서 로그인한 사용자의 정보를 얻거나, 헤더로 전달되는 인증 정보에서 토큰의 정보 및 사용자의 정보를 얻을 때 사용

 

2.2 Argument Resolver 호출 시기

 

Argument Resolver가 호출되는 시기는 handler adapter를 찾은 이후 Argument Resolver가 처리되고, 이후 Handler를 실행

 

💡 즉, 컨트롤러 method → Argument Resolver 처리 → 컨트롤러 method 실행

 

3. 토큰 추출 커스텀 어노테이션 만들기

 

3.1 어노테이션 선언

 

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

 

@Target

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

 

@Retention

RUNTIME 컴파일 이후에도 참조 가능
CLASS 클래스를 참조할 때 까지 유효
SOURCE 컴파일 이후 어노테이션 정보 소멸

 

3.2 Argument Resolver 구현

@Slf4j
@Component
@RequiredArgsConstructor
public class GetTokenArgumentResolver implements HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        boolean isString = parameter.getParameterType().equals(String.class);
        boolean isGetToken = parameter.hasParameterAnnotation(GetToken.class);

        return isString && isGetToken;
    }

    @Override
    public Object resolveArgument(
            MethodParameter parameter,
            ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest,
            WebDataBinderFactory binderFactory) {

        String refreshToken = webRequest.getHeader("Authorization");
        if (refreshToken == null || refreshToken.isEmpty()) {
            throw new GlobalException(GlobalErrorCode._UNAUTHORIZED);
        }
        if (!refreshToken.startsWith("Bearer ")) {
            throw new GlobalException(GlobalErrorCode.AUTH_INVALID_TOKEN);
        }

        return refreshToken.substring(7);
    }
}
  • supportsParameter() 컨트롤러의 method에 존재하는 파라미터들에 대해서 검사하여, 적용 여부를 판단
    1. GetToken.class (커스텀 어노테이션) 존재 여부
    2. parameter 타입이 String.class에 해당 여부

 

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

 

3.3 Resolver 추가

 

구현한 Resolver를 등록해줘야 동작을 함

 

@RequiredArgsConstructor
@Configuration
public class WebConfig implements WebMvcConfigurer {

    private final GetTokenArgumentResolver getTokenArgumentResolver;

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

 

WebMvcConfigurer의 구현체를 만들고 addArgumentResolvers 메서드를 오버라이딩 하여 List에 구현체를 추가

 

3.4 사용 방식

 

@Operation(summary = "토큰 재발급", description = "access 토큰이 만료된 경우 refresh 토큰을 통해 토큰을 재발급 합니다.")
@GetMapping("/kakao/reissue")
public ApiResponse<AuthResponseDTO.AuthToken> reissueToken(@GetToken String refreshToken) {
    return ApiResponse.onSuccess("토큰 재발급 성공", authService.reissueToken(refreshToken));
}

 

컨트롤러 method 파라미터에 적용

 

resolver가 인증 헤더에서 refresh Token을 추출하여 반환

 

참고

[JWT] 세션 의존성 제거하기 - 커스텀 어노테이션 활용

 

[JWT] 세션 의존성 제거하기 - 커스텀 어노테이션 활용

토큰 기반 인증 방식으로 소셜 로그인 및 회원가입 기능을 구현했다.그런데 구현하고 보니, 토큰을 세션에 저장하고 세션에서 해당 토큰을 꺼내서현재 로그인한 사용자를 가져오도록 구현을 했

velog.io