이번 포스트에서는 Spring Cloud에서 사용되는 OpenFeign에 대해 살펴보자.
OpenFeign
OpenFeign이란?
Feign은 Netflex에서 개발한 Http Client 라이브러리이다. 나중에 Netflex가 더 이상 유지보수 하지 않고 커뮤니티에서 이어받아 OpenFeign으로 변경되었다.
Feign은 선언적 웹서비스 클라이언트로 이를 이용하면 웹 서비스 클라이언트 작성이 매우 쉬워진다. Feign을 사용하려면 인터페이스를 생성하고 @FeignClient 애너테이션을 추가하면 된다. 다음 코드는 한눈에 봐도 어떤 의도로 작성되었는지 쉽게 파악할 수 있다.
// ❌ 기존 RestTemplate 방식
RestTemplate restTemplate = new RestTemplate();
String url = "https://jsonplaceholder.typicode.com/posts/1";
Post post = restTemplate.getForObject(url, Post.class);
// ✅ OpenFeign 방식
@FeignClient(name = "post-client", url = "https://jsonplaceholder.typicode.com")
interface PostClient {
@GetMapping("/posts/{id}")
Post getPost(@PathVariable Long id);
}
// 사용: postClient.getPost(1L);
기본 설정
OpenFeign을 사용하기 위해서는 다음의 의존성이 필요하다. 추가로 Spring Web, Lombok, DevTools 도 설정해주자.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
@EnableFeignClient
OpenFeign의 시작은 @Configuration 클래스에 @EnableFeignClient를 추가하는 것으로 시작한다. @EnableFeignClient는 Spring Cloud OpenFeign을 사용하는 외부 서비스와 통신할 때 사용할 Feign Client 인터페이스(@FeignClient 가 선언된)를 찾아내고 활성화 하는 역할을 수행한다. 주요 역할은 다음과 같다.
- 컴포넌트 스캔 (Component Scanning): 애플리케이션 내에서 @FeignClient 어노테이션이 붙은 인터페이스 스캔
- 프록시 객체 생성: 찾아낸 인터페이스를 바탕으로 실제 HTTP 요청을 보낼 수 있는 구현체(런타임 프록시 객체)를 자동으로 생성
- 빈 등록: 생성된 프록시 객체를 스프링 컨테이너에 빈(Bean)으로 등록
@SpringBootApplication
@EnableFeignClients
public class OpenfeignApplication {
public static void main(String[] args) {
SpringApplication.run(OpenfeignApplication.class, args);
}
}
간단한 요청 처리
@FeignClient
OpenFeign을 사용하기 위해서는 @FeignClient를 interface에 선언해서 HTTP Client를 생성한다. @FeignClient는 Type에 선언할 수 있으며 상속 된다.
@Target(ElementType.TYPE)
@Inherited
public @interface FeignClient {}
다음은 주요 속성들이다.
| 속성 |
설명 |
예시 |
| name |
클라이언트 이름 (필수) |
name = "post-client" |
| url |
고정 URL |
url = "https://jsonplaceholder.typicode.com" |
| configuration |
커스텀 설정 클래스 |
configuration = FeignConfig.class |
| fallback |
실패시 대체 구현체 |
fallback = PostFallback.class |
다음은 간단한 사용 예이다.
@FeignClient(name = "post-client", url = "https://jsonplaceholder.typicode.com")
public interface PostClient {...}
다양한 파라미터 전달 방법
OpenFeign은 기존의 Spring MVC가 가지는 어노테이션을 이용해서 다양한 방법으로 파라미터를 전달할 수 있다. 그냥 똑같다.
- @PathVariable
@GetMapping("/posts/{id}")
Post getPost(@PathVariable Long id);
// 호출: /posts/1
- @RequestParam
@GetMapping("/posts")
List<Post> getPostsByUser(@RequestParam Long userId);
// 호출: /posts?userId=1
- @RequestBody
@PostMapping("/posts")
Post createPost(@RequestBody Post post);
- @RequestHeader
@GetMapping("/posts/{id}")
Post getPost(@PathVariable Long id,
@RequestHeader("Authorization") String token);
- @SpringQueryMap - 여러 파라미터를 DTO 형태로 전달
@GetMapping("/posts")
List<Post> searchPosts(@SpringQueryMap SearchParams params);
// SearchParams 클래스
class SearchParams {
private Long userId;
private String title;
// getter, setter
}
// 호출: /posts?userId=1&title=example
설정 커스터마이징
JavaConfig
다음은 JavaConfig 기반으로 기본 설정을 재정의 하는 코드이다. 대부분 빈 추가 형태로 재정의 한다.
@Configuration
public class FeignConfig {
// 타임아웃 설정
@Bean
public Request.Options options() {
return new Request.Options(
10, TimeUnit.SECONDS, // connectTimeout
60, TimeUnit.SECONDS, // readTimeout
true // 3xx 응답 시 redirect url로 이동할 것인가?
);
}
// 로그 레벨 설정 - request, response 시 정보 출력: BASIC, FULL, HEADERS, NONE
@Bean
public Logger.Level loggerLevel() {
return Logger.Level.FULL;
}
// 인터셉터 (모든 요청에 헤더 추가)
@Bean
public RequestInterceptor requestInterceptor() {
return template -> {
template.header("X-Custom-Header", "value");
};
}
// 에러 처리
@Bean
public ErrorDecoder errorDecoder() {
return (methodKey, response) -> {
if (response.status() == 404) {
return new NotFoundException("게시글을 찾을 수 없음");
}
return new Exception("에러 발생");
};
}
}
Loger의 Level은 다음과 같다.
- NONE: 로깅 안함
- BASIC: 요청 메서드, URL, 응답 상태만
- HEADERS: BASIC + 헤더 정보
- FULL: 모든 정보 (요청/응답 바디 포함)
application.properties
설정 파일을 통해서도 일부 속성을 재정의할 수 있다.
spring:
cloud:
openfeign:
client:
config:
post-client: # 특정 클라이언트
connectTimeout: 5000
readTimeout: 10000
loggerLevel: BASIC
default: # 모든 클라이언트
connectTimeout: 3000
readTimeout: 5000