2024. 12. 11. 15:17ㆍSpring Microservice
🚀 Spring Cloud OpenFeign과 @EnableFeignClients
마이크로서비스 아키텍처에서 서비스 간의 HTTP 통신은 필수적입니다.
Spring Boot에서는 여러 HTTP 클라이언트를 지원하지만, Spring Cloud OpenFeign은 가장 직관적인 선언적 방식으로 REST API 호출을 단순화합니다.
이번 글에서는 @EnableFeignClients
의 역할과 내부 동작 원리, Feign의 역사, application.yml 설정 방법까지
심층적으로 다루며, OpenFeign을 보다 효과적으로 활용할 수 있도록 가이드하겠습니다.
📜 Feign의 역사와 개요
Feign은 넷플릭스(Netflix)에서 개발한 선언적 HTTP 클라이언트 라이브러리입니다.
대규모 마이크로서비스 환경에서 API 호출을 쉽게 관리할 수 있도록 설계되었습니다.
🚀 Feign의 발전 과정
- Netflix OSS Feign
- 넷플릭스가 내부적으로 개발하여 서비스 간 HTTP API 호출을 단순화
- Java 인터페이스로 API 호출을 정의하는 방식 제공
- Spring Cloud OpenFeign으로 발전
- Spring Cloud 팀이 Feign을 가져와 Spring Boot와 통합
- 기존의
RestTemplate
보다 간결한 코드 제공 - Spring Cloud LoadBalancer, Eureka, Resilience4j 등과 통합 지원
✅ 현재 Feign은 Netflix OSS에서 독립되어, Spring Cloud OpenFeign으로 발전하여 사용됩니다.
🔥 OpenFeign vs RestTemplate vs WebClient
OpenFeign | RestTemplate | WebClient | |
---|---|---|---|
선언적 방식 | ✅ | ❌ | ❌ |
비동기 지원 | ❌ | ❌ | ✅ |
Spring Boot 통합 | ✅ | ✅ | ✅ |
로드 밸런싱 지원 | ✅ | ✅ (LoadBalancer 필요) | ✅ |
기본 HTTP 라이브러리 | Apache HttpClient, OkHttp | HttpURLConnection, HttpClient | Netty, Reactor |
✅ Feign은 선언적 방식의 API 호출을 지원하여 코드가 간결하지만,
❌ 비동기 처리가 필요한 경우에는 WebClient
가 더 적절할 수 있습니다.
🛠️ @EnableFeignClients의 역할과 원리
1️⃣ @EnableFeignClients란?
@EnableFeignClients
는 Spring Cloud OpenFeign을 활성화하는 애너테이션으로,
Feign 클라이언트를 스캔하고, 프록시 객체를 생성하여 사용할 수 있도록 해줍니다.
📌 Spring Boot 애플리케이션에서 Feign 활성화
@SpringBootApplication
@EnableFeignClients
public class FeignExampleApplication {
public static void main(String[] args) {
SpringApplication.run(FeignExampleApplication.class, args);
}
}
✅ 이 애너테이션을 추가하면 @FeignClient
가 선언된 인터페이스가 자동으로 Spring Bean으로 등록됩니다.
2️⃣ Feign의 내부 동작 원리
Spring Boot가 실행되면 다음 과정이 발생합니다.
1️⃣ FeignClientsRegistrar 실행
@EnableFeignClients
가 선언되면 FeignClientsRegistrar가 동작@FeignClient
가 붙은 모든 인터페이스를 찾아서 Spring Bean으로 등록
2️⃣ Feign 클라이언트 프록시 생성
Feign.builder()
를 사용하여 인터페이스의 동적 프록시 객체를 생성- HTTP 요청을 내부적으로 실행하고, 응답을 자동으로 매핑
3️⃣ Spring Boot의 Bean 등록
- 프록시 객체를
@Bean
으로 등록하여,@Autowired
또는 생성자 주입으로 사용 가능
📌 Feign 프록시 객체 생성 예제
Feign.builder()
.decoder(new GsonDecoder()) // JSON 직렬화/역직렬화 설정
.target(UserServiceClient.class, "http://localhost:8080");
✅ Feign을 사용하면 인터페이스 메서드 호출만으로도 HTTP 요청을 실행할 수 있습니다.
📡 Feign 클라이언트 정의 및 application.yml 설정
1️⃣ FeignClient 인터페이스 정의
@FeignClient(name = "user-service", url = "http://localhost:8080")
public interface UserServiceClient {
@GetMapping("/users/{id}")
User getUserById(@PathVariable("id") Long id);
}
✅ @FeignClient
가 선언된 인터페이스는 자동으로 Spring Bean으로 등록됩니다.
2️⃣ application.yml 설정
Spring Boot에서 Feign의 동작을 조정할 수 있도록 application.yml
을 구성할 수 있습니다.
📌 기본적인 Feign 설정
spring:
application:
name: feign-client-example
cloud:
openfeign:
client:
config:
default:
connectTimeout: 5000 # 연결 타임아웃 (ms)
readTimeout: 5000 # 응답 대기 시간 (ms)
loggerLevel: full # 로깅 수준
retryer: feign.Retryer.Default # 재시도 설정
errorDecoder: com.example.CustomErrorDecoder # 에러 핸들러 설정
✅ 기본적으로 모든 Feign 클라이언트에 적용됩니다.
3️⃣ 로드 밸런싱을 활용한 Feign 설정
Eureka 같은 서비스 디스커버리를 활용하면,
Feign은 서비스 이름만으로 API를 호출할 수 있습니다.
📌 서비스 이름을 사용한 FeignClient
@FeignClient(name = "user-service")
public interface UserServiceClient {
@GetMapping("/users/{id}")
User getUserById(@PathVariable("id") Long id);
}
📌 application.yml 설정 (Eureka 연동)
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
✅ Eureka를 사용하면 서비스 이름만으로 동적으로 API 호출이 가능해집니다.
4️⃣ 개별 Feign 클라이언트에 설정 적용
특정 Feign 클라이언트에 별도의 설정을 적용할 수도 있습니다.
📌 application.yml에서 개별 설정 추가
spring:
cloud:
openfeign:
client:
config:
user-service: # 특정 클라이언트 설정
connectTimeout: 3000
readTimeout: 3000
loggerLevel: basic
✅ user-service
클라이언트만 별도로 3초 타임아웃을 적용합니다.
🔍 Feign의 고급 기능
1️⃣ 요청 인터셉터 (Header 추가)
Feign 요청 시 공통적으로 필요한 헤더(예: 인증 토큰)를 추가할 수 있습니다.
📌 FeignConfig 설정
@Configuration
public class FeignConfig {
@Bean
public RequestInterceptor requestInterceptor() {
return requestTemplate -> {
requestTemplate.header("Authorization", "Bearer my-token");
};
}
}
📌 FeignClient에 적용
@FeignClient(name = "user-service", configuration = FeignConfig.class)
public interface UserServiceClient {
@GetMapping("/users/{id}")
User getUserById(@PathVariable("id") Long id);
}
🚀 Spring Cloud OpenFeign: 활용 코드 예제와 실전 가이드
Spring Cloud OpenFeign을 사용하면 HTTP API 호출을 단순하고 직관적으로 구현할 수 있습니다.
OpenFeign의 실전 활용 코드를 중심으로, 다양한 기능을 살펴보겠습니다.
📡 1️⃣ 기본적인 Feign 클라이언트 구현
OpenFeign을 사용하여 REST API를 호출하는 기본적인 방법을 살펴보겠습니다.
아래 예제에서는 사용자(User) 정보를 조회하고, 새 사용자를 생성하는 API를 호출합니다.
📌 1. FeignClient 인터페이스 정의
@FeignClient(name = "user-service", url = "http://localhost:8080")
public interface UserServiceClient {
@GetMapping("/users/{id}")
User getUserById(@PathVariable("id") Long id);
@PostMapping("/users")
User createUser(@RequestBody User user);
}
✅ @FeignClient
를 사용하여, UserServiceClient
인터페이스를 정의하면
✅ getUserById()
와 createUser()
메서드를 통해 HTTP 요청을 자동으로 실행할 수 있습니다.
📌 2. Feign을 활용한 컨트롤러 구현
이제 정의한 UserServiceClient
를 Spring Bean으로 주입받아 사용해 보겠습니다.
@RestController
@RequestMapping("/api")
public class UserController {
private final UserServiceClient userServiceClient;
public UserController(UserServiceClient userServiceClient) {
this.userServiceClient = userServiceClient;
}
@GetMapping("/users/{id}")
public User getUserById(@PathVariable Long id) {
return userServiceClient.getUserById(id);
}
@PostMapping("/users")
public User createUser(@RequestBody User user) {
return userServiceClient.createUser(user);
}
}
✅ Feign을 사용하면 API 호출이 마치 일반 메서드 호출처럼 단순해집니다.
🌍 2️⃣ 로드 밸런싱을 활용한 Feign
대부분의 마이크로서비스 환경에서는 Eureka 또는 Consul 같은 서비스 디스커버리를 활용하여 API를 호출합니다.
이 경우 @FeignClient
의 url
속성을 제거하고, 서비스 이름으로 호출할 수 있습니다.
📌 1. 서비스 이름 기반 FeignClient
@FeignClient(name = "user-service")
public interface UserServiceClient {
@GetMapping("/users/{id}")
User getUserById(@PathVariable("id") Long id);
}
✅ 서비스 이름(user-service
)만 설정하면,
✅ Spring Cloud LoadBalancer가 자동으로 Eureka에서 해당 서비스의 인스턴스를 찾아 요청을 보냅니다.
📌 2. application.yml 설정 (Eureka 연동)
서비스 디스커버리를 사용하기 위해 application.yml
에서 Eureka를 활성화합니다.
spring:
application:
name: feign-client-example
cloud:
openfeign:
client:
config:
default:
connectTimeout: 5000
readTimeout: 5000
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
✅ eureka.client.service-url
을 통해 Eureka 서버 주소를 설정하면,
✅ user-service
라는 이름의 서비스가 자동으로 감지됩니다.
🔒 3️⃣ Feign 요청에 인증 헤더 추가
대부분의 API 호출에서는 JWT 토큰이나 API 키를 추가해야 하는 경우가 많습니다.
Feign에서는 RequestInterceptor를 사용하여 모든 요청에 자동으로 헤더를 추가할 수 있습니다.
📌 1. RequestInterceptor 구현
@Configuration
public class FeignConfig {
@Bean
public RequestInterceptor requestInterceptor() {
return requestTemplate -> {
requestTemplate.header("Authorization", "Bearer my-token");
};
}
}
✅ requestInterceptor
는 모든 요청에 Authorization 헤더를 자동 추가합니다.
📌 2. FeignClient에 설정 적용
@FeignClient(name = "user-service", configuration = FeignConfig.class)
public interface UserServiceClient {
@GetMapping("/users/{id}")
User getUserById(@PathVariable("id") Long id);
}
✅ FeignConfig.class
를 설정하면, 모든 요청에 JWT 토큰이 포함됩니다.
📡 4️⃣ Feign의 에러 처리 (Custom ErrorDecoder)
Feign을 사용할 때, 서버에서 에러 응답(404, 500 등)을 받으면 예외가 발생합니다.
이러한 에러를 직접 처리하려면 ErrorDecoder를 구현하면 됩니다.
📌 1. Custom ErrorDecoder 구현
@Component
public class CustomErrorDecoder implements ErrorDecoder {
private final ErrorDecoder defaultErrorDecoder = new Default();
@Override
public Exception decode(String methodKey, Response response) {
if (response.status() == 404) {
return new CustomNotFoundException("해당 사용자를 찾을 수 없습니다.");
}
return defaultErrorDecoder.decode(methodKey, response);
}
}
✅ 404 응답을 받을 경우, CustomNotFoundException
을 던지도록 설정
📌 2. application.yml에서 에러 디코더 등록
spring:
cloud:
openfeign:
client:
config:
user-service:
errorDecoder: com.example.CustomErrorDecoder
✅ 특정 Feign 클라이언트(user-service
)에만 적용 가능
🔥 5️⃣ Feign을 활용한 파일 업로드 (멀티파트 지원)
Feign은 기본적으로 파일 업로드(멀티파트 요청)를 지원합니다.
아래 예제에서는 이미지 파일을 서버로 업로드하는 API를 호출합니다.
📌 1. FeignClient에서 멀티파트 요청 정의
@FeignClient(name = "file-service")
public interface FileServiceClient {
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
String uploadFile(@RequestPart("file") MultipartFile file);
}
✅ @RequestPart("file")
을 사용하면, 파일 업로드 API 호출 가능
📌 2. 파일 업로드 API 호출
@RestController
@RequestMapping("/files")
public class FileController {
private final FileServiceClient fileServiceClient;
public FileController(FileServiceClient fileServiceClient) {
this.fileServiceClient = fileServiceClient;
}
@PostMapping("/upload")
public String uploadFile(@RequestParam("file") MultipartFile file) {
return fileServiceClient.uploadFile(file);
}
}
✅ fileServiceClient.uploadFile(file);
을 호출하면
✅ 파일이 자동으로 HTTP 요청을 통해 서버로 전송됩니다.
📌 결론
Spring Cloud OpenFeign을 활용하면 간결하고 직관적인 HTTP API 통신이 가능합니다.
📌 Feign의 핵심 기능을 정리하면 다음과 같습니다.
✅ 선언적 REST API 호출 (@FeignClient
)
✅ 서비스 디스커버리(Eureka)와 연동 가능
✅ Spring Boot와 통합성이 뛰어나며, 로드 밸런싱, 보안, 장애 복구 기능 지원
✅ application.yml을 활용하여 Feign 동작을 유연하게 조정 가능
✅ JWT 인증 헤더 자동 추가 (RequestInterceptor
)
✅ 커스텀 에러 처리 (ErrorDecoder
)
✅ 멀티파트 파일 업로드 지원 (@RequestPart
)
📢 Spring Cloud OpenFeign을 활용하여 더욱 효율적이고 유지보수 쉬운 마이크로서비스 아키텍처를 구축하세요! 🚀
'Spring Microservice' 카테고리의 다른 글
@LoadBalanced (0) | 2024.12.11 |
---|---|
eureka server configuration (0) | 2024.12.11 |
Spring Cloud Bus (0) | 2024.12.11 |
서비스 디스커버리 클라이언트 Push/Pull (0) | 2024.12.11 |
Gossip Protocol (0) | 2024.12.11 |