스프링부트 : RestClient

 [# New in Spring 6.1: RestClient](https://spring.io/blog/2023/07/13/new-in-spring-6-1-restclient/)
 [# REST Clients](https://docs.spring.io/spring-framework/reference/integration/rest-clients.html)

## Test코드 작성하듯 호출하고 싶다
```
mockMvc.perform(get("/api/posts")
	.contentType(MediaType.APPLICATION_JSON)
	.content(objectMapper.writeValueAsString(condition)))
	.andDo(print())
	.andExpect(status().isOk())
	.andExpect(jsonPath("$.length()", equalTo(1)));
```

 [!NOTE] fluent API
 설계가 메서드 체인에 광범위하게 의존하는 객체지향 api이다. fluent api는 DSL(domain specific language)을 만들어 코드 가독성을 높이는데 그 목적이 있다.

```
 	Author author = AUTHOR.as("author");
	create.selectFrom(author)
      .where(exists(selectOne()
                   .from(BOOK)
                   .where(BOOK.STATUS.eq(BOOK_STATUS.SOLD_OUT))
                   .and(BOOK.AUTHOR_ID.eq(author.ID))));
```

- RestTemplate과 비교
	-  [### Migrating from `RestTemplate` to `RestClient`](https://docs.spring.io/spring-framework/reference/integration/rest-clients.html#_migrating_from_resttemplate_to_restclient)
```
	restTemplate.postForObject(URI, Object, Class)
	
	restClient.post()
		.uri(String, Object...)
		.body(Object)
		.retrieve()
```

 참고영상 : [빌더와 lombok](https://youtu.be/rKpCtQL-C08)

 [!NOTE] spring에서 제공하는 4가지 REST endpoints
    - RestClient - synchronous client with a fluent API.
    - WebClient - non-blocking, reactive client with fluent API.
    - RestTemplate - synchronous client with template method API.
    - HTTP Interface - annotated interface with generated, dynamic proxy implementation.
## RestClient 생성하기
- create() 메서드를 이용한 방법
```
RestClient defaultClient = RestClient.create();
```
- builder를 이용한 방법
```java
@Bean
RestClient restClient(RestClient.Builder builder) {
	return builder
		.requestFactory(new JdkClientHttpRequestFactory())
		.messageConverters(converters -> 
				 converters.add(new MyCustomMessageConverter())) 
		.baseUrl("https://example.com")
		.defaultUriVariables(Map.of("variable", "foo")) 
		.defaultHeader("My-Header", "Foo")
		.requestInterceptor(myCustomInterceptor) 
		.requestInitializer(myCustomInitializer) 
		.build();
}
```

 [!NOTE] client request factory : HttpClient
    - JdkClientHttpRequestFactory : jdk에서 제공 [java.net.http.HttpClient](https://docs.oracle.com/en/java/javase/17/docs/api/java.net.http/java/net/http/HttpClient.html),
    - HttpComponentsClientHttpRequestFactory : 톰켓에서 제공
    - JettyClientHttpRequestFactory : 제티에서 제공
    - ReactorNettyClientRequestFactory : 네띠에서 제공

## RestClient 사용하기
```
restClient.get() 
  .uri("https://example.com") 
  .retrieve() 
  .body(String.class); 
```
- header가 필요하면 header(string, string) 넣어주면 됨. header관련 메서드도 존재
- get, post, put, delete 등 다 지원
- retrieve() 호출하면 응답 받음
```
ResponseEntity<Void> response = restClient.post() 
	.uri("https://petclinic.example.com/pets/new") 
	.contentType(APPLICATION_JSON) 
	.body(pet) 
	.retrieve() 
	.toBodilessEntity();
```

- retrieve() body에 담아서 호출
- 아래는 에러처리
```
String result = restClient.get() 
	.uri("https://example.com/this-url-does-not-exist") 
	.retrieve() 
	.onStatus(HttpStatusCode::is4xxClientError, 
			(request, response) -> 
				  { throw new MyCustomRuntimeException(response.getStatusCode(), 
						  response.getHeaders()) }) 
	.body(String.class);
```

- 아래는 응답을 다르게 주는 exchange() 예제
```
Pet result = restClient.get() 
.uri("https://petclinic.example.com/pets/{id}", id) 
.accept(APPLICATION_JSON) 
.exchange((request, response) -> { 
	if (response.getStatusCode().is4xxClientError()) { 
		throw new MyCustomRuntimeException(response.getStatusCode(), 
		response.getHeaders()); 
	} else { Pet pet = convertResponse(response); return pet; } });
```

## HTTP Interface : @HttpExchange
- 예제는 조금 이따 봅시다
	- https://github.com/ohhoonim/lms.git
	- `http://jsonplaceholder.typicode.com/posts/1/comments/`
- 어짜피 배운거니 가상스레드 옵션 켜주자(jdk 21이상)
```
# application.properties
spring.threads.vitual.enabled=true
```

- 아래 예제처럼 인터페이스만 선언해주면 위에 처럼 일일히 구현할 필요없음.
```
@HttpExchange
interface RepositoryService { 
	@GetExchange("/repos/{owner}/{repo}") 
	Repository getRepository(@PathVariable String owner, @PathVariable String repo);
}
```

- 단, 빈 등록을 미리 해주어야함. 언젠가는 자동 생성해주겠지만, 아래이슈보면 이미 만들고들 있음.
- https://github.com/spring-projects/spring-boot/issues/31337
```
@Bean
RepositoryService repositoryProxy(RestClient.Builder builder) {
	RestClient restClient = builder.baseUrl("https://api.github.com/").build();
	RestClientAdapter adapter = RestClientAdapter.create(restClient);
	HttpServiceProxyFactory factory = 
			HttpServiceProxyFactory.builderFor(adapter).build();

	return factory.createClient(RepositoryService.class);
}

```
 - 사용할 수 있는 parameter
	 - [Method Parameters](https://docs.spring.io/spring-framework/reference/integration/rest-clients.html#rest-http-interface-method-parameters)
 - 응답 객체는 다음과 같음
	 - void
	 - HttpHeaders
	 - <T>
		- ResponseEntity<Void>
		- ResponseEntity<T>


댓글

이 블로그의 인기 게시물

Session 대신 JWT를 사용하는 이유

VSCode에서의 VIM 단축키와 키보드 구매 가이드

우분투에서 테스트링크(testlink)와 맨티스(mantis)로 테스팅 서버 구성하기