사용자 정의 어노테이션을 활용한 Profile 별 호출 가능 API 설정 - @LocalDevOnly

2024. 3. 14. 23:16자바/자바스프링

728x90
반응형

#1. 개요

  • 각 API 에 대해 prod / dev / local 어디에서 호출할 수 있는 지에 대한 설정 추가를 위해 어노테이션 생성
  • 기존 어노테이션으로는 각 API 가 아닌 Bean 단위로 Controller 속 API 전체에 대해 어느 서버에서 호출 할 수 있는 지 나타낼 수 있음
    • @Profile({"local"})
      • string[] 값 속에 어느 서버에서 해당 controller 속 API 들을 허용할 지 정하면 됨
    • @ConditionalOnExpression("'${spring.profiles.active}'.equals('dev')")
      • 위 같은 방법도 가능
  • 그러나 이 방법으로는 controller 내부 특정 API에 대해서 접근 제어를 할 수 없음.
    • 그래서 @**LocalDevOnly** 어노테이션을 만들어서, 특정 API에 대한 접근 제어를 가능케 함

#2. 사용방법

1. 사용 설명

@PutMapping(value = "/item")
@Operation(summary = "메뉴 수정")
@LocalDevOnly
public ResponseEntity<JsonResponse> putMenu(
        HttpServletRequest request,

// ... 중략 ... 
        @RequestBody @Valid MenuDto menuDto) {

// ... 중략 ... 

    return ResponseEntity.ok(response);
}
  • controller 내부 API 선언 위쪽에 어노테이션 달아주면 끝

2. 결과

  • 해당 API 가 호출되지 않는 서버에서 호출 되었을 경우 리턴 값.


#3. 구현

  • 어노테이션 정의
    • @Target
      • 이 어노테이션이 적용될 수 있는 대상을 method 등을 지정할 수 있음\
    • @Retention
      • 이 어노테이션이 유지되는 기간 설정
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface LocalDevOnly {
    
    }
    
    
  • 핸들러 인터셉터 정의
    • 정의된 어노테이션을 활용하여 어떤 기능을 실행할 지를 정하는 인터셉터 정의
      • 인터셉터: http 요청이 들어올 때마다 먼저 실행 됨
    • ${spring.profiles.active}
      • spring 의 profile 값 프로퍼티 를 가져옴. 위 변수에는 현재 어플리케이션의 활성 profile 이 저장되어있음
    • HandlerInterceptor
      • 스프링 MVC 에서 컨트롤러의 요청처리 전후에 추가적인 처리를 수행할 수 있도록 해줌
    • preHandle()
      • 우선 LocalDevOnly 어노테이션을 사용했는 지 확인
      • 어노테이션이 존재하면, Http 요청이 컨트롤러에 도달하기 전 실행되어서, profile 변수값이 조건에 맞지 않으면 NoHandlerFoundException을 던져 404 예외처리 핸들러 handle404에서 처리 될 수 있도록 한다.
    @Component
    public class LocalDevOnlyInterceptor implements HandlerInterceptor {
    
    	@Value("${spring.profiles.active}")
    	private String profilesActive;
    
    	@Override
    	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
    		throws Exception {
    		if (handler instanceof HandlerMethod) {
    			HandlerMethod handlerMethod = (HandlerMethod)handler;
    			Method method = handlerMethod.getMethod();
    			LocalDevOnly localDevOnly = method.getAnnotation(LocalDevOnly.class);
    			if (localDevOnly != null) {
    				if (!profilesActive.equals("local") && !profilesActive.contains("dev")) {
    					HttpHeaders headers = new HttpHeaders();
    					headers.add("Content-Type", "application/json");
    					throw new NoHandlerFoundException(request.getMethod(), request.getRequestURL().toString(), headers);
    				}
    			}
    		}
    		return true;
    	}
    }
    
    
  • 인터셉터 등록
    • WebMvcConfig 에서 정의한 인터셉터를 등록
    	@Override
    	public void addInterceptors(InterceptorRegistry registry) {
    		registry.addInterceptor(localDevOnlyInterceptor);
    	}
    
    

 

 

 

 

728x90
반응형