[Spring Cloud] Spring Cloud OpenFeign - 기본 개념 및 활용

mrcocoball·2024년 1월 8일
0

Spring Cloud

목록 보기
5/8

해당 포스트는 Spring Cloud에 속한 기술들에 대한 개념과 주요 기술에 대해 알아보고 실무에 적용했었던 내용을 정리하는 포스트입니다.

1. 개요

서버 간 동기 통신 클라이언트

MSA와 관련된 패턴 중 어플리케이션 패턴으로 마이크로서비스 간 동기 통신 / 비동기 통신이 있는데요.
기능 / 관심사 / 도메인 별로 분리된 마이크로서비스 간 협력이나 트랜잭션을 수행해야 할 때 가장 쉽게 사용하는 것이 REST API를 활용한 동기 통신입니다.

이 때 서버 측에서는 다른 서버에 통신을 보내는 동기 통신 클라이언트가 있어야 하는데, 대표적으로 RestTemplate과 Spring Cloud OpenFeign이 있습니다.

Spring Cloud OpenFeign이란

Spring Cloud OpenFeign은 Spring Cloud 프로젝트에 포함된 동기 통신 클라이언트로, 선언적 REST 클라이언트로서 웹 서비스 클라이언트 작성을 보다 쉽게 할 수 있습니다.

직접 RestTemplate을 호출해서 대상 서버에게 통신을 요청하는 기존 방식과는 달리 인터페이스로 선언만 해두면 자동으로 구현체가 생성되는 형식입니다.

// 인터페이스를 선언
@FeignClient(name = "example-service", url = "${example-service.url}")
public interface ExampleFeignClient {

    @GetMapping("/api/resource")
    String getResource();
}

RestTemplate과 Spring Cloud OpenFeign의 차이점

RestTemplate과 Spring Cloud OpenFeign의 주요 차이점은 다음과 같습니다.

  • 명령형 API vs 선언적 API
    • RestTemplate은 명령형 API로 HTTP 요청을 생성하고 수행
    • Spring Cloud OpenFeign은 선언적 API로 인터페이스를 정의, 어노테이션을 사용하여 해당 인터페이스를 통해 HTTP 클라이언트를 생성 -> 명시적으로 HTTP 요청을 작성하지 않고도 원격 서비스 호출이 가능
  • 자동 구성
    • RestTemplate은 RestTemplate 빈 등록 및 호출, 메서드를 통한 명령을 직접 처리해야 함
    • Spring Cloud OpenFeign은 인터페이스만 선언하더라도 클라이언트 구현체가 자동 구성됨
  • URL 템플릿
    • RestTemplate은 URL 템플릿을 사용하여 동적인 요청 URL을 생성할 수 있음
    • Spring Cloud OpenFeign은 어노테이션을 사용하여 동적인 URL을 정의할 수 있음

2. 활용 예제

설정

Spring Cloud OpenFeign의 기능을 활성화하는 방법은 간단합니다. 메인 클래스에 다음과 같은 주석을 추가합니다.

@EnableFeignClients // OpenFeign 활성화
@SpringBootApplication
public class TestApplication {

    public static void main(String[] args) {
        SpringApplication.run(TestApplication.class, args);
    }

}

기능을 활성화 한 후, 클라이언트에 대한 구성 클래스를 생성하면 됩니다.
다만 여기서 LoadBalancer가 존재하지 않거나 Ribbon을 쓰지 않는 로컬 환경에서의 구성 클래스는 다음과 같이 진행해야 하는데요.

@Configuration
public class FeignConfig {

    // Ribbon 로드 밸런서를 사용하는 것이 기본값이라 이 부분을 null로 처리
    // 처리하지 않을 경우 아래의 오류 메시지 출력
    // No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-loadbalancer?
    @Bean
    public Client feignClient() {
        return new Client.Default(null, null);
    }

}

반면 LoadBalancer를 사용할 수 있는 환경이라면 별도의 Client 설정을 처리하지 않아도 됩니다.
그밖에 Client 설정에서 SSL 관련 설정 등 다양한 설정을 할 수 있습니다.

application.yml에서도 부가 설정을 처리할 수 있습니다.

spring:
  cloud:
    openfeign:
      httpclient:
        enabled: true
        connection-timeout: 5000 # 통신 요청 후 서버 연결 시간이 5초 경과 시 connection-time out 처리
        ok-http:
          read-timeout: 5000 # 응답 데이터를 읽는 시간이 5초 경과 시 read-time out 처리

클라이언트 인터페이스를 활용한 마이크로서비스 간 동기 통신

인터페이스와 선언적 API로 클라이언트를 간단하게 만들 수 있다고 했는데 예시를 들면 다음과 같습니다.

// Client 선언부, name 또는 url 사용 가능
// name은 Spring Cloud Discovery Client 
// 또는 Spring Cloud Kubernetes Discovery Client로 서비스 이름을 직접 등록할 수 있음
@FeignClient(url = "http://localhost:8081")
public interface SampleClient {

    @GetMapping("/api/v1/internal/sample/cases")
    ResponseEntity<List<CaseDTO>> getCaseList(@RequestHeader("Internal-Auth-Token") String token);

    @GetMapping("/api/v1/internal/sample/cases/{id}")
    ResponseEntity<CaseDTO> getCase(@RequestHeader("Internal-Auth-Token") String token,
                                    @PathVariable("id") Long id);

}

위의 코드는 test라는 마이크로서비스에서 sample(url은 http://localhost:8081)이라는 마이크로서비스의 특정 엔드포인트로 통신을 요청하는 인터페이스를 설정한 것입니다.

클라이언트 어노테이션에 요청을 받을 대상의 URL이나 서비스 이름을 선언할 수 있습니다. (name에 서비스 이름 대신 URL을 넣어도 작동합니다)

그리고 클라이언트가 sample이라는 마이크로서비스에 보낼 요청에 대한 메서드를 작성할 때 HTTP 메서드와 URI, 리턴값, 요청 헤더와 PathVariable을 모두 선언할 수 있습니다.

반대로 sample이라는 마이크로서비스에서는 test가 요청을 보낼 수 있도록 위의 인터페이스에서 선언한 메서드를 그대로 구현하면 됩니다.

    
    // sample에서 동기 통신을 받을 API와 관련된 Controller 내부
    
    @GetMapping("/api/v1/internal/sample/cases")
    public ResponseEntity<List<CaseDTO>> getCaseList(@RequestHeader("Internal-Auth-Token") String token) {
    	return ResponseEntity.ok().body(...);
    }

    @GetMapping("/api/v1/internal/sample/cases/{id}")
    public ResponseEntity<CaseDTO> getCase(@RequestHeader("Internal-Auth-Token") String token,
                                    @PathVariable("id") Long id) {
        return ResponseEntity.ok().body(...);                    
   	}

마지막으로 test에서는 위의 클라이언트를 활용할 수 있습니다.

@RequiredArgsConstructor
@RestController
public class TestController {

    // Client 인터페이스 사용
    private final SampleClient client;
    
    @GetMapping("/api/v1/cases")
	public void handleSampleClient() {
    
    	// 클라이언트를 활용해 sample 서비스와 동기 통신을 하여 응답을 가져옴
    	ResponseEntity<List<CaseDTO>> result = client.getCaseList();
        
        // ...
    
    }

Appendix. 레퍼런스

https://docs.spring.io/spring-cloud-openfeign/reference/spring-cloud-openfeign.html

profile
Backend Developer

0개의 댓글