Spring MVC에서 부정 매개변수(!
접두사가 붙은 매개변수)는 URL 매핑을 더 세밀하게 제어하기 위한 기능이다. @RequestMapping(params = {"!expiration"})
과 같은 형태로 사용되며, "expiration 매개변수가 없는 요청"을 특정 메서드로 라우팅하는 역할을 한다.
Spring Cloud OpenFeign 4.0.x에서 4.1.4로 업그레이드하면서 다음과 같은 오류가 발생했다:
Caused by: java.lang.IllegalArgumentException: Negated params are not supported: !sync
at org.springframework.cloud.openfeign.support.SpringMvcContract.parseParams(SpringMvcContract.java:372)
오류 메시지가 명확히 말해주듯, Spring Cloud OpenFeign 4.1.4 버전에서는 부정 매개변수를 더 이상 지원하지 않는다.
부정 매개변수는 다음과 같은 중요한 역할을 한다:
아래는 issue에 등록되었던 예제이다:
public interface TokenApi {
@PostMapping(path = "/api/token", params = {"!expiration"})
String create();
@PostMapping(path = "/api/token", params = {"expiration"})
String create(@RequestParam LocalDateTime expiration);
}
@FeignClient(value = "token", url ="${feign.client.config.token.uri}", contextId = "tokenClient")
public interface TokenClient extends TokenApi {
}
@RestController
public class TokenController implements TokenApi {
// 구현 내용
}
부정 매개변수 지원이 제거된 이유를 이해하려면 두 프레임워크의 차이를 알아야 한다:
Spring MVC (서버 측):
Feign (클라이언트 측):
GET /api/token?expiration=2023-12-31
또는 GET /api/token
중 하나만 가능하다.이런 차이로 인해 Feign 관점에서 부정 매개변수는 의미가 없어서 지원이 제거되었다.
처음에는 다음과 같은 접근 방식을 제안했다:
이 방식은 사용자에게 더 많은 제어 권한을 주면서 하위 호환성도 유지하는 접근법이라고 생각했다.
그러나 프로젝트 메인테이너는 더 간단한 접근법을 제안했다: 경고 로그만 남기고 부정 매개변수를 무시하는 방식. 이 방식이 최종적으로 채택되었다.
private void parseParams(MethodMetadata data, Method method, RequestMapping methodMapping) {
String[] params = methodMapping.params();
if (params == null || params.length == 0) {
return;
}
for (String param : params) {
NameValueResolver nameValueResolver = new NameValueResolver(param);
if (!nameValueResolver.isNegated()) {
data.template().query(resolve(nameValueResolver.getName()), resolve(nameValueResolver.getValue()));
}
else {
// 예외를 발생시키는 대신 경고 로그만 남긴다
log.warn("Negated param '{}' is ignored in Feign client. This may cause unexpected behavior " +
"if you're sharing interfaces between server and client.", param);
// 부정 매개변수는 무시된다
}
}
}
처음에는 설정 옵션을 추가하는 방식을 제안했지만, 더 깊이 생각해보니 로그만 남기는 간단한 해결책으로도 충분했다. 그 이유는:
부정 매개변수는 클라이언트 측에서 실제로 의미가 없다. Feign이 HTTP 요청을 생성할 때는 매개변수를 포함하거나 포함하지 않는 두 가지 방법만 있다. 부정 매개변수는 단지 "이 매개변수를 포함하지 마라"라는 의미인데, 이는 결국 매개변수를 추가하지 않는 것과 동일하다.
HTTP 요청 자체는 변하지 않는다. 예외를 던지든 로그만 남기든, 결과적으로 생성되는 HTTP 요청은 같다. 어차피 부정 매개변수가 있으면 해당 매개변수를 제외하고 요청을 만들기 때문이다.
서버 측 동작은 영향 받지 않는다. Spring MVC에서는 여전히 부정 매개변수를 정상적으로 처리한다.
이 이슈를 해결하면서 몇 가지 중요한 점을 배웠다:
문제의 본질을 파악하는 중요성: 처음에는 부정 매개변수를 지원하는 설정 옵션을 추가하는 방향으로 생각했지만, 결국 문제의 본질은 "부정 매개변수가 클라이언트에서 어차피 의미가 없다"는 점이었다. 근본 원인을 제대로 이해하면 더 적절한 해결책을 찾을 수 있다.
기능 제거의 의도 이해하기: Spring Cloud OpenFeign에서 부정 매개변수 지원을 제거한 것은 단순히 기능을 없애려는 게 아니었다. 서버는 요청을 받아 처리하는 역할, 클라이언트는 요청을 보내는 역할이라는 각자의 책임을 명확히 하기 위한 결정이었다. 클라이언트에서는 어차피 "이 매개변수가 없는 요청"이란 개념이 실제로 필요 없기 때문이다. 이런 의도를 이해하니 더 적절한 해결책을 찾을 수 있었다.
하위 호환성과 실용성의 균형: 때로는 엄격한 하위 호환성(예외 발생)보다 실용적인 접근(경고 로그)이 사용자에게 더 도움이 될 수 있다. 특히 실제 동작에 영향이 없는 경우에는 더욱 그렇다.
오픈소스 협업의 가치: 메인테이너와의 논의를 통해 더 간단하고 효과적인 방법을 발견할 수 있었다. 이것이 오픈소스 커뮤니티의 가치인가
GitHub PR 링크에서 확인할 수 있다.