@EnableFeignClients

2024. 12. 11. 15:17Spring 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의 발전 과정

  1. Netflix OSS Feign
    • 넷플릭스가 내부적으로 개발하여 서비스 간 HTTP API 호출을 단순화
    • Java 인터페이스로 API 호출을 정의하는 방식 제공
  2. 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란?

@EnableFeignClientsSpring 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를 호출합니다.
이 경우 @FeignClienturl 속성을 제거하고, 서비스 이름으로 호출할 수 있습니다.

📌 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