본 글은 아래의 링크를 바탕으로 작성되었다.
- 우아한형제들 기술 블로그 [우아한 feign 적용기]:https://techblog.woowahan.com/2630/
- Bealdung [Introduction to Spring Cloud OpenFeign]: https://www.baeldung.com/spring-cloud-openfeign
- Bealdung [Defferences Between Betflix Feign and OpenFeign]: https://www.baeldung.com/netflix-feign-vs-openfeign
Intro
Spring Cloud OpenFeign Docs: https://spring.io/projects/spring-cloud-openfeign
about Feign
Feign은 Netflix에서 개발한 Http client binder이다.
- Feign은 Spring Cloud echo 시스템에 통합되었다.
- 그러나 Netflix에서 내부적으로 사용을 중단하며, 해당 프로젝트는 OpenFeign이라는 오픈 소스 커뮤니티의 프로젝트로 이전되었다.
- 결론적으로, 이후 언급하는 Feign은 Spring Cloud OpenFeign 기반임을 염두에 두어야 한다.
강점
Feign은 기존 Http Client 라이브러리에 비해 여러 가지 강점을 가진다.
- Feign 사용을 위해서는 인테페이스 작성이 필요하며, 해당 인터페이스는 JPA의 경우처럼 자동으로 구현체가 만들어진다.
- annotation을 통해 다양한 옵션을 간단히 처리할 수 있다.
- 구현 코드 없이 로직을 명확히 이해할 수 있다.
비교 대상
Feign 대신 대안으로 사용할 수 있는 여러 라이브러리가 있다. 다만 이 중 작성자가 사용해 본 것은 RestTemplate, WebClient 뿐이다.
- RestTemplate: 현재 deprecated되어 장기적인 사용이 어려울 수 있다.
NOTE: As of 5.0 this class is in maintenance mode, with only minor requests for changes and bugs to be accepted going forward. Please, consider using the org.springframework.web.reactive.client.WebClient which has a more modern API and supports sync, async, and streaming scenarios.
- Spring Reactive WebClient: Non-Blocking IO 기반의 비동기식 flow를 지원한다.
그러나 일반적인 환경에서 사용하는데에 부담이 될 수 있다.- 비동기 처리 등에 대한 수요가 크지 않고, 기존의 MVC 구조를 가진 프로젝트에 이질적일 수 있다.
- 로깅이나 테스트 등이 까다로운 측면이 있었다.
- Retrofit2: squareup사의 라이브러리로, 안드로이드 환경에서 주로 사용된다.
- 다만 Spring Boot의 기본 지원 범위가 아니고, 별도로 라이브러리를 추가해야 한다.
- 그렇기 때문에 지원이나 호환 등에서 불리한 점을 가진다.
- Feign의 구현 코드와 비교했을 때, 별도의 configuration 클래스가 필요하여 코드량이 증가한다.
- 다만 Spring Boot의 기본 지원 범위가 아니고, 별도로 라이브러리를 추가해야 한다.
개발 전 준비사항
종속성 추가
Maven을 사용할 경우, pom.xml에 아래 코드를 추가한다.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
또한 spring-cloud-dependencies를 추가해 줘야 한다.
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Application Class에 설정 추가
main 코드가 있는 클래스에 어노테이션을 추가해야 한다.
@SpringBootApplication
@EnableFeignClients //추가된 부분!!
public class ExampleApplication {
public static void main(String[] args) {
SpringApplication.run(ExampleApplication.class, args);
}
}
- @EnableFeignClients 어노테이션을 추가하면, 지정된 경로 이하의 패키지를 탐색하면서 @FeignClient 어노테이션이 붙은 인터페이스의 구현체를 만들어 준다.
- 수동으로 구현체를 만들 수 있다. 링크: https://cloud.spring.io/spring-cloud-netflix/multi/multi_spring-cloud-feign.html#_creating_feign_clients_manually
개발
Feign을 통해 GET Request 보내기
우선 GET 요청을 보내고자 한다.
Feign을 통해 아래와 같이 코드를 작성해 보자.
@FeignClient(value = "test", url = "${api.test.url}")
public interface TestApiClient{
@GetMapping("/api/v1/test/{testId}")
TestResponse getTest(@PathVariable("testId") Long testId);
}
위처럼 코드를 작성하면, 해당 API에 get 호출을 수행할 수 있다!
아래처럼 controller에 코드를 작성해 보자.
@Controller
@RequestMapping("/api/v1")
public class TestController {
@Autowired //생성자 주입으로 대체 가능
private TestApiClient testApiClient;
@GetMapping("/test/{testId}")
public ResponseEntity<TestResponse> getTestData(@PathVariable final Long testId) {
final TestResponse response = testApiClient.getTest(testId);
return ResponseEntity.ok(response)
.build();
}
}
이를 통해 요청을 보내고 그 값을 적절히 반환받을 수 있다.
Feign을 통해 POST/DELETE Request 보내기
아래 코드는 spring cloud open-fegin docs의 예제 코드이다.
@FeignClient("stores")
public interface StoreClient {
@RequestMapping(method = RequestMethod.GET, value = "/stores")
List<Store> getStores();
@RequestMapping(method = RequestMethod.GET, value = "/stores")
Page<Store> getStores(Pageable pageable);
@RequestMapping(method = RequestMethod.POST, value = "/stores/{storeId}", consumes = "application/json")
Store update(@PathVariable("storeId") Long storeId, Store store);
@RequestMapping(method = RequestMethod.DELETE, value = "/stores/{storeId:\\\\d+}")
void delete(@PathVariable Long storeId);
}
- 여기에서 RequestMapping의 경우 method 인자값에 따라 XXXMapping(ex: GetMapping)으로 대체할 수 있다.
- consumes의 경우 들어오는 값을 어떻게 매핑할지를 지정한다. 링크: https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-requestmapping-consumes
- Request Body를 넣을 경우, 별도의 어노테이션 없이 객체를 파라미터로 넣어주면 된다. 다만 여러 개의 어노테이션 없는 객체가 인자로 존재하면, 예외가 발생한다.(body는 1개만 가능)
Feign 요청에 header 값 추가하기
아래의 코드를 참고해 보자. (출처: https://github.com/woowabros/feign-apply-experience-sample/blob/master/src/main/java/io/github/mayaul/feign/basic/Step2Client.java)
/**
* header 를 넣어서 호출 하기
*/
@FeignClient(value = "step2", url = "<https://httpbin.org>", configuration = {HeaderConfiguration.class})
public interface Step2Client {
@GetMapping(value = "/status/{status}")
void status(@PathVariable("status") int status);
@GetMapping(value = "/status/{status}", headers = "key2=value2")
void status2(@PathVariable("status") int status);
@GetMapping(value = "/status/{status}")
void status3(@RequestHeader("key3") String headers, @PathVariable("status") int status);
}
헤더를 넣는데에는 3가지 방법이 있다.
- Configuration 클래스를 만들고 추가하기 : 제일 확실한 방법
- headers 속성을 Mapping시 넣어주기 : 별도의 Contract 설정이 없다면 적용 안됨
- 메소드의 파라미터로 헤더를 넣어주기 : 성공하는 것 확인
더하여, 인증이 필요한 경우 아래처럼 처리해줄 수 있다.
//인증을 위한 Configuration
public class BasicAuthConfiguration {
@Bean
public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
return new BasicAuthRequestInterceptor("mayaul", "1234567890");
}
}
//Feign Client
/**
* Basic Auth 인증 호출 하기
*/
@FeignClient(value = "step3", url = "<https://httpbin.org>", configuration = {BasicAuthConfiguration.class})
public interface Step3Client {
@GetMapping("/status/{status}")
void status(@PathVariable("status") int status);
}
Feign 설정 수정하기(로깅, 연결시간)
application.yml에 아래의 코드를 추가할 수 있다.
feign:
client:
config:
default:
connectTimeout: 5000
readTimeout: 5000
loggerLevel: basic
feign:loggerlevel의 경우, full로 설정하면 아래와 같은 상세한 로그가 나온다.
마무리하며
Feign을 사용하면서 여러가지 점에서 만족스러웠다.
- 코드가 깔끔해서 관리하기 편하고, 요구사항을 명확히 알 수 있었다.
- 로깅, 옵션 설정, 헤더 설정 등에서 어려움이 적었다.
- 사내 프로젝트를 진행하면서 Kotlin으로 Feign 코드를 작성했는데, 이때는 더 깔끔해져서 만족했다 :)
다만, 한가지 어려움을 느꼈던 부분이 있다.
- 테스트가 어렵다. 테스트 관련 문서도 적고, 뭔가 원하는대로 구현하는데에 어려움이 있었다.
그래서 다음 글에서는, Feign을 테스트하는 것에 대해 간단히 생각을 정리해보려고 한다.
잘못된 내용이 있다면 댓글 부탁드립니다. 늘 감사합니다.
'프로그래밍 > Spring Boot' 카테고리의 다른 글
Kotlin 에서 Slf4j를 통해 로깅하기 (1) | 2024.01.01 |
---|---|
Spring Cloud OpenFeign 테스트하기 (0) | 2022.03.07 |
LocalDateTime 사용 시 주의할 몇몇 오류사례 (0) | 2021.10.27 |
[SpringBoot] 프로젝트에 Swagger 적용하기 (0) | 2021.09.06 |
[SpringBoot] application.properties를 yml로 교체하기 (0) | 2021.08.31 |
댓글