본문 바로가기
프로그래밍/Spring Cloud

[MSA] Spring Cloud Feign - 기본 사용 및 기본 설정편

by 사바라다 2020. 9. 27.

안녕하세요. 오늘은 오랜만의 Spring Cloud에 대해서 알아보는 시간을 가지려고 합니다. 오늘 알아 볼 프로젝트는 Spring Cloud Feign 프로젝트입니다. MSA로 시스템 아키텍처가 많이 변경되면서 시스템간의 단일 책임 원칙으로 많은 시스템의 개선점을 보인 것은 사실입니다. 하지만 모든지 트레이드 오프는 있습니다. MSA로 변화의 큰 단점 중 하나로 꼽자면 바로 API 호출의 증가일 것입니다. 기존에 단순 DB 조회로 가능하던 부분들이 MSA로 전환되면서 직접 접근하는 것이 불가능해졌습니다. 따라서 이들이 모두 API가 되었습니다.

MSA 처럼 분산되어있는 시스템을 사용하는 입장에서는 API를 호출하는 코드를 노가다 식으로 항상 만들어줘야하는 번거로움이 생겼습니다. 이를 해결해 줄 수 있는 Spring Cloud 프로젝트가 바로 Feign입니다. Feign은 RestTemplate 호출 등을 JPA Repository 처럼 interface로 단순히 추상화 한 프로젝트입니다.

오늘은 이 프로젝트를 실습을 통해 알아보는 시간을 가지도록 하겠습니다.

의존성 (Dependency)

Feign은 Ribon, Hystirx와 마찬가지로 Spring Cloud 프로젝트중 하나입니다. 버전을 따로 명시해서 의존성을 부여할 수 도 있지만 다른 제품군과 호환을 위해 버전을 맞춰주어야 추후에 의존성에 의한 문제점이 생기지 않습니다. 이런 부분을 해소해주는게 Spring Cloud Release Train입니다. 호환성을 위해 Spring Cloud Release Train을 사용하도록 저는 가장 최신인 Hoxton.SR8를 이용하도록 하겠습니다. Train군이 궁금하신 분은 링크를 참조해주시기 바랍니다. 아래와 같이 프로젝트의 gradle.build에 넣습니다.

dependencyManagement {
    imports {
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:Hoxton.SR8"
    }
}

dependencies {
    implementation "org.springframework.cloud:spring-cloud-starter-openfeign"
}

빠르게 둘러보기

의존성을 추가했으니 바로 사용해보도록 하겠습니다. Feign을 사용하기 위해서는 먼저 @EnableFeignClient를 선언해주셔야합니다. 해당 어노테이션이 하는일은 @FeignClient이 있는 interface를 찾아서 등록해주는 역할을합니다. 기본적으로 @EnableFeignClient가 붙어있는 Class의 하위를 탐색합니다. 만약 해당 어노테이션이 있는 하위폴더에 존재하지 않는다면 basePackages 또는 basePackageClasses 속성을 이용하여 커스터마이징하여 사용할 필요가 있습니다.

@EnableFeignClients
@SpringBootApplication
public class Application {

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

}

그리고 기본적으로 제공하는 설정정보가 있기때문에 별도의 Configuration을 설정하지 않아도 바로 사용할 수 있습니다. 아래 코드를 보면서 설명하도록 하겠습니다. 아래코드는 Azure의 번역 시스템을 호출하는 FeignClient입니다. 헤더, 파라미터, 그리고 바디가 추가되어 있습니다. 만약 restTemplate을 사용해서 구현했다고 한다면 url 제작, 헤더생성, 파라미터 및 Body 추가가 모두 하나의 메서드에 들어가서 구현되었어야할 녀석들이 이렇게 하나의 interface 메서드 정의로써 구현할 수 있게됩니다. 이것만 보더라도 API Client를 만드는데 얼마나 많은 비용이 절감되는지 절실히 다가오실거라고 생각되어집니다.

@FeignClient(name = "azureClient" url = "https://api.cognitive.microsofttranslator.com")
public interface AzureClient {

  @PostMapping(value = "/translate", produces = "application/json", consumes = "application/json")
  String translateSingleSentence(
      @RequestHeader("Ocp-Apim-Subscription-Key") String key,
      @RequestHeader("Ocp-Apim-Subscription-Region") String region,
      @RequestParam("api-version") String version,
      @RequestParam("from") Locale from,
      @RequestParam("to") Locale to,
      @RequestBody String content);
}

간단히 위에서 사용한 어노테이션들에 대해서 설명드리도록 하겠습니다.

  • @FeignClient
    • FeignClient라고 선언하는 어노테이션입니다.
    • name 속성 : feignClient의 서비스 이름으로 필수 속성입니다, beanName과는 다릅니다.
    • url : 해당 interface baseUrl입니다.
    • qualifier : beanName입니다.
    • configuration : 커스터마이징한 configuration을 넣을 수 있습니다ㅣ.
    • fallback : Hystrix fallback 메서드입니다.

그리고 보시면 WebMvc에서 많이 사용하시던 @PostMapping을 보실 수 있습니다. 이건 실제로 WebMvc에서 사용하시던 어노테이션과 동일힙니다. Spring Cloud의 FeignClient에 쓰면 동일한 어노테이션으로 다른효과를 가지실 수 있습니다. feign에서 제공하는 @RequestLine을 사용하는 방법도 있습니다.

그리고 파라미터로 받은 @RequestHeader는 헤더를 붙이는 것이며, @RequestParam는 파라미터를 붙이는 것입니다. 그리고 @RequestBody는 Body에 데이터를 넣는 것입니다.

Basic Configuration

FeignClient에서는 기본적으로 제공하는 Configuration이 있기 때문에 위와 같이 간단히 사용할 수 있다고 말씀드렸습니다. 기본적으로 제공하는 Configuration은 FeignClientsConfiguration.class 입니다. 코드를 통해 어떤 설정들이 적용되어 있는 확인해보도록 하겠습니다. 먼저 Feign 공식 문서에서 확인한 기본적으로 제공하는 설정 Bean은 아래와 같습니다. 아래 Bean들을 보시면 @ConditionalOnMissingBean이 붙어있습니다. 이 말은 즉, 해당 Bean은 해당 옵션으로 사용하는 별도의 Bean이없을 때 적용되는 Default라는 의미입니다.

Decoder feignDecoder

Feign으로 호출 하고 난 후 http 응답에 대해서 어떻게 기본적으로 디코딩 처리를 진행할 지 대해서 기본 설정을 제공해주고있습니다. 아래와 같이 Bean이 코드로 등록되며, OptionalDecoder, ResponseEntityDecoder, SpringDecoder가 데코레이터패턴으로 적용되어있는것을 확인할 수 있었습니다. 각 Decoder가 어떻게 해석하는지에 대해서는 아래 Decoder 클래스들의 decode 메서드를 보시면 알 수 있습니다.

@Bean
@ConditionalOnMissingBean
public Decoder feignDecoder() {
    return new OptionalDecoder(new ResponseEntityDecoder(new SpringDecoder(this.messageConverters)));
}

Encoder feignEncoder

Decoder가 응답에 대해서 처리하는 거라면 Encoder는 요청에 대해서 웹에 맞게 인코딩 처리를 하는 부분입니다. Feign에서는 아래와 같이 기본적으로 SpringEncoder를 이용하여 Encoding을 합니다. HttpMessageConverter를 이용하여 요청의 Type에 맞게 처리를 해주는 걸로 소스를 확인하였습니다.

@Bean
@ConditionalOnMissingBean
public Encoder feignEncoder() {
    return new SpringEncoder(this.messageConverters);
}

Logger feignLogger

Feign의 Logger는 기본적으로 Slf4jLogger를 사용하고 있습니다. 기본적으로 통신에 대한 로깅은 NONE 레벨로 잡혀있습니다. 그냥 실행하시면 로그가 찍히지 않을겁니다. 실제 로깅을 찍기위해서는 서버의 로깅레벨을 application.yml 파일에서 FeignClient가 있는 패키지를 logging.level을 DEBUG로 변경해주셔야합니다.

@Autowired(required = false)
private Logger logger;

@Bean
@ConditionalOnMissingBean(FeignLoggerFactory.class)
public FeignLoggerFactory feignLoggerFactory() {
    return new DefaultFeignLoggerFactory(logger);
}

Contract feignContract: SpringMvcContract

org.springframework.cloud:spring-cloud-starter-openfeign를 이용했을 때 신기한게 FeignClient의 모든 어노테이션을 SpringMvc에서 사용하던 어노테이션을 재활용하여 사용할 수 있다는 점입니다. 그 이유는 Contract에 있습니다. Contract Bean을 보도록 하겠습니다. 아래 코드를 보시면 기본적으로 제공되는것이 SpringContract입니다. 만약 Feign의 기본 Contract를 Bean으로 커스터마이징 하시면 @RequestLine과 같은 Feign 용 Annotation으로 작업을 진행할 수 있게 됩니다.

@Bean
@ConditionalOnMissingBean
public Contract feignContract(ConversionService feignConversionService) {
    return new SpringMvcContract(this.parameterProcessors, feignConversionService);
}

Hystrix

Feign은 Hystrix 기능을 사용할 수 있도록 설정을 내장하고 있습니다. 조건은 Hystrix의 의존성이 있어야하며 feign.hystrix.enabled가 true여야합니다. Feign과 Hystrix의 연동은 별도의 포스터로 다시 여러분께 제대로 찾아뵙도록 하겠습니다.

@Configuration
@ConditionalOnClass({ HystrixCommand.class, HystrixFeign.class })
protected static class HystrixFeignConfiguration {
    @Bean
    @Scope("prototype")
    @ConditionalOnMissingBean
    @ConditionalOnProperty(name = "feign.hystrix.enabled")
    public Feign.Builder feignHystrixBuilder() {
        return HystrixFeign.builder();
    }
}

마무리

오늘은 이렇게 Spring Cloud Feign에 대해서 기본적인 설정정보들을 확인해보는 시간을 가졌습니다. 위의 기본적인 요소들은 모두 커스터마이징이 가능합니다. 또한 실제 환경에서 Feign을 사용하기 위해서는 적절하게 커스터마이징이 필수입니다. 가령 Feign의 기본설정은 최소한의 Log만 남기는데 통신에 대한 full로그를 남기고 싶은 경우가 있을 수 있습니다. 그리고 Hystrix, Retryer과 같이 제공되는 기능이나 기본적으로는 제공하지 않는 기능들도 있습니다.

다음시간에는 Spring Cloud Feign의 환경설정을 커스터마이징하는 방법에 대해서 알아보는 시간을 가져보도록 하겠습니다.

감사합니다.

참조

우아한_형제들_Feign

우아한_형제들_Feign_2

spring_cloud_openfeign_reference_doc

댓글