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?