이 글은 Spring Boot 2.7.4 버전을 기준으로 작성되었습니다.
SpringBoot에서 @RequestMapping으로 API의 경로를 지정할 때, 상위 컨트롤러에 정의된 @RequestMapping을 오버라이드(Override)하지 않고, 상속하여 이어주는 방법은 아래와 같습니다. 예를 들어, API 컨트롤러에 대한 공통영역을 정의한 BaseApiController 클래스가 존재하고, 하위에 각 업무 또는 서비스별로 컨트롤러를 생성한다고 가정하였을 때, 아래와 같은 코드를 갖게 됩니다.
// BaseApiController.java
@RestController
@RequestMapping("/v1")
public abstract class BaseApiController {
...
}
// ServiceApiController.java
@RestController
@RequestMapping("/service")
public class ServiceApiController extends BaseApiController {
@GetMapping("/request")
public ResponseEntity request() {
return ResponseEntity.ok().build();
}
}
위와 같은 형태로 서버를 구동할 경우, ServiceApiController에 존재하는 request 함수에 접근하기 위해서는 /service/request로 호출해야 접근할 수 있습니다. 부모 컨트롤러에 선언된 @RequestMapping 정보는 자식 컨트롤러에 선언된 @RequestMapping에 의해 오버라이드(Override) 되므로 /v1의 값은 사라지게 됩니다. 이 때, 부모 컨트롤러에 선언한 @RequestMapping의 값을 상속받아서 /v1/service/request 의 경로를 생성하기 위해서는 아래와 같이 설정이 필요합니다.
RequestMappingHandlerMapping 생성
RequestMappingHandlerMapping은 RequestMappingHandler를 매핑해주는 클래스입니다. RequestMappingHandler는 각 함수에 선언된 @RequestMapping 값을 가져와서 DispatcherServlet이 어떤 URL로 요청이 왔을 때, 특정 컨트롤러로 보낼 수 있도록 매핑정보를 만들어주는 역할을 담당합니다. 따라서 매핑정보를 만드는 과정에서 오버라이드(Override) 되는 부분을 수정해야하므로 기본적으로 사용되는 RequestMappingHandlerMapping를 상속받아서 새로운 클래스를 아래와 같이 생성합니다.
// CustomRequestMappingHandler.java
public class CustomRequestMappingHandler extends RequestMappingHandlerMapping {
@Override
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
RequestMappingInfo requestMappingInfo = super.getMappingForMethod(method, handlerType);
// 요청 URL 정보가 없을 경우 null을 반환합니다.
if (requestMappingInfo == null) {
return null;
}
List<String> superclassUrlPatterns = new ArrayList<>();
Class<?> superclass = handlerType.getSuperclass();
// Method의 상위 클래스를 모두 검색하면서
// 클래스에 @RequestMapping 값이 존재할 경우 Pattern을 추가합니다.
for (; superclass != Object.class; superclass = superclass.getSuperclass()) {
if (superclass.isAnnotationPresent(RequestMapping.class)) {
superclassUrlPatterns.add(0, superclass.getAnnotation(RequestMapping.class).value()[0]);
}
}
// 상위 클래스에 @RequestMapping이 존재했다면
// Pattern을 기준으로 RequestMappingInfo를 생성하고, 기존 RequestMapping 정보와 결합합니다.
if (!superclassUrlPatterns.isEmpty()) {
RequestMappingInfo superclassRequestMappingInfo = RequestMappingInfo.paths(String.join("", superclassUrlPatterns)).build();
return superclassRequestMappingInfo.combine(requestMappingInfo);
} else {
// 상위 클래스에 @RequestMapping이 없는 경우
// 기존 RequestMappingInfo를 그대로 반환합니다.
return requestMappingInfo;
}
}
}
이렇게 생성한 RequestMappingHandler 정보를 Dispatcher Servlet이 초기화될 때, 사용될 수 있도록 DelegatingWebMvcConfiguration을 상속받는 클래스를 생성합니다.
// RequestMappingConfiguration.java
@Configuration
public class RequestMappingConfiguration extends DelegatingWebMvcConfiguration {
@Override
protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
return new CustomRequestMappingHandler();
}
}
위와 같이 2개의 클래스를 생성하고, 서버를 구동한 뒤 /v1/service/request로 접속을 한다면 응답을 받을 수 있습니다.
'SpringBoot' 카테고리의 다른 글
spring-boot-devtools 설정 (0) | 2023.03.07 |
---|
댓글