홈으로 돌아가기

HttpClient Spring 7: Feign 대체

Spring 7은 FeignClient의 네이티브 대안으로 선언형 HttpClient를 도입합니다. OpenAPI generator, virtual threads 및 Kotlin null-safety 지원으로 외부 API 통합이 간편해집니다. 설정, 유효성 검사 및 오류 처리 상세 설명.

Spring 7 HttpClient: Feign 교체 완전 가이드
Advertisement 728x90

Spring 7 선언적 HttpClient: FeignClient 대안

Spring 7은 RestClient와 WebClient를 기반으로 한 네이티브 선언적 HttpClient를 도입했습니다. 컨트롤러의 @GetMapping과 유사한 @GetExchange, @PostExchange 같은 익숙한 어노테이션을 사용합니다. 이를 통해 인터페이스만 정의하면 수동 구현 없이 외부 HTTP 요청을 처리할 수 있습니다.

주요 인터페이스 예시는 다음과 같습니다:

public interface UserClient {

    @GetExchange("/api/users/{id}")
    UserResponse getUser(
        @PathVariable("id") Long id,
        @RequestParam(name = "includeDetails", defaultValue = "false") boolean includeDetails,
        @RequestHeader("Authorization") String authToken,
        @RequestHeader("X-Request-Id") String requestId
    );

    @PostExchange("/api/users")
    UserResponse createUser(
        @RequestBody @Valid CreateUserRequest request,
        @RequestHeader("Authorization") String authToken
    );

    @PatchExchange("/api/users/{id}")
    void updateUserEmail(
        @PathVariable("id") Long id,
        @RequestParam("email") String email,
        @RequestHeader("Authorization") String authToken
    );
}

HttpClient는 기본적으로 관찰 가능성(observability)과 통합되며, 2022년부터 유지보수 모드인 FeignClient와 달리 Spring Cloud가 필요 없습니다.

Google AdInline article slot

OpenAPI Generator와의 통합

자동 클라이언트 생성을 위해 openapi-generatorspring-http-interface 라이브러리를 사용하세요.

Maven 플러그인 설정:

<plugin>
    <groupId>org.openapitools</groupId>
    <artifactId>openapi-generator-maven-plugin</artifactId>
    <version>${openapi-generator.version}</version>
    <executions>
       <execution>
    <id>generate-weather-client</id>
    <goals>
       <goal>generate</goal>
    </goals>
    <configuration>
       <inputSpec>${project.basedir}/src/main/resources/openapi/external/weather-api.yaml</inputSpec>
       <generatorName>spring</generatorName>
       <library>spring-http-interface</library>
       <output>${project.build.directory}/generated-sources/openapi-client</output>
       <apiPackage>ulllie.exchange.openapi.gen.client.api</apiPackage>
       <modelPackage>ulllie.exchange.openapi.gen.client.model</modelPackage>
       <generateApis>true</generateApis>
       <generateModels>true</generateModels>
       <generateSupportingFiles>false</generateSupportingFiles>
       <configOptions>
          <useSpringBoot4>true</useSpringBoot4>
          <useJackson3>true</useJackson3>
          <interfaceOnly>true</interfaceOnly>
          <skipDefaultInterface>true</skipDefaultInterface>
          <useBeanValidation>true</useBeanValidation>
          <annotationLibrary>none</annotationLibrary>
          <serializationLibrary>jackson</serializationLibrary>
          <useTags>true</useTags>
       </configOptions>
    </configuration>
</execution>
    </executions>
</plugin>

이렇게 생성된 인터페이스 예시:

Google AdInline article slot
public interface OpenMeteoApi { 
  @HttpExchange(method = "GET", value = "/v1/forecast")   
  ResponseEntity<ForecastResponse> getForecast( 
    @RequestParam("latitude") Double latitude, 
    @RequestParam("longitude") Double longitude 
  );
}

서비스 연결 및 설정

import를 통한 설정:

@Configuration  @ImportHttpServices(group = "weather", types = OpenMeteoApi.class)  
public class WeatherRestClientConfig implements RestClientHttpServiceGroupConfigurer {    
  @Override      
  public void configureGroups(Groups<RestClient.Builder> groups) { 
    groups.filterByName("weather").forEachClient(
      ($, builder) -> builder.baseUrl("https://api.weather.com") 
    );    
  }  
}

에러 처리는 defaultStatusHandler로 설정:

builder.baseUrl(properties.baseUrl())
  .defaultStatusHandler(
  status -> status.is4xxClientError() || status.is5xxServerError(),               
  errorHandler         
);

커스텀 HttpErrorHandler로 4xx/5xx 상태 코드 로직을 맞춤화하거나 Resilience4j와 통합, @ControllerAdvice를 사용할 수 있습니다.

Google AdInline article slot

주요 기능:

  • 인터셉터 설정
  • Resilience4j 통합
  • ResponseSpec 수준 에러 처리

Project Loom 환경에서 RestClient vs WebClient

가상 스레드(spring.threads.virtual.enabled=true)를 사용하면 블로킹 RestClient가 리액티브 프로그래밍 없이 I/O 확장성을 제공합니다.

장점:

  • 디버깅이 쉬운 명령형 코드
  • 리액티브 파이프라인 불필요
  • I/O 중심 작업에서 WebClient와 동등한 성능

리액티브 시나리오에서는 WebClient.Builder를 선택해 Mono/Flux 시그니처를 생성하세요.

검증 챌린지와 해결책

Bean Validation은 생성기가 @Valid를 생략해 중첩 객체로 재귀되지 않습니다.

해결책:

  • OpenAPI의 x-field-extra-annotation: @jakarta.validation.Valid — 스펙 변경 필요.
  • AlwaysTraversableResolver — 전체 객체 트리를 전역 순회하지만 사이클 및 성능 위험.
  • 커스텀 Mustache 템플릿 — 생성 시 @Valid 자동 추가.

보너스: 널 안전성을 위한 Kotlin 데이터 클래스 생성.

data class ForecastResponse(
  val temperature: Double,
  val windSpeed: Double?
)

의존성: kotlin-stdlib + jackson-module-kotlin. 널 위반 시 즉시 실패하는 역직렬화.

주요 요약

  • HttpClient는 Spring Framework 네이티브 — Spring Cloud 의존성 없음.
  • spring-http-interface와 OpenAPI 생성기로 즉시 사용 가능한 인터페이스 생성.
  • 가상 스레드로 리액티브 없이 I/O 간소화.
  • Kotlin 데이터 클래스로 널 문제 조기 발견.
  • 에러 처리와 검증 완전 커스터마이징 가능.

— Editorial Team

Advertisement 728x90

다음 읽기