Feign Client

By | 2022년 7월 28일
Table of Contents

Feign Client

참조

Rest API 호출과 Json 을 클래스로 자동변환까지 해주는 라이브러리이다.

의존성

dependencies {
    implementation 'org.springframework.cloud:spring-cloud-openfeign-core:3.1.5'
    implementation 'org.springframework.cloud:spring-cloud-starter-openfeign:3.1.5'
    # compile("org.springframework.cloud:spring-cloud-starter-openfeign")
}

활성화

@EnableFeignClients
@SpringBootApplication
public class HelloApplication {
    public static void main(String[] args) {
        SpringApplication.run(HelloApplication.class, args);
    }
}

설정

public class FeignClientConfig implements Jackson2ObjectMapperBuilderCustomizer {
    private static final Logger log = LoggerFactory.getLogger(FeignClientConfig.class);

    public FeignClientConfig() {
    }

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

    @Bean
    public FeignErrorDecoder decoder() {
        return new FeignErrorDecoder();
    }

    public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) {
        jacksonObjectMapperBuilder
                .featuresToEnable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)
                .featuresToDisable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
                .timeZone(TimeZone.getDefault())
                .modulesToInstall(new Module[]{new JavaTimeModule()})
                .locale(Locale.getDefault()).simpleDateFormat("yyyy-MM-dd HH:mm:ss");
    }
}
public class FeignRetryConfig {
    @Value("${feign.retry.period}")
    private long period;
    @Value("${feign.retry.max-period}")
    private long maxPeriod;
    @Value("${feign.retry.max-attempt}")
    private int maxAttempt;

    public FeignRetryConfig() {
    }

    @Bean
    public Retryer retryer() {
        return new Retryer.Default(this.period, this.maxPeriod, this.maxAttempt);
    }
}
public class FeignErrorDecoder implements ErrorDecoder {
    @Override
    public Exception decode(String methodKey, Response response) {
        switch (response.status()){
            case 400:
                break;
            case 404:
                if(methodKey.contains("getOrders")){
                    return new ResponseStatusException(HttpStatus.valueOf(response.status()),
                            "User's orders is empty.");
                }
                break;
            default:
                return new Exception(response.reason());
        }
        return null;
    }
}

서비스

@FeignClient(name = "HELLO-SERVICE",configuration = {FeignClientConfig.class, FeignRetryConfig.class})
public interface HelloService {
    @GetMapping("/hello/{itemId}")
    HelloModel getHelloByItemId(@PathVariable("itemId") String itemId);

    @PostMapping("/hello/basic")
    ProductDetailBasicResponseModel fetchHello(
            @RequestBody List<ItemidOnlyDto> req
    );
}

Header 붙이기

특정 메소드에만 붙이기

@FeignClient(name = "HELLO-SERVICE",configuration = {FeignClientConfig.class, FeignRetryConfig.class})
public interface HelloService {
    @GetMapping("/hello/{itemId}")
    HelloModel getHelloByItemId(@PathVariable("itemId") String itemId);

    @PostMapping("/hello/basic")
    ProductDetailBasicResponseModel fetchHello(
            @RequestHeader("X-Auth-Token") String accessToken,
            @RequestBody List<ItemidOnlyDto> req
    );
}

모든 메소드에 붙이기

RequestInterceptor 를 구현해 줌으로 해서,
모든 Feign Client method 에 header 가 자동으로 붙는다.

@Component
public class FeignRequestInterceptor implements RequestInterceptor {
    private static final String X_AUTH_TOKEN = "X-Auth-Token";
    @Override
    public void apply(RequestTemplate requestTemplate) {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (requestAttributes == null) {
            return;
        }
        HttpServletRequest request = requestAttributes.getRequest();
        LoginInfo loginInfo = (LoginInfo) request.getAttribute("loginInfo");
        if (loginInfo == null) {
            return;
        }

        requestTemplate.header(X_AUTH_TOKEN, loginInfo.getBrowser());
    }
}

후기

첨 JPA 접했을 때,
명령 하나 호출하면,
지 혼자 쿼리 작성해서 호출하고 리턴값 받아서 엔터티에 채워주고…
신기했는데…

딱 그느낌이다.
명령 하나 호출하면,
지 혼자 RequestBody 생성해서 url 호출하고,
리턴 json 받아서 response class 에 매핑까지 해준다.
심지어 서버가 죽어 있으면 retry 까지도 해준다.

더군다나,
learning curve 라는 단어 쓰는게 민망할 정도로 쉽다.

헐…
(머리가 나쁘면 손가락이 고생한다.)

2 thoughts on “Feign Client

답글 남기기