728x90
반응형
0. 이 글을 쓰는 이유
통신 과정에서 method에 대한 정보를 가져온 후 특정 어노테이션이 붙은 경우에 대해 필터링을 하는 방법을 정리하기 위함
1. 가장 필요한 건 HandlerMethod
Spring 공식 문서에 보면 HandlerMethod클래스에 이런 말이 적혀있다.
Provides convenient access to method parameters, the method return value, method annotations, etc.
메서드의 어노테이션이나 파라미터, 리턴 등에 대해 쉽게 제공할 수 있게 해준다고 한다. 이걸로는 정보가 좀 부족한 것 같다. 요새 ChatGPT가 코딩조무사처럼 잘 도와주고 있던데 한번 예제 코드를 달라고 해 봤다.
@RequestMapping("/users/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
User user = userService.getUserById(id);
return ResponseEntity.ok(user);
}
...
@RequestMapping("/users")
public ResponseEntity<User> createUser(@RequestBody User user) {
User createdUser = userService.createUser(user);
return ResponseEntity.ok(createdUser);
}
...
private HandlerMethod getHandlerMethod(HttpServletRequest request) {
RequestMappingHandlerMapping handlerMapping = new RequestMappingHandlerMapping();
handlerMapping.setDetectHandlerMethodsInAncestorContexts(true);
HandlerExecutionChain handler = handlerMapping.getHandler(request);
return (HandlerMethod) handler.getHandler();
}
...
// example usage
HttpServletRequest request = ... // create a mock request
HandlerMethod handlerMethod = getHandlerMethod(request);
Method method = handlerMethod.getMethod();
String methodName = method.getName();
String className = method.getDeclaringClass().getSimpleName();
System.out.println("Handler method: " + className + "." + methodName);
보니까 요청에 대해 HandlerMethod타입으로 형변환해서 해당 요청에 대해 handler를 반환해주고 있었다.
그러면 이 요청을 가로챌 수 있는 방법에 대해서도 필요했다. 이 또한 물어봐보자.
2. 그리고 다음은 HandlerInterceptor
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
String methodName = method.getName();
String className = method.getDeclaringClass().getSimpleName();
System.out.println("Handler method: " + className + "." + methodName);
}
return true;
}
// Other methods from HandlerInterceptor interface
}
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor());
}
}
이제 Interceptor에서 메서드에 대한 정보를 가져올 수 있게 되었다. 그러면 특정 메서드에 대해 하나하나 이름으로 체크하는 게 아니라 annotation을 붙이면 그 annotation이 붙은 메서드들만 골라서 핸들링할 수 있게 되었다.
3. 정말 그렇게 할 수 있는지 검증이 필요하다.
내가 생각하는 테스트 구조는 이렇게 흘러간다.
- controller를 하나 만든 후 getDataList()라는 메서드를 만든다.
- custom annotation으로 dataCheckerAnnotation이라는 메서드를 만든다.
- 해당 annotation을 getDataList()에 붙인다.
- Interceptor를 구현한 후 해당 어노테이션이 붙은 요청이 올 때 console에 print를 한다.
4. 검증 시작
public class TestInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
if(handlerMethod.getMethodAnnotation(DataCheckerAnnotation.class) != null){
String methodName = ((HandlerMethod) handler).getMethod().getName();
System.out.println("method : [" + methodName + "] is calling!");
}
}
return true;
}
// Other methods from HandlerInterceptor interface
}
@Configuration
class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TestInterceptor());
}
}
@RestController
@RequestMapping("/test")
public class TestController {
@DataCheckerAnnotation
@GetMapping("/data")
public DataList getDataList(){
List<String> dataList = new ArrayList<>();
dataList.add("data1");
dataList.add("data2");
dataList.add("data3");
return new DataList(dataList);
}
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataCheckerAnnotation {
}
이제 내가 예상하는 흐름대로 간다면 console에는 method : [getDataList] is calling! 이라는 문장이 찍혀야 한다.
잘 찍혀있다.
5. 결론
해당 방식으로 특정 어노테이션이 붙은 메서드의 호출에 대해 컨트롤을 할 수 있게 되었다.
ChatGPT는 좋은 코딩조무사다.
728x90
반응형
'JAVA > spring' 카테고리의 다른 글
SpringBoot 3.x 버전에서 junit5 를 사용해서 테스트하는데 왜 RunWith가 먹히지 않고 필요가 없는가? (0) | 2023.07.07 |
---|---|
스프링 데이터 jpa 공식 문서 읽으면서 끄적끄적 (0) | 2023.04.23 |
spring boot 최소한으로 logging 설정하기(logback을 곁들인) (0) | 2023.03.12 |
Flyway Placeholder사용(application.yml / properties 파일 읽기) (0) | 2023.02.26 |
spring(boot)의 예외 처리 방식 (0) | 2023.02.24 |