HTTP Client
1. RestTemplate
: 원래의 Spring에서 HTTP 요청을 보내기 위해 제공한 HTTP Client
- 오랫동안 사용된 만큼 여전히 사용처가 많음..
- since Spring 3.0...
- Spring 5 버전 이후로는 유지보수 단계.
- 명령형 인터페이스.
1) Template 준비하기
- RestTemplate을 Configuration에 Bean 객체로 등록하여 사용한다.
- 이때 RestTemplateBuilder로 상세 설정을 해줄수도 있고 바로 생성도 가능하다.
//RestTemplateConfig class
@Configuration
public class RestTemplateConfig {
//RestTemplateBuilder를 활용해 전체 서비스에서 사용할
//기본 설정을 갖춘 RestTemplate을 Bean으로 등록 가능.
@Bean
public RestTemplate defaultRestTemplate(
RestTemplateBuilder templateBuilder
) {
//그냥 새로 생성해서 사용할 수도 잇음
// RestTemplate restTemplate = new RestTemplate();
return templateBuilder.rootUri("http://localhost:8081").build();
}
}
2) GET 요청 보내기
- getForObject : 응답의 Response Body가 어떤 타입일지를 명확히 알고 있으며, 다른 Status Code 등이 필요하지 않을 경우. 돌아온 응답을 두번째 인자로 지정한 객체로 해석해서 반환해주는 메서드.
- getForEntity : 응답의 상세한 형태를 확인하고 싶다면 getForEntity를 사용. 다양하게 활용이 가능(getBody(), getStatusCode() 등)
- 응답 자료형을 잘 모르면 Object로 받아줄 수 있다. (배열은 ArrayList, JSON 객체는 LinkedHashMap으로 반환됨)
- ReadOne 하나의 정보 조회하기
- getForObject
- getForEntity
// ArticleTemplateClient class
@Component
@Slf4j
@RequiredArgsConstructor
public class ArticleTemplateClient {
private final RestTemplate restTemplate;
//GET
public ArticleDto readOne(Long id) {
//1. getForObject =객체를 받기위해 GET요청을 한다.
ArticleDto response =
restTemplate.getForObject(String.format("/articles/%d", id),
ArticleDto.class);
log.info("response: {}", response);
//2. getForEntity: ResponseEntity를 받기위해 GET요청을 보낸다.
ResponseEntity<ArticleDto> responseEntity =
restTemplate.getForEntity(String.format("/articles/%d", id),
ArticleDto.class);
log.info("responseEntity: {}", responseEntity);
log.info("status code: {}", responseEntity.getStatusCode());
log.info("headers: {}", responseEntity.getHeaders());
log.info("body: {}", responseEntity.getBody());
//3. getForObject- Object 객체로 받아줄 수도 있다.
Object responseObject = restTemplate.getForObject(
String.format("/articles/%d", id),
Object.class);
log.info("responseObject: {}", responseObject.getClass());
response = responseEntity.getBody();
return response;
}
}
// ArticleController class
@Slf4j
@RestController
@RequestMapping("/articles")
@RequiredArgsConstructor
public class ArticleController {
private final ArticleTemplateClient templateClient;
@GetMapping("/{id}")
public ArticleDto readOne(@PathVariable("id") Long id) {
return templateClient.readOne(id);
}
}
>> 결과는 요렇게 잘 나왔다.
response와 responseEntity 모두 나왔고,
responseObject도 나왔고, responseObject의 형태는 LinkedHashMap이라는 사실을 알 수 있다.
- Read All 여러개의 정보 조회하기
: JSON 배열 응답의 경우에는 배열을 응답으로 받을 수 있다! (ex. ArticleDto[])
+ 또는 exchange를 사용하면 ParameterizedTypeReference를 사용할 수 있다. (List와 같은 Generic을 사용하는 클래스의 모습을 정의하기위한 클래스)
// ArticleTemplateClient
//readAll
public List<ArticleDto> readAll() {
//1. getForObject
ArticleDto[] response =
restTemplate.getForObject("/articles",
ArticleDto[].class);
log.info("response type: {}", response.getClass());
//2. getForEntity
ResponseEntity<ArticleDto[]> responseEntity =
restTemplate.getForEntity("/articles", ArticleDto[].class);
log.info("responseEntity: {}", responseEntity);
log.info("status code: {}", responseEntity.getStatusCode());
// exchange : 일반적인 상황에서 HTTP요청의 모든 것(메서드, 헤더, 바디 등)을 묘사하여 요청하기 위한 메서드
// +ParamterizedTypeReference<T>를 사용하면 List로 반환한다.
ResponseEntity<List<ArticleDto>> responseListEntity =
restTemplate.exchange(
//String url
"/articles",
// HttpMethod method
HttpMethod.GET,
// @Nullable HttpEntity<?> requestEntity
null,
// Class<T> responseType
new ParameterizedTypeReference<>() {}
);
log.info("response parameterized: {}", responseListEntity.getBody().getClass());
//3. getForObject - Object
Object responseObject = restTemplate.getForObject("/articles", Object.class);
log.info("responseObject : {}", responseObject.getClass());
//array를 list로 변환
// return List.of(response); 혹은
return Arrays.stream(response).toList();
}
}
// ArticleController class
@Slf4j
@RestController
@RequestMapping("/articles")
@RequiredArgsConstructor
public class ArticleController {
private final ArticleTemplateClient templateClient;
@GetMapping
public List<ArticleDto> readAll() {
return templateClient.readAll();
}
}
>> 결과 로그를 보면
1) response type으로 ArticleDto 나왔고,
2) responseEntity 전체와 status code 200 OK로 나왔다
3) response parameterized된 responseEntity의 클래스는 ArrayList로 잘 저장이 되어있다.
4) Object 형식으로 저장한 것도 ArrayList 형식으로 저장되어있다는 것을 알 수 있다.
3) POST
- postForObject : 객체를 받기 위해 POST 요청을 한다.
- postForEntity : ResponseEntity를 받기 위해 POST 요청을 한다.
// ArticleTemplateClient class
@Component
@Slf4j
@RequiredArgsConstructor
public class ArticleTemplateClient {
private final RestTemplate restTemplate;
public ArticleDto create(ArticleDto dto) {
// postForObject : 객체를 받기위해 POST 요청을 한다.
ArticleDto response =
restTemplate.postForObject(
//1.요청 url
"/articles",
//2.RequestBody,
dto,
// 3.Response Body의 Type
ArticleDto.class);
log.info("response: {}", response);
//postForEntity : ResponseEntity를 받기 위해 POST 요청을 한다.
ResponseEntity<ArticleDto> responseEntity =
restTemplate.postForEntity("/articles",
dto,
ArticleDto.class);
log.info("responseEntity: {}", responseEntity);
log.info("status code: {}", responseEntity.getStatusCode());
log.info("headers: {}", responseEntity.getHeaders());
log.info("body: {}", responseEntity.getBody());
response = responseEntity.getBody();
return response;
}
}
// ArticleController class
@Slf4j
@RestController
@RequestMapping("/articles")
@RequiredArgsConstructor
public class ArticleController {
private final ArticleTemplateClient templateClient;
@PostMapping
public ArticleDto create(
@RequestBody
ArticleDto dto) {
return templateClient.create(dto);
}
}
>> 짠 Insert 정확히 들어갔다. 한 메서드에 두 번의 post 요청이 들어가게 된다.
1) response
2) responseEntity 요렇게 두번의 삽입이 일어난다!
4) PUT&DELETE
- PUT
: exchange 메서드를 이용해 HTTP 요청을 상세 묘사해준다. & 이 때 Body를 HttpEntity로 만들어주어야 한다.
> PUT의 경우 GET이나 POST와 같이 응답을 반환하는 메서드가 없기 때문에..
// ArticleTemplateClient class
// PUT
public ArticleDto update(Long id, ArticleDto dto) {
// 이렇게 표현해볼 수 있지만, 이렇게 되면 해당 id를 가진 객체가 수정이 되는 것이 아니라
// id가 null로 나오며 RequestBody ArticleDto 그 자체가 들어가게 된다.
// 반환도 하지 않음. (void put())
// 그러므로 이렇게 사용하지 않기!
restTemplate.put(String.format("/articles/%d", id),
dto);
//해답은 exchange
ResponseEntity<ArticleDto> responseEntity =
restTemplate.exchange(
// String url
String.format("/articles/%d", id),
// HttpMethod method
HttpMethod.PUT,
// HttpEntity<?> requestEntity
new HttpEntity<>(dto),
// Class<T> responseType
ArticleDto.class);
log.info("status code: {}", responseEntity.getStatusCode());
log.info("updated:: {}", responseEntity.getBody());
dto = responseEntity.getBody();
return dto;
}
}
// ArticleController class
@Slf4j
@RestController
@RequestMapping("/articles")
@RequiredArgsConstructor
public class ArticleController {
private final ArticleTemplateClient templateClient;
@PutMapping("/{id}")
public ArticleDto update(@PathVariable("id") Long id,
@RequestBody ArticleDto dto) {
return templateClient.update(id, dto);
}
}
>> exchange 메서드로 수정 완료! id가 null이 아니다~~~!
- DELETE
: DELET도 마찬가지로 delete()메서드가 아닌 exchange 메서드를 사용해야 한다. + 이 때 자료형은 Void.class를 활용.
// ArticleTemplateClient
//DELETE
public void delete(Long id) {
// 응답을 반환하지 않으므로 사용 X
restTemplate.delete(String.format("/articles/%d", id));
// 역시 해답은 exchange
// ResponseEntity<Void>: Response Body가 비어있는 응답
ResponseEntity<Void> responseEntity = restTemplate.exchange(
String.format("/articles/%d", id),
HttpMethod.DELETE,
null,
Void.class
);
//response는 article에서 보내서 받은 응답을 받아오는 것임.
log.info("status code: {}", responseEntity.getStatusCode());
}
}
//ArticleController class
@Slf4j
@RestController
@RequestMapping("/articles")
@RequiredArgsConstructor
public class ArticleController {
private final ArticleTemplateClient templateClient;
@DeleteMapping("{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void delete(@PathVariable("id") Long id) {
templateClient.delete(id);
}
}
>> 4~7까지 삭제 완료.
5) Url 구성하기
- String.format() 대신 가변인자로 uriVariables 또는 Map<String, ?> uriVariables을 받는다. >> 인자로 전달한 URL의 일부분을 값으로 대치해서 활용한다.
// ArticleTemplateClient class
//readAll
public List<ArticleDto> readAll() {
// ... 중략
// 1. URL 인자 대체하기, 가변갯수인자
Object responsePage = restTemplate.getForObject(
// String url
"/articles/paged?page={page}&limit={limit}",
// Class<T> responseType
Object.class,
// Object ... uriVariables
0, //page
5 //limit
);
log.info("response object page: {}", responsePage);
// 2. URL 인자 대체하기, Map<String, Object>
Map<String, Object> uriVariables = new HashMap<>();
uriVariables.put("page", 0);
uriVariables.put("limit", 5);
responsePage = restTemplate.getForObject(
"/articles/paged?page={page}&limit={limit}",
Object.class,
uriVariables
);
log.info("response object page: {}", responsePage);
return Arrays.stream(response).toList();
}
>> 이렇게 페이징 되어 결과가 잘 나온다.
그러나, Query를 많이 추가해야 하는 경우 URL 자체가 길어질 수 있다 >> 이 때 UriComponentsBuilder를 사용한다.
- UriComponentsBuilder 사용.
이 때 Builder 패턴 형식으로 Path나 Query Parameter를 설정할 수 있다.
// ArticleTemplateClient class
//readAll
public List<ArticleDto> readAll() {
//... 중략
// 3. UriComponentsBuilder 사용
log.info("UriComponentsBuilder 사용: "+UriComponentsBuilder.fromUriString("/articles")
.queryParam("page", 0)
.queryParam("limit", 5)
.toUriString());
return Arrays.stream(response).toList();
}
이렇게 url을 설정해줄수가 있다
** 단, 인자로 %, & 와 같이 URL 상에 표현될 때 특수한 역할이 있는 문자열의 경우....문제가 생길 수 있다.
> UriComponentsBuilder에서 한번 URL Encoding이 진행된다. 이를 다시 RestTemplate에 전달할 경우 이중으로 Encoding이 될 수 있다.
// ArticleTemplateClient class
//readAll
public List<ArticleDto> readAll() {
//... 중략
// /test?foo=%25%26 -> /test?foo=%2525%2526
log.info(UriComponentsBuilder.fromUriString("/articles/test")
.queryParam("foo", "%&")
.toUriString());
// /test?foo=%& -> /test?foo=%25%26
log.info(UriComponentsBuilder.fromUriString("/test")
.queryParam("foo", "%&")
.build(false)
.toUriString());
}
>> build를 false로 설정하여 이중으로 Encoding되지 않도록 하였다.
2. WebClient
: 반응형(Reactive) 웹 개발의 수요에 맞춰서 등장한 HTTP Client
- 어떤 함수의 결과에 따라 동작하지 않고, 어떤 엔티티의 변화에 반응해서 동작하는 프로그래밍.
(반응형 웹은 미래에 다시 알아보자..)
(cf. Bootstrap도 반응형 UI이지만 이때에는 Responsive Web)
1) 반응형 웹 관련 의존성 추가.
// build.gradle
implementation 'org.springframework.boot:spring-boot-starter-webflux'
2) WebClient 준비하기
: class가 아닌 interface로, builder를 이용해 인스턴스 생성. + 여러가지 기본 설정도 가능.
// WebClientConfig
@Configuration
public class WebClientConfig {
@Bean
public WebClient defaultWebClient() {
// 그냥 새로 생성해서 사용할수도 있음
// WebClient webClient = WebClient.create();
// RestTemplate에 비해 선언형 (함수형) 구조를 가진다.
return WebClient.builder()
.baseUrl("http://localhost:8081")
//처음부터 Header를 기록해두기 때문에 바꿀수가 없다.
.defaultHeader("test", "foo")
//request의 header의 default값을 설정해준거기 때문에 바꿀 수 있다.
.defaultRequest(request -> request.header("test", "value"))
.defaultStatusHandler(
HttpStatusCode::isError,
response -> {
throw new ResponseStatusException(response.statusCode());
}
)
.build();
}
}
1) 요청 보내기
- Builder를 사용하는 느낌으로 요청을 보낸다.
- get(), post() 메서드를 이용하여 메서들 설정하고 HTTP 요청의 모습을 묘사한다.
- POST
- (1) ArticleDto로 받기 : bodyToMono()
- (2) ResponseEntity로 받기 : toEntity()
- Mono : 결과가 돌아올 때 어떻게 행동할지를 정의하기 위한 Reactor. 즉시 데이터를 활용하는 것이 아닌, 비동기로 데이터가 들어오는 것을 의미
- block : 실제로 데이터가 도착할 때까지 대기해, 원하는 형태의 응답으로 돌려준다.
// ArticleWebClient class
@Slf4j
@Component
@RequiredArgsConstructor
public class ArticleWebClient {
private final WebClient webClient;
public ArticleDto create(ArticleDto dto) {
// 1. ArticleDto로 받기 : bodyToMono()
//WebClient는 HTTP 요청을 Build한다고 생각해보자.
ArticleDto response = webClient
//POST 요청이다.
.post()
// uri 경로 설정
.uri("/articles")
//Body 설정
.bodyValue(dto)
// 여기부터 응답을 어떻게 처리할지로 넘어간다.
.retrieve()
//응답을 Mono<ArticleDto>롤 받는다. (반응형 아니면 mono)
.bodyToMono(ArticleDto.class)
// 응답을 대기해 결과를 돌려받는다. (동기식 처리)
.block();
log.info("response: {}", response);
//2. ResponseEntity로 받기 : toEntity()
ResponseEntity<ArticleDto> responseEntity =
webClient
//POST 요청이다.
.post()
// uri 경로 설정
.uri("/articles")
//Body 설정
.bodyValue(dto)
// 여기부터 응답을 어떻게 처리할지
.retrieve()
// ResponseEntity가 담긴 Mono를 받는다.
.toEntity(ArticleDto.class)
.block();
log.info("responseEntity: {}", responseEntity);
response = responseEntity.getBody();
return response;
}
}
>> 이렇게 잘 insert 되고 있다.
- GET
- readOne()
- readAll()
// ArticleWebClient class
@Slf4j
@Component
@RequiredArgsConstructor
public class ArticleWebClient {
private final WebClient webClient;
//readOne .get()
public ArticleDto readOne(Long id) {
// 1. ArticleDto로 받기
ArticleDto response = webClient
.get()
.uri("/articles/{id}", id)
.retrieve()
.bodyToMono(ArticleDto.class)
.block();
log.info("Read one ~ response: {}", response);
// 2. Map을 활용한 uriVariables를 인자로 넣어 요청하기
Map<String, Object> uriVariables = new HashMap<>();
uriVariables.put("id", id);
response = webClient
.get()
.uri("/articles/{id}", uriVariables)
.retrieve()
.bodyToMono(ArticleDto.class)
.block();
log.info("Read One ~ map response: {}", response);
return response;
}
//readAll .get()
public List<ArticleDto> readAll() {
//ParameterizedTypeReference 활용
List<ArticleDto> response = webClient.get()
.uri("/articles")
.retrieve()
.bodyToMono(new ParameterizedTypeReference<List<ArticleDto>>() {
})
.block();
log.info("ReadAll ~ response type: {}", response.getClass());
return response;
}
}
이렇게 잘 나온당~~ 아 참고로 Controller에서 ArticleWebClient를 주입받아 얘를 불러 메서드 사용해주는 걸 잊으면 안된다!
- PUT & DELETE
// ArticleWebClient class
@Slf4j
@Component
@RequiredArgsConstructor
public class ArticleWebClient {
private final WebClient webClient;
public ArticleDto update(Long id, ArticleDto dto) {
Map<String, Object> uriVariables = new HashMap<>();
uriVariables.put("id", id);
ArticleDto response = webClient.put()
.uri("/articles/{id}", uriVariables)
.bodyValue(dto)
.retrieve()
.bodyToMono(ArticleDto.class)
.block();
log.info("response : {}", response);
ResponseEntity<ArticleDto> responseEntity = webClient.put()
.uri("/articles/{id}", uriVariables)
.bodyValue(dto)
.retrieve()
.toEntity(ArticleDto.class)
.block();
log.info("ResponseEntity로 받기 response : {}", responseEntity.getBody());
return responseEntity.getBody();
}
public void delete(Long id) {
Map<String, Object> uriVariables = new HashMap<>();
uriVariables.put("id", id);
ResponseEntity<?> responseEntity = webClient.delete()
.uri("/articles/{id}", uriVariables)
.retrieve()
.toBodilessEntity()
.block();
log.info("delete status code: {}", responseEntity.getStatusCode());
}
}
3. RestClient
: RestTemplate을 대체하는 새로운 동기식 HTTP Client
- WebClient와 거의 비슷하다!
- Mono 대신 Body 객체, ResponseEntity가 반환되는 것 외엔 거의 동일하다.
- config
// RestClient class
@Configuration
public class RestClientConfig {
@Bean
// RestClient.Builder를 활용해 전체 서비스에서 사용할
// 기본 설정을 갖춘 RestClient Bean으로 등록 가능
public RestClient defaultRestClient() {
// RestClient restClient = RestClient.create();
return RestClient.builder()
.baseUrl("http://localhost:8081")
// authentication에 관한 header 설정 가능.
// .defaultHeader("Authentication", "Bearer token")
.defaultHeader("test0", "foo")
.requestInitializer((request -> request.getHeaders().add("test", "header")))
.defaultRequest(request ->
request.header("test1", "bar"))
.defaultStatusHandler(
HttpStatusCode::isError, (request, response)
-> {
throw new ResponseStatusException(response.getStatusCode());
}
)
.build();
}
}
- 동기식 WebClient
: 바로 응답 객체가 반환된다!
- POST
// ArticleRestClient class
@Slf4j
@Component
@RequiredArgsConstructor
public class ArticleRestClient {
private final RestClient restClient;
public ArticleDto create(ArticleDto dto) {
ArticleDto response = restClient.post()
.uri("/articles")
.body(dto)
.retrieve()
.body(ArticleDto.class);
log.info("response: {}", response);
ResponseEntity<ArticleDto> responseEntity =
restClient.post()
.uri("/articles")
.body(dto)
.retrieve()
.toEntity(ArticleDto.class);
log.info("responseEntity: {}", responseEntity);
response = responseEntity.getBody();
return response;
}
}
- GET
// ArticleRestClient class
@Slf4j
@Component
@RequiredArgsConstructor
public class ArticleRestClient {
private final RestClient restClient;
public ArticleDto readOne(Long id) {
ResponseEntity<ArticleDto> responseEntity = restClient.get()
.uri("/articles/{id}", id)
.retrieve()
.toEntity(ArticleDto.class);
log.info("responseEntity, Object...: {}", responseEntity);
Map<String, Object> uriVariables = new HashMap<>();
uriVariables.put("id", id);
responseEntity = restClient.get()
.uri("/articles/{id}", id)
.retrieve()
.toEntity(ArticleDto.class);
log.info("responseEntity, Map: {}", responseEntity);
return responseEntity.getBody();
}
public List<ArticleDto> readAll() {
return restClient.get()
.uri("/articles")
.retrieve()
.body(new ParameterizedTypeReference<>() {
});
}
}
- PUT, DELETE
// ArticleRestClient class
@Slf4j
@Component
@RequiredArgsConstructor
public class ArticleRestClient {
private final RestClient restClient;
public ArticleDto update(Long id, ArticleDto dto) {
Map<String, Object> uriVariables = new HashMap<>();
uriVariables.put("id", id);
ResponseEntity<ArticleDto> responseEntity = restClient.put()
.uri("/articles/{id}", uriVariables)
.body(dto)
.retrieve()
.toEntity(ArticleDto.class);
log.info("update responseEntity: {}", responseEntity);
return responseEntity.getBody();
}
public void delete(Long id) {
ResponseEntity<Void> responseEntity = restClient
.delete()
.uri("/articles/{id}", id)
.retrieve()
.toBodilessEntity();
log.info("delete responseEntity: {}", responseEntity);
}
}
4. HTTP Interface
: Java의 interface를 바탕으로 HTTP 요청을 선언적 방식으로 묘사하는 방법.
- Spring 6 이후.
1) interface 정의하기
- CRUD 관련 메서드 5가지를 inteface에 정의한다.
// ArticleHttpInterface
@HttpExchange("/articles")
public interface ArticleHttpInterface {
//CRUD를 할거기때문에 해당하는 메서드를 다 만든다.
ArticleDto create();
List<ArticleDto> readAll();
ArticleDto readOne();
ArticleDto update();
void delete();
}
- 각각 메서드의 실제 기능이 어떤 HTTP Method로 요청이 보내질지에 따라 > @<Method>Exchange 어노테이션을 추가한다.
+ 요청에 추가할 Body나 Path 변수, Query Parameter 등을 매개변수로 추가해준다.
// ArticleHttpInterface interface
@HttpExchange("/articles")
public interface ArticleHttpInterface {
// @<Method>Exchange 어노테이션은 해당 메서드가 실행될 때
// HTTP Request의 메서드와 Path를 결정한다.
// Path, Body, (Query) Parameter를 매개변수로
// 나타내면 HTTP Request에 포함된다.
//CREATE
@PostExchange
ArticleDto create(
@RequestBody
ArticleDto dto
);
//READ ALL
@GetExchange
List<ArticleDto> readAll();
//READ ONE
@GetExchange("/{id}")
ArticleDto readOne(@PathVariable("id")
Long id);
//UPDATE
@PutExchange("/{id}")
ArticleDto update(
@PathVariable("id") Long id,
@RequestBody ArticleDto dto
);
//DELETE
@DeleteExchange("/{id}")
void delete(@PathVariable("id") Long id);
}
2) Proxy 생성
- 실제로 요청을 실행할 HTTP Client를 이용해 Proxy 객체를 만들어야 한다. >> HttpServiceProxyFactory
- RestClientAdapter.create(restClient) : restClient를 이용해 RestClientAdapter를 생성하고, 이를 통해 HTTP Client를 만든다.
- HttpServiceProxyFactory.builderFor(...) : Proxy를 만드는 Factory를 생성
- .build() : Factory를 빌드.
- .createClient(ArticleHttpInterface.class) : 위에서 설정한 RestClient를 바탕으로 ArticleHttpInterface 인터페이스의 Proxy 객체를 생성.
- 생성된 객체를 exchange 필드에 할당하여 외부 HTTP 서비스와 통신할 수 있도록 한다.
@Component
public class ArticleService {
// 사용할 때는 구현체를 만들어주어야 한다.
private final ArticleHttpInterface exchange;
//생성자
public ArticleService(
//실제로 요청을 보내는 역할을 하는
// HTTP Client 객체가 있어야 한다.
RestClient restClient
) {
exchange = HttpServiceProxyFactory
//내가 사용할 HTTP Client를 사용할 수 있도록 설정.
.builderFor(RestClientAdapter.create(restClient))
// Proxy를 만드는 Factory를 만든다.
.build()
//해당 RestClient를 바탕으로 Proxy 객체를 만든다.
.createClient(ArticleHttpInterface.class);
}
}
3) 일반적인 Java 객체를 사용하듯 메서드 호출.
- 앞서 사용한 RestClient와 동일한 설정을 활용. +뿐만 아니라 WebClient, RestTemplate도 사용 가능 원한다면
- WebClient 사용시 반환형으로 Mono나 Flux를 사용 가능.
// ArticleService class
@Component
public class ArticleService {
// 사용할 때는 구현체를 만들어주어야 한다.
private final ArticleHttpInterface exchange;
public ArticleService(
//실제로 요청을 보내는 역할을 하는
// HTTP Client 객체가 있어야 한다.
// RestClient 객체를 받아와 구현체를 생성할 것.
RestClient restClient
) {
// Factory가 restClient를 이용하여 RestClientAdapter를 생성하고,
// 이를 통해 HTTP Client를 만든다.
// Factory.builderFor()를 사용하여
exchange = HttpServiceProxyFactory
//내가 사용할 HTTP Client를 사용할 수 있도록 설정.
.builderFor(RestClientAdapter.create(restClient))
// Proxy를 만드는 Factory를 만든다.
.build()
//해당 RestClient를 바탕으로 Proxy 객체를 만든다.
.createClient(ArticleHttpInterface.class);
}
public ArticleDto create(ArticleDto dto) {
return exchange.create(dto);
}
public ArticleDto readOne(Long id) {
return exchange.readOne(id);
}
public List<ArticleDto> readAll() {
return exchange.readAll();
}
public ArticleDto update(Long id, ArticleDto dto) {
return exchange.update(id, dto);
}
public void delete(Long id) {
exchange.delete(id);
}
}