@RequestMapping, @PathVariable, @RequestParam 등 스프링 MVC 어노테이션을 그대로 사용할 수 있습니다.
유연한 설정
인터셉터, 디코더, 인코더 등을 커스터마이징할 수 있습니다.
로드 밸런싱
Ribbon과 통합되어 클라이언트 사이드 로드 밸런싱을 지원합니다.
서킷 브레이커
Hystrix/Resilience4j와 통합하여 장애 허용 패턴을 구현할 수 있습니다.
2. 외부 통신 클라이언트 비교 : RestTemplate, WebClient, OpenFeign
💡 외부 통신 클라이언트 비교 : RestTemplate, WebClient, OpenFeign
- Spring Boot 환경에서 사용하는 외부 통신 클라이언트로는 RestTemplate, WebClient, OpenFeign를 이용합니다. 각각에 대해서 비교해 봅니다.
1. RestTemplate - Spring 3.0부터 제공된 전통적인 HTTP 클라이언트로 동기식으로 데이터 통신이 처리되며 응답을 받을 때까지 블로킹 방식으로 응답을 기다리는 통신이 이루어집니다.
2. WebClient - WebFlux의 일부인 Webclient는 비동기적인 방식으로 HTTP 요청을 보내고 응답을 기다리지 않는 논-블로킹 방식으로 통신이 이루어집니다.
3. OpenFeign - Spring Cloud의 일부인 OpenFeign는 선언적 REST 클라이언트로, 인터페이스와 어노테이션만으로 HTTP API 클라이언트를 작성하며 동기식으로 데이터 처리되며 응답을 받을 때까지 블로킹 방식으로 응답을 기다리는 통신이 이루어집니다.
- 아래와 같이 URL, HTTP Method, 요청/응답 데이터 지정 및 모든 처리에 대해서 작성해야 하며, 이는 코드가 길어지고 반복적인 작성이 필요합니다. - RestTemplate 인스턴스를 생성하고 .getForObject() 메서드를 통해서 HTTP 요청을 수행합니다. - 첫 번째 인자로는 호출 엔드포인트를 지정하고, 두 번째는 전송데이터를 지정, 마지막은 반환되는 타입을 지정합니다.
- 메서드 체이닝 방식을 사용하기에 직관적이고 간결한 코드를 확인할 수 있습니다. - WebClient 인스턴스를 생성하고 .create 메서드를 통해서 통신하려는 도메인을 지정합니다. - .get()는 HTTP Method를 지정하며, .url을 통해 엔드포인트를 지정하고 값을 전달하는 방식입니다.
- 주로 메인 애플리케이션 클래스나 구성 클래스 내에 선언하며, @ComponentScan과 유사하게 작동하여 @FeignClient가 선언된 인터페이스들을 찾아내는 역할을 수행합니다. - 찾아낸 구현체를 Spring Bean에 등록하여 Open Feign 관련 자동구성을 활성화합니다.
💡 @EnableFeignClients 속성
속성
리턴 타입
설명
value
Class<?><[]>
FeignClient를 스캔할 패키지들을 지정
basePackages
String[]
value와 동일한 역할을 하며 FeignClient를 스캔할 패키지 지정
basePackageClasses
Class<?><[]>
지정된 클래스들이 있는 패키지를 스캔
defaultConfiguration
Class<?><[]>
모든 Feign 클라이언트에 적용될 기본 구성 클래스 지정
clients
String[]
특정 FeignClient 클래스들만 등록하도록 명시적으로 지정
💡 @EnableFeignClients 어노테이션 속성 예시
- 아래와 같이 @FeignClient가 선언된 스캔을 위해 basePackages 속성으로 패키지를 지정하거나 defaultConfiguration 속성으로 특정 클래스를 지정합니다. - 기본적으로 @EnableFeignClients 속성 없이 선언하는 경우 모든 패키지나 클래스를 스캔합니다.
💡 @FeignClient - HTTP 클라이언트를 생성하기 위한 인터페이스를 선언하는 어노테이션입니다. 이 어노테이션을 사용하면 REST API를 호출하는 인터페이스를 쉽게 정의할 수 있습니다. - 선언만으로 REST 클라이언트를 정의하며, Spring Runtime 단계에서 해당 인터페이스의 구현체를 자동으로 생성합니다. - @GetMapping, @PostMapping 등의 Spring MVC 어노테이션을 그대로 사용할 수 있습니다.
- name 속성: name = "user-service": Feign 클라이언트의 이름을 지정 - url 속성: url = "http://api.example.com": 요청을 보낼 대상 서버의 기본 URL을 지정 - @GetMapping("/users/{id}"): HTTP GET 요청을 위한 메서드로, 특정 사용자 정보를 조회 - @PostMapping("/users"): HTTP POST 요청을 위한 메서드로, 새로운 사용자를 생성
@FeignClient(name = "user-service", url = "http://api.example.com")publicinterfaceUserClient{
@GetMapping("/users/{id}")User getUser(@PathVariable("id") Long id);
@PostMapping("/users")User createUser(@RequestBody User user);
}
2) Spring Cloud OpenFeign 환경설정
1. 개발 환경
개발 환경
버전
java
17
spring boot
3.3.6
spring-boot-starter-web
3.3.6
spring-cloud-starter-openfeign
4.1.3
spring-cloud-dependencies
2023.0.3
lombok
-
💡[참고] 무료로 REST API를 테스트해볼 수 있는 JSONPlaceholder 페이지를 통해서 데이터 통신 테스트를 수행합니다.
- 공통 도메인 주소는 https://jsonplaceholder.typicode.com/입니다. REST API 형태로 테스트를 위해 호출이 가능합니다.
- Spring Cloud OpenFeign을 이용하기 위한 “spring-cloud-starter-openfeign”라이브러리를 추가해야 합니다.
- 또한, org.springframework.cloud:spring-cloud-dependencies도 함께 추가해야 합니다. 이를 추가하는 이유는 OpenFeign의 경우 Spring Cloud의 다른 컴포넌트들과 함께 작동하기 위해서 추가해야 합니다. - 해당 프로젝트에서는 3.3.6 버전을 기준으로 구성하였기에 2023.0.3을 버전을 사용합니다.
plugins {
id 'java'
id 'org.springframework.boot' version '3.3.6'
id 'io.spring.dependency-management' version '1.1.6'
}
ext {
set('springCloudVer', "2023.0.3")
}
dependencies {
implementation "org.springframework.cloud:spring-cloud-starter-openfeign:4.1.3"
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVer}"
}
}
💡 [참고] org.springframework.cloud:spring-cloud-dependencies는 무슨 버전으로 받아야 할까?
- 공식 사이트의 표에 따르면 아래와 같이 Spring Boot Version에 따라서 Spring Cloud 지원 버전이 달라집니다. - 해당 글에서는 가장 최신 버전이 되는 Spring Boot 3.4.0 버전을 사용하기에 2023.0.3 버전을 이용하여 의존성을 추가하였습니다.
- OpenFeignService 인터페이스의 인스턴스를 생성하여서 해당 서비스의 값을 받아서 추가적인 비즈니스 로직을 구성합니다.
- getFilteredPosts()의 경우는 게시물 id가 30 초과인 경우로 필터링을 적용하여 비즈니스 로직 처리를 하였습니다. - getFilterBodySummary() 경우는 body 값이 100자가 초과되는 경우 “…” 처리를 수행하며, 초과되지 않는 경우는 기존의 값을 그대로 리턴합니다.
package com.adjh.springbootexternalnetwork.service;
import com.adjh.springbootexternalnetwork.dto.PostResponseDto;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
/**
* OpenFeign 통신을 통해 가져온 응답 값을 통해 비즈니스 로직을 처리합니다.
*/@ServicepublicclassOpenFeignBusinessService{
privatefinal OpenFeignService openFeignService;
publicOpenFeignBusinessService(OpenFeignService openFeignService){
this.openFeignService = openFeignService;
}
// 특정 조건으로 게시물 필터링public List<PostResponseDto> getFilteredPosts(){
List<PostResponseDto> posts = openFeignService.getPosts();
return posts.stream()
.filter(post -> post.getId() > 30)
.collect(Collectors.toList());
}
// 게시물 데이터 가공public PostResponseDto getFilterBodySummary(String id){
PostResponseDto post = openFeignService.getPostById(id);
// 컨텐츠 요약 추가if (post.getBody().length() > 100) {
post.setBody(post.getBody().substring(0, 100) + "...");
} else {
post.setBody(post.getBody());
}
// 추가 데이터 처리 로직return post;
}
}
3. 결과 확인
💡결과 확인
- 아래와 같이 게시물 아이디가 30 초과인 리스트 값만 출력하도록 필터링에 대한 비즈니스로직을 구성하였습니다.
💡 아래와 같이 게시물 문자열 길이가 100자 초과인 경우 “…”을 적용하여 출력하도록 하였습니다.
4) Spring Cloud OpenFeign 활용하기 -2 : Header 설정
💡 Spring Cloud OpenFeign Header 설정
- OpenFeign를 통해 데이터 통신을 수행할 때, 추가적인 Header를 보내야 하는 경우도 있습니다. 이 경우를 위해서 Header를 설정하는 방법에 대해서 알아봅니다.
1. @RequestHeader 사용하는 방법
💡@RequestHeader 사용하는 방법
- 메서드 별로 직접 헤더를 지정하는 방식을 의미합니다. 이는 각 API 호출마다 다른 헤더값이 필요한 경우에 유용합니다. - @RequestHeader 어노테이션을 통해 메서드 파라미터로 헤더값을 전달할 수 있습니다. 인증 토큰과 같이 동적으로 변하는 헤더값을 처리하기에 적합합니다.
💡 @RequestHeader 해당 예시
- https://jsonplaceholder.typicode.com 사이트에 Header를 전달하는 방식입니다. - /todos 엔드포인트에서는 header로 Authorization 값을 전달합니다. - todos/{id} 엔드포인트에서는 header로 Authorization, x-refresh-token를 전달하고, todos/{id} 형태로 id 값을 전달합니다.