이끌든지 따르든지 비키든지

Framework/Spring

[Spring] OpenFeign 알아보기

SeongHo5 2024. 1. 9. 22:22

OpenFeign은 Spring Cloud 기반의 선언적 HTTP 클라이언트 도구로,  간편하게 외부 API를 호출 할 수 있는 도구이다.

 

어노테이션과 인터페이스 기반의 간소화된 코드 작성으로, 외부 API를 마치 로컬 메서드처럼 호출해서 사용할 수 있다.

(Spring Data JPA에서 인터페이스를 통해 DB 처리 메서드를 작성하는 것과 유사하다.)

 

OpenFeign의 장점

  • 인터페이스와 어노테이션 기반이므로, 이전 방식(RestTemplate 등)에 비해 작성할 코드를 줄일 수 있음
  • Spring MVC Annotation(@GetMapping, @RequestBody…)를 활용해 구성 가능
  • Spring Cloud의 다른 기술들( Eureka, Ribbon, Hystrix 등 )과 통합이 용이

 

 


시작하기

 

OepnFeign은 Spring Cloud 기반 기술로 주로 Spring Cloud 환경에서 사용되지만, 기존 방식의 Spring Boot 프로젝트에서도 활용이 가능하다.

 

// Gradle
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign:{version}'

 

(사용 중인 Spring Boot 버전에 맞게 OpenFeign의 버전을 명시해야 하는데, 아래 이미지와 링크를 참고)

 

 

Spring Cloud

Spring Cloud provides tools for developers to quickly build some of the common patterns in distributed systems (e.g. configuration management, service discovery, circuit breakers, intelligent routing, micro-proxy, control bus, short lived microservices and

spring.io

 

의존성을 추가했다면, 메인 클래스 또는 Configuration 클래스에 @EnableFeignClients 어노테이션을 추가한다.

 

@ComponentScan 이 스프링 컴포넌트를 스캔해 Bean으로 등록하는 것과 유사하게, @EnableFeignClients Feign 클라이언트를 스캔하고 등록하는 기능을 한다.

 

(※ Configuration 클래스에 @EnableFeignClients 어노테이션을 추가하는 경우, basePackages를 명시해줘야 함!)

 

// 메인 클래스에 추가하는 경우
@EnableFeignClients
@SpringBootApplication
public class FooApplication {

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


// Configuration 클래스에 추가하는 경우
@Configuration
@EnableFeignClients(basePackages = "com.example.demo")
public class FeignConfig {
    // 이 클래스 내에서 정의된 Bean과 설정은 모든 Feign Client에 적용된다.
}

 

 


설정하기

 

Feign Client의 동작을 제어하거나, 커스터마이징하려는 경우, Configuration 클래스를 정의할 수 있다.

 

 

  • 로깅 레벨 정의
    @Bean
    public Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL; // 옵션 : NONE, BASIC, HEADERS, FULL 
    }

 

NONE < BASIC < HEADERS < FULL 순으로 자세한 정보를 로깅한다.

  • NONE : 아무 정보도 로깅하지 않음
  • BASIC : 요청 메서드와 URL, 응답의 상태 코드 및 요청 처리 시간
  • HEADERS : BASIC에 더해 요청과 응답의 헤더 정보
  • FULL : BASIC과 HEADERS의 모든 정보와 모든 메타데이터

 

  • 재시도 정책
    @Bean
    public Retryer retryer() {
    	// Parameter : (long period, long maxPeriod, int maxAttempts)
        return new Retryer.Default(10L, 1L, 5);
    }

 

  • period : 재시도 간의 대기 시간 / default : 100밀리초
  • maxPeriod : 재시도 간의 최대 대기 시간 / default : 1초
  • maxAttempts : 최대 재시도 횟수 / default : 5회

 

  • 에러 처리 정책
    @Bean
    public ErrorDecoder errorDecoder() {
        return new YourErrorDecorder();
    }

 

 

ErrorDecoder의 decode 메서드를 오버라이드해 내가 사용할 에러 처리 정책을 정의하면 된다.

 

public class YourErrorDecorder implements ErrorDecoder {

    @Override
    public Exception decode(String methodKey, Response response) {
        // Handling Logic
    }
}

 

 

  • 요청 옵션 정의
    @Bean
    public Request.Options options() {
    	// Options(Duration connectTimeout, Duration readTimeout, boolean followRedirects)
        return new Request.Options(Duration.ofSeconds(10), Duration.ofSeconds(60), true);
    }

 

 

  • connectTimeout : 연결을 시도하는 최대 시간 / default : 10초
  • readTimeout : 응답을 기다리는 최대 시간 / default : 60초
  • followRedirects : HTTP 리디렉션을 자동으로 따를지 여부 / default : true

(※ 밀리초 단위로 Timeout을 설정하던 방식은 Deprecated되었으니, Duration 형식으로 입력해야 함! )

 

  • 인터셉터 정의
    @Bean
    public RequestInterceptor requestInterceptor() {
        return requestTemplate 
                -> requestTemplate
                .header("Content-Type", "application/json")
                .header("Authorization", "Bearer " + yourToken)
                // Custom Header
                .header("Custom-Header", "CustomValue")
                // Query Parameter
                .query("param1", "value1")
                .query("param2", "value2");
    }

 

Feign Client가 HTTP 요청에 공통으로 사용할 옵션을 RequestInterceptor를 통해 설정할 수 있다.

 

 


사용하기

 

@FeignClient(name = "FeignExample", url = "https://api.example.com/v1")
public interface UpbitFeignClient {

    @GetMapping("/user/{userId}")
    String getUserInfo(@PathVariable("userId") int userId);

  	// Other API Endpoint
}

 

@FeignClient 어노테이션으로 이 인터페이스가  Feign Client임을 선언한고, url 속성에 호출할 외부 서비스의 기본 URL을 작성한다.

name 속성으로 Feign Client의 이름을 지정하거나, configuration 클래스로 전역 설정 외의 설정을 추가로 적용할 수도 있다.

 

그 후, 인터페이스 내에 메서드를 선언해주면 되는데, 

  • 호출하려는 서비스가 GET 메서드, /ping 엔드포인트를 사용하고, 응답이 "pong"(String )이라면,
    • @GetMapping("/ping") 으로 엔드포인트를 매핑한다.
    • API의 응답에 맞게 반환 타입을 String으로 지정한다.

 

호출하려는 API가 파라미터를 요구하는 경우

Controller 단에서 HTTP 요청을 매핑하는 것과 유사하게, 메서드 시그니처를에 매핑하면 된다.

  • @PathVariable : URL 경로 변수를 매핑할 때 / e.g. @PathVariable("id") int id
  • @RequestParam : 쿼리 파라미터를 매핑할 때 / e.g. @RequestParam("value") String value
  • @RequestBody : 요청 본문에 포함된 데이터를 객체로 매핑할 때 / e.g. @RequestBody RequestDto request

 

API의 응답이 본문을 포함하는 경우

API의 응답이 본문을 포함하는 경우, 해당 응답의 형식에 맞는 자바 객체(예: DTO 등)를 정의하여 응답 데이터를 매핑할 수 있다. Feign Client의 메서드 반환 타입을 DTO로 설정하 API 응답을 적절히 처리한다.

 


추가

JSONPlaceholder, Reqres와 같은 가짜 데이터를 제공하는 서비스를 사용하면 API 호출 구성이 올바르게 되었는지 빠르게 테스트하는 데 유용하다.

 


참고한 자료

 

우아한 feign 적용기 | 우아한형제들 기술블로그

{{item.name}} 안녕하세요. 저는 비즈인프라개발팀에서 개발하고 있는 고정섭입니다. 이 글에서는 배달의민족 광고시스템 백엔드에서 feign 을 적용하면서 겪었던 것들에 대해서 공유 하고자 합니다

techblog.woowahan.com

 

feign 좀더 나아가기 | 우아한형제들 기술블로그

{{item.name}} 안녕하세요. 저는 상품시스템팀에서 개발하고 있는 고정섭입니다. 이 글에서는 배달의민족 광고시스템 백엔드에서 feign 을 적용하면서 겪었던 것들에 대해서 공유 하고자 합니다. 이

techblog.woowahan.com