OpenFeign

미미·2025년 6월 30일

Spring

목록 보기
4/4

OpenFeign

OpenFeign은 Netflix에 의해 처음 만들어진 Declarative(선언적인) HTTP Client 도구로써, 외부 API 호출을 쉽게 할 수 있도록 도와준다. 여기서 선언적인 이란 어노테이션 사용을 의미하는데, Open Feign은 인터페이스에 어노테이션들만 붙여주면 구현이 된다.

장점

  • 인터페이스와 어노테이션 기반으로 작성할 코드가 줄어든다
    @Component
    @RequiredArgsConstructor
    class ExchangeRateRestTemplate {
    
        private final RestTemplate restTemplate;
        private final ExchangeRateProperties properties;
        private static final String API_KEY = "apikey";
    
        public ExchangeRateResponse call(final Currency source, final Currency target) {
            return restTemplate.exchange(
                            createApiUri(source, target),
                            HttpMethod.GET,
                            new HttpEntity<>(createHttpHeaders()),
                            ExchangeRateResponse.class)
                    .getBody();
        }
    
        private String createApiUri(final Currency source, final Currency target) {
            return UriComponentsBuilder.fromHttpUrl(properties.getUri())
                    .queryParam("source", source.name())
                    .queryParam("currencies", target.name())
                    .encode()
                    .toUriString();
        }
    
        private HttpHeaders createHttpHeaders() {
            final HttpHeaders headers = new HttpHeaders();
            headers.add(API_KEY, properties.getKey());
            return headers;
        }
    }
    @FeignClient(name = "ExchangeRateOpenFeign", url = "${exchange.currency.api.uri}")
    public interface ExchangeRateOpenFeign {
    
        @GetMapping
        ExchangeRateResponse call(
                @RequestHeader String apiKey,
                @RequestParam Currency source,
                @RequestParam Currency currencies);
    
    }
    
  • 익숙한 Spring MVC 어노테이션으로 개발이 가능하다
  • 다른 Spring Cloud 기술들(Eureka, Circuit Breaker, LoadBalancer)과의 통합이 쉽다

단점 및 한계

  • 기본 Http Client가 Http2를 지원하지 않음 → Http Client에 대한 추가 설정 필요
  • 경우에 따라서 애플리케이션이 뜰 대 초기화 에러가 발생할 수 있음 → Object Provider로 대응 필요
  • 테스트 도구를 제공하지 않는다.

시작하기

1. 의존성 추가하기

Open Feign은 Spring Cloud 기반의 기술이므로 Spring Cloud에 대한 의존성이 필요하다.

Spring Cloud 문서

[아래는 스프링 3.2.x 기준]

  • spring cloud 의존성
    ext {
        set('springCloudVersion', "2023.0.4")
    }
    
    dependencyManagement {
        imports {
            mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
        }
    }
  • open Feign 의존성
    implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'

2. OpenFeign 활성화하기

@EnableFeignClients 어노테이션을 붙여주면 된다.

Main에 @Enable 어노테이션을 붙여주는 것은 SpringBoot가 제공하는 테스트에 영향을 줄 수 있으므로 별도의 config 파일로 만들어주자. 별도의 파일로 설정할 경우에는 feign 인터페이스들의 위치를 반드시 지정해주어야한다.

@Configuration
@EnableFeignClients(basePackages = "Journey.Together.global.external")
class OpenFeignConfig {

}

3. FeignClient 구현하기

API 호출을 수행할 클라이언트는 인터페이스에 @FeignClient 어노테이션을 붙여주면 된다. 그리고 value와 url설정이 필요한데 url에는 호출할 주소를, value에는 임의의 client의 이름을 적어주면 된다.

value는 다른 스프링 클라우드와의 통합 시에 SpringCloud LoadBalancer Client를 만드는 데 사용된다는데 중요하지 않으므로 넘어가도 되지만, 필수값이므로 반드시 입력해주어야 한다. value와 url에는 placeholder가 사용가능하므로 프로퍼티에 작성해둔 값을 사용하면 된다.

@FeignClient(
	name = "discord-client",
	url = "${discord.webhook-url}",
	configuration = DiscordFeignConfiguration.class)
public interface DiscordClient {

	@PostMapping()
	void sendAlarm(@RequestBody DiscordMessage message);
}
  • 여러 파라미터를 한번에 넘겨주고싶은 경우에는 @SpringQueryMap 사용가능
  • 동적인 URI로 호출이 필요하다면 기존에 url에는 아무값이나 넣고 URI를 파라미터로 사용해 호출해주면된다.
    @FeignClient(name = "ExchangeRateOpenFeign", url = "USE_DYNAMIC_URI")
    public interface DynamicUrlOpenFeign {
    
        @GetMapping
        ExchangeRateResponse call(
                URI uri
                @RequestHeader String apiKey,
                @RequestParam Currency source,
                @RequestParam Currency currencies);
    
    }

4. 타임아웃 설정

OpenFeign이 Defaut로 갖는 timeout 설정은 다음과 같다.

  • connectTimeout : 1000
  • readTimeout: 60000

원하는 시간으로 변경하려면 다음과 같이 설정할 수 있다 (ex. 5초)

 feign:
    client:
        config:
            default:
                connectTimeout: 5000
                readTimeout: 5000

5. 재시도 설정하기

Feign은 기본적으로 Retryer.NEVER_RETRY를 등록하여 Retry를 시도하지 않으므로 Retry를 시키려면 추가적인 설정이 필요하다.

예를 들어 0.1초의 간격으로 시작해 최대 3초의 간격으로 간격이 점점 증가하며, 최대 5번 재시도하는 Retryer는 다음과 같이 설정할 수 있다. 다만 Feign이 제공하는 Retryer는 IOException이 발생한 경우에만 처리되므로, 이외의 경우에도 재시도가 필요하다면 Spring-Retry를 이용하거나 에러디코더 혹은 인터셉터로 직접 구현하는 등의 방법을 사용해야 한다

@Configuration
@EnableFeignClients("com.mangkyu.openfeign")
class OpenFeignConfig {

    @Bean
    Retryer.Default retryer() {
        // 0.1초의 간격으로 시작해 최대 3초의 간격으로 점점 증가하며, 최대5번 재시도한다.
        return new Retryer.Default(100L, TimeUnit.SECONDS.toMillis(3L), 5);
    }
}

재시도는 조심해서 설정되어야 한다. 만약 어떤 서버에서 장애가 발생해서 이제 막 복구가 되었는데, 우리의 서버가 재시도(Retry)를 적용하여 수 많은 동시 요청을 보내게 된다면 다시 장애를 유발하는 Retry Storm을 일으킬 수 있기 때문이다.

6. 요청/응답 로깅 설정하기

Logger의 이름은 전체 인터페이스 이름이며, Feign Client들마다 만들어진다. Feign은 남길 로그에 따라 4가지 수준을 제공한다.

  • NONE: 로깅하지 않음(기본값)
  • BASIC: 요청 메소드와 URI와 응답 상태와 실행시간만 로깅함
  • HEADERS: 요청과 응답 헤더와 함께 기ㄹ본 정보들을 남김
  • FULL: 요청과 응답에 대한 헤더와 바디, 메타 데이터를 남김
@Configuration
@EnableFeignClients("com.mangkyu.openfeign")
class OpenFeignConfig {

    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }
}

Feign은 DEBUG 레벨로만 로그를 남길 수 있다. 그러므로 반드시 로그 레빌이 DEBUG로 설정되어 있어야한다.

logging:
    level:
        com.mangkyu.openfeign.app.openfeign: DEBUG

7. LocalDate, LocalDateTime, LocalTime 등을 위한 설정하기

주고 받는 데이터 타입으로 LocalDate, LocalDateTime, LocalTime 등이 사용된다면 제대로 처리가 되지 않는다. 그러므로 관련 포매터 추가 설정이 필요하다.

@Bean
public FeignFormatterRegistrar dateTimeFormatterRegistrar() {
    return registry -> {
        DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
        registrar.setUseIsoFormat(true);
        registrar.registerFormatters(registry);
    };
}

라이브러리가 존재하지 않는다면 아래의 2가지 의존성을 추가해주면 된다.

implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8'
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'

참고링크

[Spring] OpenFeign이란? OpenFeign 소개 및 사용법

0개의 댓글