HTTP Clients in Spring Boot
๋จผ์ , ์คํ๋ง์์ ์ธ๋ถ API๋ฅผ ํธ์ถํ๋ ๋ฐฉ๋ฒ์ ๋ํด์ ํ๋์ฉ ์์๋ณด์.
Java ํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ธ HttpURLConnection ์ ๋์ด๊ฐ์.
๊ธฐ๋ณธ์ ์ธ ์์ฒญ/์๋ต ๊ธฐ๋ฅ์ ์ ๊ณตํ์ง๋ง, ์๋ต ์ฒ๋ฆฌ๊ฐ ๋ณต์กํ๋ฉฐ ์ฝ๋์ ๊ฐ๋ ์ฑ์ด ๋ฎ๋ค๋ ๋จ์ ์ผ๋ก ์ ์ฌ์ฉํ์ง ์๋๋ค.
1. RestTemplate
๋ํ์ ์ธ Spring์ HTTP Client ์ค ํ๋์ด์ง๋ง, ๋ค์๊ณผ ๊ฐ์ ๋์์ ๊ถ์ฅํ๊ณ ์๋ค.
๋๊ธฐ์ ์์ฒญ: RestClient
๋น๋๊ธฐ/์คํธ๋ฆฌ๋ฐ: WebClient
์ฃผ์ ํน์ง
์ค๋ซ๋์ Spring ์ง์์์ ์ง์๋๊ณ ์์
๋๊ธฐ์ Blocking ๋ฐฉ์์ผ๋ก ๋์
RESTful ์์น ์ค์
๊ธฐ๋ณธ์ ์ธ ์ฌ์ฉ๋ฒ
Bean ๋ฑ๋ก
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
์์ฒญ ์์
@Service
public class ExampleService {
private final RestTemplate restTemplate;
public String getData() {
return restTemplate.getForObject(
"http://example.com/api",
String.class
);
}
}
RestTemplate์ ์ฌ์ฉํ์ฌ ์ธ๋ถ API๋ฅผ ํธ์ถํด์ผํ๋ ์ํฉ์ผ ๊ฒฝ์ฐ ํ์์์(Timeout), ์ฌ์๋(retry), ๋ก๊น (Logging), ์ํท ๋ธ๋ ์ด์ปค ๋ฑ์ ์ค์ ํ๋ ๋ฐฉ๋ฒ์ ์์๋๋ฉด ์ข์ ๊ฒ ๊ฐ๋ค.
2. WebClient
Spring WebFlux์ ํฌํจ๋ HTTP ํด๋ผ์ด์ธํธ๋ก, Non-Blocking I/O๋ฅผ ํ์ฉํ ๋ฆฌ์กํฐ๋ธ ํ๋ก๊ทธ๋๋ฐ ๋ชจ๋ธ์ ๊ธฐ๋ฐ์ผ๋ก ํ๋ค.
์ฃผ์ ํน์ง
๋น๋๊ธฐ/Non-Blocking ๋ฐฉ์ ์ง์
์ฑ๊ธ ์ค๋ ๋ ๋ฐฉ์์ผ๋ก ๋์
Reactor ๊ธฐ๋ฐ์ ์ ์ธ์ API ์ ๊ณต
Reactor: JVM ์์์ ๋์ํ๋ ๋ ผ๋ธ๋กํน ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ง๋ค๊ธฐ ์ํ ๋ฆฌ์กํฐ๋ธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
๊ธฐ๋ณธ์ ์ธ ์ฌ์ฉ๋ฒ
Bean ๋ฑ๋ก
@Configuration
public class WebClientConfig {
@Bean
public WebClient webClient() {
return WebClient.builder()
.baseUrl("http://api.example.com")
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.build();
}
}
์์ฒญ ์์
@Service
public class ExampleService {
private final WebClient webClient;
public Mono<UserResponse> getUser(String userId) {
return webClient.get()
.uri("/users/{id}", userId)
.retrieve()
.bodyToMono(UserResponse.class);
}
}
๋ง์ฝ ํ์ต์ด ํ์ํ๋ค๋ฉด ์๋์ ๊ณต์ ๋ ํผ๋ฐ์ค๋ฅผ ํ๊ธ๋ก ๋ฒ์ญํ ๋ฌธ์๋ฅผ ํ์ธํ๋ฉด ์ข์ ๊ฒ ๊ฐ๋ค.
3. OpenFeign (FeignClient)
Spring Cloud OpenFeign์ Netflix์์ ๊ฐ๋ฐํ ์ ์ธ์ REST ํด๋ผ์ด์ธํธ๋ก, org.springframework.cloud
ํจํค์ง์ ํฌํจ์ด ๋์ด์๋ค.
์ฃผ์ ํน์ง
์ ์ธ์ REST Client - ์ธํฐํ์ด์ค + ์ด๋ ธํ ์ด์ ์ผ๋ก ๊ตฌํ ๊ฐ๋ฅ
๋๊ธฐ์ HTTP ํต์ ๋ฐฉ์ (๋น๋๊ธฐ ์ฒ๋ฆฌ๋ ๊ฐ๋ฅ)
๊ธฐ๋ณธ์ ์ธ ์ฌ์ฉ๋ฒ
@FeignClient(name = "book-service", url = "${api.book.url}")
public interface BookClient {
@GetMapping("/api/books")
List<Book> searchBooks(
@RequestParam String query,
@RequestHeader(HttpHeaders.AUTHORIZATION) String authorization
);
}
์์ฒ๋ผ Spring MVC์ ์ด๋ ธํ ์ด์ ๋ฐฉ์์ผ๋ก ๋์ผํ๊ฒ ์ฌ์ฉ์ด ๊ฐ๋ฅํฉ๋๋ค.
๋ง์ฝ, RestTemplate ์ ์ฌ์ฉํ๋ค ํ๋ค๋ฉด ์๋์ ๊ฐ์ ์ฝ๋ ๊ตฌ์กฐ๋ก ํธ์ถ์ ํด์ผํ๋ค.
@Component
@RequiredArgsConstructor
public class BookRestTemplate {
private final RestTemplate restTemplate;
private final String apiUrl;
private final String apiKey;
public List<Book> search(String query) {
String url = createSearchUrl(query);
HttpHeaders headers = createHeaders();
HttpEntity<?> entity = new HttpEntity<>(headers);
ResponseEntity<List<Book>> response = restTemplate.exchange(
url,
HttpMethod.GET,
entity,
new ParameterizedTypeReference<List<Book>>() {}
);
return response.getBody();
}
private String createSearchUrl(String query) {
return UriComponentsBuilder.fromUriString(apiUrl)
.path("/api/books")
.queryParam("query", query)
.encode()
.build()
.toUriString();
}
private HttpHeaders createHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.set(HttpHeaders.AUTHORIZATION, "KEY " + apiKey);
return headers;
}
}
ํน์ง
์ง๊ด์ ์ธ ์ธํฐํ์ด์ค ๊ธฐ๋ฐ์ผ๋ก ์ค๊ณ๊ฐ ๊ฐ๋ฅํ๋ฉฐ, ๋ณด์ผ๋ฌํ๋ ์ดํธ ์ฝ๋๊ฐ ์ต์ํ ๋ฉ๋๋ค.
์ฝ๋์ ๊ฐ๋ ์ฑ ์ธก๋ฉด์์๋ ๋ฐ์ด๋๋ค.
ํ์ฅ์ฑ์ ๊ณ ๋ คํ์ ๋ Retry, Circuit Breaker ๋ฑ์ ๋์ ํ๊ธฐ ํธํ๋ค.
ํ์ง๋ง, ์ด๋ ธํ ์ด์ ๊ณผ ์ธํฐํ์ด์ค ๊ธฐ๋ฐ์ผ๋ก ํ ์คํธ๋ฅผ ํตํด ํ์ธํด์ผํ์ง๋ง ํ ์คํธ ๋๊ตฌ๋ฅผ ์ง์ํ์ง ์๋๋ค๋ ๋จ์ ์ด ์กด์ฌํ๋ค.
์ฝ์ด๋ณผ๊ฑฐ๋ฆฌ
Last updated
Was this helpful?