Spring

[Spring] OpenFeign 기본부터 알아보기

SungminKim 2024. 10. 13. 21:41

 


 

 

 

1. 개요

현재 참여중인 프로젝트에서는 백엔드 애플리케이션간의 HTTP 통신을 위해 Spring Cloud OpenFeign을 사용하고 있습니다. 기존 제가 담당하는 업무에서는 직접적으로 다뤄볼 일이 없었으나, 최근 새로 배정 받은 업무로 인해 Feign을 제대로 사용해볼 기회가 생겼습니다. 

"이 기회에 사용할 기술을 제대로 알고 사용해보자."라는 취지로 공식문서에 기재된 기본적인 구성 정보들을 학습했으며, 이번 글에서는 이러한 학습을 통해 알게된 내용과 그 과정에서 발생한 궁금증을 해소하여 정리한 내용을 공유드리려 합니다.

 

 

 

2. Open Feign

OpenFeignJava HTTP 클라이언트 작성 과정을 간소화하기 위한 라이브러리입니다. 즉, Spring 프레임워크를 사용하지 않아도 사용가능합니다. 원래는 Feign이 Spring Cloud Netflix의 일부로 제공되었지만, Spring Cloud Netflix가 유지보수 종료를 선언하면서 주요 기능이 대체 라이브러리로 이전되었습니다. 그 중 하나가 OpenFeign으로, Spring Cloud 릴리즈에 독립적인 프로젝트로 제공되어 현재의 Spring Cloud OpenFeign으로 거듭나게 되었다고 합니다.

 

 

 

3. Spring Cloud OpenFeign

Spring Cloud OpenFeign 공식문서에서는 "Feign은 선언적 웹 서비스 클라이언트이다." 라고 정의하고 있습니다. 사실 저는 정의에서 부터 막혔습니다('선언적 뭐,뭐라고?'). 그래서 정의에서 명시된 '선언적'의 의미는 무엇인지 부터 알아보았습니다.

 

 

 

선언적 웹 서비스 클라이언트란?

"선언적이란, 어떤 내용을 명시하여 말 그대로 '선언'하는 것을 의미합니다."
"Feign은 'REST 클라이언트를 선언적으로 처리할 수 있게 해 주는 도구'입니다."

 

저는 위 두 사항을 기반으로 선언적 웹서비스 클라이언트에 대해 "무엇"을 할 것인지를 명시한 내용(선언)을 통해 HTTP 통신 처리하는 클라이언트" 라고 정의내릴 수 있었습니다.

그리고 이러한 정의를 통해 Feign은 "개발자가 HTTP 요청을 어떻게 보내는지에 대한 구체적인 구현을 작성할 필요 없이, 요청의 형태와 결과만을 선언하여 네트워크 통신을 처리함으로 비즈니스에 집중할 수 있게 만들어 주는 도구"라는 것을 알게 되었습니다.

위와 같은 과정을 통해 개발자가 구체적인 구현을 작성하지 않도록 하는 요소들을 우선적으로 파악해야겠다는 동기를 얻게 되었으며, 다음 순서로 구체적인 구현을 위한 구성 요소들이 무엇인지에 대해 알아보았습니다.

 


 

 

Spring Cloud OpenFeign은 Feign을 위해 다음과 같은 bean을 기본적으로 제공하고 있습니다. 

 

Decoder와 Encoder

Decoder와 Encoder는 Feign 클라이언트가 외부 API와 통신할 때, HTTP 요청/응답 데이터를 변환하는 데 도와주는 역할을 합니다. 그리고 Spring Cloud는 Feign과의 통합을 위해 SpringEntityDecoder, SpringEncoder라는 구현체를 제공하고 있습니다. 이러한 구현체는 Spring에서 제공하는 HttpMessageConverters를 기반하고 있으며, 이를 통해 Spring MVC에서 사용하는 데이터 변환 로직을 Feign에서 그대로 사용할 수 있게 된 것입니다.

 

 

기본적으로 @FeignClient가 명시된 클라이언트 인터페이스의 경우 별도의 설정없이 Default 클래스가 적용됩니다. 그러나 특정 상황에서는 디코딩시 별도의 로직이 들어가야 될 경우도 존재합니다. 이 경우 사용자는 별도의 커스텀화한 Configuration을 만들고 @FeignClient 어노테이션에 해당 커스텀 Configuration 클래스를 지정함으로써 해당 Feign 클라이언트만을 개별적으로 설정할 수 있습니다.

 

커스텀 Configuration 적용 예시

@Configuration
public class CustomFeignConfig {

    @Bean
    public Decoder feignDecoder() {
       ObjectFactory<HttpMessageConverters> objectFactory = () -> new HttpMessageConverters(
             new MappingJackson2HttpMessageConverter(customObjectMapper()));
       return new OptionalDecoder(new ResponseEntityDecoder(new SpringDecoder(objectFactory)));
    }
    
    /**
    * 네이밍 전략을 이용한 SNAKE_CASE 변환
	**/
    private ObjectMapper customObjectMapper() {
    	ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setPropertyNamingStrategy(new CustomPropertyNamingStrategy());
        objectMapper.findAndRegisterModules();
        return objectMapper;
    }
}
@FeignClient(name = "sample", url = "http://localhost:8081",
			 configuration = CustomFeignConfig.class
             )
public interface SampleFeignClient{

    @GetMapping("/search")
    public List<OrderInfo> searchOrderInfo(@SpringQueryMap SearchQuery query);
}

 

 

Logger

Logger는 Feign 클라이언트에서 발생하는 HTTP 요청/응답에 대한 로그를 기록하는 역할을 담당하고 있습니다. 사용자는 Logger.Level 을 통해서 로그의 세부 수준을 설정하여 로그의 어떤 정보를 기록할지 간단하게 제어가 가능합니다.

  • NONE: 로그를 기록하지 않음.
  • BASIC: 요청 메서드와 URL, 응답 상태 코드, 실행 시간을 기록.
  • HEADERS: 요청/응답의 헤더와 기본적인 정보만 기록.
  • FULL: 요청/응답 본문, 헤더, 메타데이터 등 모든 정보를 기록.

 

 

Slf4jLogger란?

Feign에서 slf4j(Simple Logging Facade for Java)를 사용하여 로그를 기록하기 위한 구현체입니다. Logback 또는 Log4j와 같은 로깅 프레임워크와 함께 로그를 중앙에서 관리하기 위해 주로 사용합니다.

 

 

 

Micrometer

Micrometer애플리케이션 모니터링 라이브러리로 다양한 모니터링 시스템과 연동을 지원하여 애플리케이션의 메트릭을 간편하게 수집할 수 있도록 도와주는 도구입니다. Spring Boot Actuator와 함께 사용할 경우, Micrometer는 자동으로 애플리케이션의 주요 메트릭을 수집하고 노출시키기 때문에 추가적인 설정 없이도 쉽게 모니터링이 가능합니다.

Feign은 사용자가 Feign 클라이언트 API 호출에 대한 메트릭 수집할 수 있도록 Micrometer 구현체를 별도로 제공하고 있으며, 이를 통해 사용자는 다음과 같은 기본적인 메트릭을 간편하게 수집할 수 있습니다.

 

  • 요청 빈도 (Request Count) : API가 얼마나 자주 호출되었는지 측정
  • 성공/실패 요청 비율 (Success/Failure Rate) : API 요청 중에서 성공한 요청과 실패한 요청의 비율을 측정
  • 응답 시간 (Response Time / Latency) : API 호출에 대한 응답 시간(Latency)을 측정
  • 에러율 (Error Rate) : 호출된 API 요청 중 에러가 발생한 비율을 측정
  • 재시도 횟수 (Retry Count) : API 호출이 실패했을 때 재시도한 횟수를 측정
  • 타임아웃 횟수 (Timeout Count) : API 호출 시 타임아웃이 발생한 횟수를 측정
  • 응답 코드 분포 (Response Code Distribution) : API 호출 결과의 HTTP 응답 코드 분포를 측정
  • 호출 실패 원인 분석 (Failure Cause Analysis) : API 호출 실패 시 실패 원인(네트워크 문제, 서버 다운 등)을 추적한 측정
  • 현재 활성화된 세션 수 (Active Session Count) : Feign 클라이언트의 현재 활성화된 연결 수 또는 세션 수를 추적한 측정
  • 호출의 분산 상태 (Request Distribution) : 로드 밸런싱이 제대로 이루어지고 있는지 파악하기 위한 측정

 

 

MicrometerCapability

MicrometerCapability는 전통적인 MeterRegistry기반의 메트릭 수집 방식을 사용하는 구현체입니다. feign-micrometer가 classpath에 포함되어 있는 상태에서 ObservationRegistry가 없을 경우 활성화 되며, Spring Boot의 버전이 3.0 이전에서 주로 사용됩니다.

기본적으로 단순한 숫자 기반 메트릭만을 수집이 가능하며, 복잡한 트랜잭션 또는 마이크로서비스 간의 호출 흐름 추적하기 위해서는 사용자가 별도의 정의를 통해 수동으로 메트릭을 수집하고 이를 MeterRegistry에 등록하는 방식의 커스텀 작업이 필요합니다.

 

 

MicrometerObservationCapability

Feign 클라이언트에서 관찰(Observation) 기능을 활성화하는 구현체입니다. Feign 클라이언트가 외부 API와 통신할 때 발생하는 메트릭을 수집하고, 이를 Observation API를 통해 모니터링 시스템에 전달하는 역할을 담당합니다.

 

이를 사용하기 위해서는 feign-micrometer가 classpath에 포함되어 있는 상태에서 ObservationRegistry를 사용 가능한 상태여야합니다. Spring Boot 3.x 부터는 micrometer-observation이 기본적으로 포함되어 있기 때문에 의존성 추가 없이 바로 사용 가능합니다.

ObservationRegistry는 ‘관찰(Observation)’이라는 개체를 생성하여 특정 비즈니스 로직이 실행되는 동안의 메트릭을 수집하거나, 비동기 작업에서 컨텍스트를 스레드 간에 전파하여 일관된 추적 정보를 유지할 수 있는 기능을 제공합니다. 이러한 기능을 통해 단순한 숫자 기반 메트릭만을 수집하는 것을 넘어서서 상황에 맞춘 메트릭 및 분산 추적 데이터를 수집하는 것이 가능하며, 추적 정보까지 결합하여 보다 깊이 있는 분석이 가능합니다.

 

 

 

CachingCapability

CachingCapability는 Feign 클라이언트에서 캐싱을 사용하도록 지원하는 기능입니다. 이를 통해 외부 API 호출 결과를 캐시하여 반복적인 API 호출을 방지하고 성능을 최적화할 수 있습니다. 해당 구현체를 활성화하기 위해서는 Spring의 @EnableCaching 을 통해 캐싱 기능이 활성화된 상태여야합니다.

Feign CachingCapability 사용 예시

1. Springboot Application class에 @EnableCaching 추가

@SpringBootApplication
@EnableCaching
public class FeigntempleteApplication {

	public static void main(String[] args) {
		SpringApplication.run(FeigntempleteApplication.class, args);
	}

}

 

2. cacheManager 설정

@Configuration
public class CacheConfig {

    @Bean
    public CacheManager cacheManager() {
        SimpleCacheManager cacheManager = new SimpleCacheManager();
        cacheManager.setCaches(Arrays.asList(
            new ConcurrentMapCache("searchOrderResponses")
        ));
        return cacheManager;
    }
}

 

3. 캐싱 가능한 응답을 위해 @Cacheable 적용

@FeignClient(name = "sample", url = "http://localhost:8081")
public interface SampleFeignClient {

	@Cacheable("searchOrderResponses")
	@GetMapping("/search")
	public List<OrderInfo> searchOrderInfo(@SpringQueryMap SearchQuery query); 
}

 

 

 

 

Contract

Contract는 Feign 클라이언트가 인터페이스를 기반으로 HTTP 요청을 어떻게 구성하고 처리할지 결정하는 계약(Contract)을 의미하며, 인터페이스에 정의된 메서드와 어노테이션을 해석하여 HTTP 메서드 추출, URL 경로 추출, 헤더 설정, 요청 본문을 처리하는 역할을 수행합니다.

 

Contract를 사용하는 두 가지 방식

1. SpringMvcContract 구현체 사용

@FeignClient(name = "sample", url = "http://localhost:8081")
public interface SampleFeignClient {

	@GetMapping("/search")
	public List<OrderInfo> searchOrderInfo(@SpringQueryMap SearchQuery query);
	
}

SpringMvcContrac란, Spring MVC web bind 어노테이션을 Feign 클라이언트에서 사용할 수 있도록 해석하는 Feign Contract의 구현체입니다. Spring Cloud OpenFeign은 @FeignClient를 사용할 때 기본적으로 SpringMvcContract를 사용함으로 별도로 Contract를 지정하지 않아도 Spring MVC 스타일의 어노테이션을 사용할 수 있도록 편의성을 제공하고 있습니다.

 Spring MVC web bind 어노테이션이란, HTTP 요청을 Controller의 특정 메서드에 매핑시키기 위해 사용되는 어노테이션들을 의미합니다. (RequestMapping, PostMapping, PutMapping, DeleteMapping, PatchMapping)

 

2. 기본 Feign 방식 사용

@FeignClient(name = "sample", url = "http://localhost:8081")
public interface SampleFeignClient {

	@RequestLine("GET /search")
	public List<OrderInfo> searchOrderInfo(@QueryMap SearchQuery query);
	
}

Spring Cloud OpenFeign이 아닌 Feign은 기본적으로 자체적인 Contract를 사용하여 동작하며 이를 FeignContract 라고 부릅니다. 그리고 이러한 FeignContract는 Feign 자체의 어노테이션(@RequestLine, @Body 등)을 사용하여 API 요청을 처리할 수 있습니다.

만약, Spring Cloud OpenFeign을 사용하는 상태에서 기본적인 Feign 어노테이션 (@RequestLine 등) 방식을 사용하고 싶다면, 커스텀하게 FeignContract 를 지정하여 사용이 가능합니다.

@Bean
public Contract feignContract() {
    return new Contract.Default();  // 기본 FeignContract 사용
}

 

 

 

Feign.Builder와 Client

Spring Boot와 Spring Cloud는 Feign 클라이언트를 사용할 경우 자동 설정(@FeignClient)을 통해 Feign의 Client를 생성합니다. 이러한 자동화를 통해 HTTP 요청에 대한 구체적인 구현 사항을 은닉하여 비즈니스 로직과의 관심사 분리를 가능하게 되었습니다. 

하지만, 특정 상황에서는 개발자가 Client를 직접 다뤄야 할 경우도 존재합니다. 이때, 필요한 것이 Feign.Builder 입니다.

 

Feign.Builder는 개발자가 Feign 클라이언트를 직접 구성하여 Client 생성할 수 있도록 지원하기 위한 빌더입니다. 위 이미지 예시와 같이 동일한 FeignClient를 서로 다른 설정으로 사용할 경우 유의미하게 사용할 수 있습니다.

 

 

FeignBlockingLoadBalancerClient

프로젝트의 classpath에 spring-cloud-starter-loadbalancer 가 존재할 경우 자동으로 사용되는 Client 구현체로써, 로드 밸런싱을 통해 여러 서비스 인스턴스 간에 HTTP 요청을 분산시키는 역할 수행합니다. 

여기서 로드밸런싱은 “클라이언트 사이드 로드밸런싱”을 의미합니다. 해당 내용은 본 글에서 다루기에는 내용이 광범위 하다고 판단됩니다. 아래의 관련 링크를 참고 부탁드립니다.

https://velog.io/@ayoung3052/클라이언트-사이드-로드-밸런싱-FeignClient와-Ribbon
https://pixx.tistory.com/277#로드 밸런싱란❓-1

 

 

 

4. 마무리

지금까지 Spring Cloud OpenFeign으로 통합된 Feign의 구성요소들에 대해 알아보았습니다. 가장 기본적인 내용만으로도 글이 많이 길어지는 것 같아 생략한 것들이 많습니다. 본 글에서 담지 못한 내용들과 구체적으로 사용해본 경험들을 녹여 후속편으로 만들 수 있도록 노력해보겠습니다. 

지금까지 읽어주셔서 감사합니다~!