최근 WebClient를 잘못 사용하고 있다는 것을 깨닫고 기존의 코드를 Open Feign으로 리팩토링을 하려고 했다.
RestTemplate도 있지만 코드가 지저분해지기 때문에 OpenFeign을 이용했다.
Feign
Feign이란 인터페이스와 어노테이션 만으로 웹 서비스 호출을 쉽게 만든 Netflix에서 만든 기술이다.
Spring Cloud Netflix
Spring Cloud는 마이크로서비스 아키텍처를 구축하는데 필요한 도구 모음이다. 이 도구들은 분산 시스템에서 발생할 수 있는 여러 복잡한 문제를 해결하도록 설계되었습니다.
Netflix는 이러한 마이크로서비스 문제를 해결하기 위해 여러 오픈 소스 소프트웨어(OSS)를 개발하고 공개했다.
이 중에서 Feign은 그 사용법이 간단하고, HTTP 클라이언트 코드를 깔끔하게 작성할 수 있어서 많이 사용되는 도구이다. 그래서 Spring Cloud는 Feign을 별도의 Spring Cloud Starter 패키지로 제공하여, 사용자가 필요에 따라 선택해서 사용할 수 있게 하였다.
OpenFeign
Netflix에서 만든 Feign을 기반으로 Spring과의 호환성을 향상 하고 기능을 확장한 오픈 소스다.
시간이 지나면서 Netflix의 내부 요구 사항이 변경되고, 이에 따라 그들의 기술 스택도 변화하였습니다.
2018년, Netflix는 이러한 변경으로 인해 Feign의 내부 사용을 중단하고 이 결정에 따라 Netflix는 Feign을 OpenFeign이라는 새로운 프로젝트로 완전히 오픈 소스 커뮤니티에 넘겼다.
Spring Cloud OpenFeign
Spring Cloud OpenFeign은 Feign 프로젝트를 Spring Cloud 생태계에 통합한다.
편리하게도, 이 통합은 Spring MVC 어노테이션에 대한 지원을 추가하며 동일한 HttpMessageConverters를 제공한다.
자세한 내용은 다음의 링크에 있다. https://www.baeldung.com/spring-cloud-openfeign
나는 이 기술을 카카오페이 API를 호출하는 데 사용했다.
사용법
이제 내가 사용한 코드를 보면서 사용법을 익혀보자
1. 먼저 의존성을 추가한다.
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'
2. 다음으로 앞서 말했듯이 Spring MVC와 동일한 어노테이션으로 http 메시지 변환이 가능하기 때문에 익숙한 어노테이션들을 사용하면 된다.
아래와 같은 방법으로 EnableFeingClients 어노테이션을 붙이고 @GetMaaping으로 간단한 Get 요청을 할 수 있다.
@SpringBootApplication
@EnableFeignClients
public class GcCoffeeApplication {
public static void main(String[] args) {
SpringApplication.run(GcCoffeeApplication.class, args);
}
}
@FeignClient(name = "myClient", url = "http://localhost:8080")
public interface MyClient {
@GetMapping("/api/v1/vouchers")
String getDataFromApi();
}
3. 이런식으로도 post요청도 보낼 수 있다.
@FeignClient(name = "kakaoClient", url = "https://kapi.kakao.com/v1/payment", configuration = FormFeignEncoderConfig.class)
public interface KakaoClient {
@PostMapping(value = "/ready", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
KaKaoReadyResponseDto someEndpointMethod2(
@RequestParam("cid") String cid,
@RequestParam("approval_url") String approvalUrl,
@RequestParam("cancel_url") String cancelUrl,
@RequestParam("fail_url") String failUrl,
@RequestParam("partner_order_id") String partnerOrderId,
@RequestParam("partner_user_id") String partnerUserId,
@RequestParam("item_name") String itemName,
@RequestParam("quantity") String quantity,
@RequestParam("total_amount") String totalAmount,
@RequestParam("tax_free_amount") String taxFreeAmount
);
}
class FormFeignEncoderConfig {
// @Bean
// public Encoder encoder(ObjectFactory<HttpMessageConverters> converters) {
// System.out.println(converters.toString());
// return new SpringFormEncoder(new SpringEncoder(converters));
// }
//
// @Bean
// Logger.Level feignLoggerLevel() {
// return Logger.Level.FULL;
// }
@Bean
public RequestInterceptor requestInterceptor() {
return requestTemplate -> {
requestTemplate.header("Authorization", "KakaoAK ${인증 키}");
};
}
}
+ 카카오페이 api는 컨텐트 타입을 urlencoded를 요구하기 때문에 위와 같이 url에다 쿼리스트링으로 전할 값을 작성했다. 링크대로 요청을 보내봤는데 자꾸 body에 값이 들어가지 않아 쿼리스트링으로 대체했다..
https://www.baeldung.com/spring-cloud-post-form-url-encoded-data
4. 헤더에 값 추가
이런식으로 빈을 등록하고 요청을 인터셉트 해서 헤더에 값을 추가할 수 있다. 또는 @Headrs라는 어노테이션을 붙이는 방법도 있음.
class FormFeignEncoderConfig {
@Bean
public RequestInterceptor requestInterceptor() {
return requestTemplate -> {
requestTemplate.header("Authorization", "KakaoAK ${인증 키}");
};
}
}
5. 클라이언트 코드
var response = kakaoClient.someEndpointMethod2(
"TC0ONETIME",
"http://localhost:8081/approve",
"http://localhost:8081/fail",
"http://localhost:8081/cancel",
"test",
"test",
"test",
"1",
"1",
"0"
);
json이었다면 그냥 바디에 객체를 집어 넣었을텐데 urlencoded 방식이다 보니 좀 지저분하다. 이 부분은 좀 고민해 봐야겠다. 메시지 컨버터를 내가 커스텀해서 따로 정의했는데 왜 작동이 안하는지는 모르겠다..
마치며
결론적으로 Spring MVC에서 사용하는 어노테이션 몇개만으로 웹 서비스 호출이 가능한 것을 볼 수 있다. 또한 Hystrix를 이용해 서킷 브레이크, fall back 처리, 타임 아웃 등 다양한 설정을 할 수 있으니 RestTemplate 보다는 feign을 사용하는 것이 좋다고 생각한다.
'프로젝트 > 고민' 카테고리의 다른 글
테이블 설계 (0) | 2023.10.05 |
---|---|
주문 코드 리팩토링 (0) | 2023.08.19 |
elasticsearch에 형태소 분석기 적용하기 (0) | 2023.06.12 |
Slack Webhook API 사용법 (0) | 2023.05.31 |
Elasticsearch 쿼리빌더로 리팩토링 (0) | 2023.05.23 |