본문 바로가기
🪲 bugs

Error: Springboot ArgumentResolver를 거치지 않는 예외

by iirin 2023. 9. 25.

발생 상황

  • 관리자 권한 검증을 위한 어노테이션과 ArgumentResolver를 구현했음에도 예외가 발생하였습니다.
org.springframework.dao.InvalidDataAccessApiUsageException: The given id must not be null
	at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:371) ~[spring-orm-6.0.11.jar:6.0.11]
	at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:234) ~[spring-orm-6.0.11.jar:6.0.11]
	at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:550) ~[spring-orm-6.0.11.jar:6.0.11]
	at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61) ~[spring-tx-6.0.11.jar:6.0.11]
	at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242) ~[spring-tx-6.0.11.jar:6.0.11]
	at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:152) ~[spring-tx-6.0.11.jar:6.0.11]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.0.11.jar:6.0.11]
	at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:164) ~[spring-data-jpa-3.1.3.jar:3.1.3]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.0.11.jar:6.0.11]
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97) ~[spring-aop-6.0.11.jar:6.0.11]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.0.11.jar:6.0.11]
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:244) ~[spring-aop-6.0.11.jar:6.0.11]
	at jdk.proxy2/jdk.proxy2.$Proxy166.existsById(Unknown Source) ~[na:na]
	at project.labelingtool.backend.app.member.adapter.out.persist.MemberRepository.existById(MemberRepository.java:45) ~[main/:na]
	at project.labelingtool.backend.app.member.application.service.MemberService.checkExistMember(MemberService.java:55) ~[main/:na]
	at project.labelingtool.backend.app.admin.adapter.in.rest.AdminFacade.getAllBills(AdminFacade.java:30) ~[main/:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343) ~[spring-aop-6.0.11.jar:6.0.11]
  • 대충 파라미터가 null로 들어오고 있는 상황으로보입니다. 디버깅 모드로 찍어봤을 때 filter까지는 잘 거치나 controller에서는 파라미터가 못들어오고 있었습니다.

 

  • 아래는 구현한 코드입니다.
// controller

@GetMapping("/bills")
    public BaseResponse<AdminBills.Response> getbills(AdminBills.Request request, @Admin MemberSummary memberSummary) throws MemberException {
        AdminBills.Response allBills = adminFacade.getAllBills(request, memberSummary);
        return BaseResponse.send("조회가 완료되었습니다.", allBills);
    }
// annotation

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface Admin {
    String value() default "";
}
// argument resolver

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

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(Admin.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) webRequest.getNativeRequest();
        Object logined = httpServletRequest.getAttribute("logined");
        log.debug("test = {}",((MemberSummary)logined).getMemberId().toString());
        if (logined==null) {
            throw new AuthorizationServiceException("로그인이 필요한 기능입니다.");
        }
        if (((MemberSummary) logined).getRole()!= Role.ADMIN) {
            throw new AccessDeniedException("관리자 권한이 필요한 기능입니다.");
        }
        return logined;
    }
}
  • 디버깅 모드에서 ArgumentResolver 브레이킹 포인트도 안먹고 로그도 안찍히는 것으로 보아 아예 ArgumentResolver를 거치지 않고 있음이 확인되었습니다.

 

발생 원인

  • 어이없게도 원인은 WebMvcConfigurer에 ArgumentResolver로 등록하지 않았기 때문이었습니다.
  • 어이없는 실수에 황당하지만 잊지 않기 위해서 이렇게 실수를 기록해둡니다. (내 2시간... 🥲)

 

대처

  • ArgumentResolver를 등록하였습니다..
@Configuration
@RequiredArgsConstructor
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(new LoginArgumentResolver());
        resolvers.add(new AdminArgumentResolver());
    }
    // 기타등등
}

Refs.

 

스프링에서 Argument Resolver 사용하기

컨트롤러에서 쿼리 스트링을 변수에 바인딩하려면 @RequestParam 을, 가변적인 경로를 변수에 바인딩하려면 @PathVariable 을, HTTP Body를 변수에 바인딩하려면 @RequestBody 를 사용해야한다. 하지만 HTTP Head

hudi.blog