[Java] Spring Boot에서 HTTP 요청 보내기

이광훈·2023년 7월 6일
7

💡 RestTemplate vs WebClient

🔸 기존에 많이 사용한 RestTemplate과 WebClient의 가장 큰 차이점은 RestTemplateBlocking이고, WebClientNon-Blocking 방식이라는 것

  • Blocking은 호출되는 함수가 호출하는 함수에게 제어권을 넘기지 않고 대기하게 함
  • Non-Blocking은 제어권을 바로 넘김

현재 RestTemplate는 Spring 5.0 이후부터 Deprecated되었고, WebClient는 앞서 설명하였듯 블로킹되지 않아 성능이 더 좋기 때문에 WebClient를 사용하는 것이 권장됨

✅ RestTemplate

: Spring에서 제공하는 HTTP Client로 REST API를 호출하기 위한 함수를 제공하는 클래스

🔸 특징

  • HTTP 요청 후 JSON, xml, String과 같은 응답을 받을 수 있는 템플릿
  • RESTful 형식을 지원
  • 멀티 스레드Blocking 방식 사용
  • Blocking I/O 기반의 *동기 방식 API

💡 동기

동기는 데이터 요청의 결과를 기다림 👉 요청과 결과가 한 자리에서 동시에 일어남

ex)
카페에 방문해 커피를 주문할 때, 직원이 한 명 뿐이라면
→ 직원에게 커피를 주문
→ 주문을 받은 직원은 즉시 커피를 제조
→ 제조된 커피 수령

이러한 프로세스가 진행되고 이처럼 요청과 결과가 동시에 일어나는 것동기라고 함

💡 비동기

비동기는 데이터를 요청한 후 결과를 기다리지 않음 👉 요청과 결과가 동시에 일어나지 않음

ex)
카페에 방문해 커피를 주문할 때, 주문을 받는 직원과 커피를 내리는 직원이 있다면
→ 주문을 받는 직원에게 커피를 주문
→ 주문을 받은 직원은 커피를 내리는 직원에게 주문 전달
→ 이후 새로운 주문을 받음

이처럼 요청과 결과가 동시에 일어나지 않는 것비동기라고 합니다.

🔸 메소드 종류

  • getForObject() : 주어진 URL 주소로 HTTP GET 메소드로 객체 결과를 반환받음
  • getForEntity() : 주어진 URL 주소로 HTTP GET 메소드로 ResponseEntity로 결과를 반환받음
  • postForLocation() : POST 요청을 보내고 결과로 헤더에 저장된 URI를 결과로 반환받음
  • postForObject() : POST 요청을 보내고 객체로 결과를 반환받음
  • postForEntity() : POST 요청을 보내고 결과로 ResponseEntity로 반환받음
  • delete() : 주어진 URL 주소로 HTTP DELETE 메소드를 실행
  • headForHeaders() : 헤더의 모든 정보를 얻을 수 있으며 HTTP HEAD 메소드를 사용
  • put() : 주어진 URL 주소로 HTTP PUT 메소드를 실행
  • patchForObject() : 주어진 URL 주소로 HTTP PATCH 메소드를 실행
  • optionsForAllow() : 주어진 URL 주소에서 지원하는 HTTP 메소드를 조회
  • exchange() : HTTP 헤더를 새로 만들 수 있고 어떤 HTTP 메소드도 사용 가능
  • execute() : Request/Response 콜백을 수정할 수 있음

🌐 GET 요청 보내기

🔹 getForObject → 응답 body만 있으면 될 때

public void getBeerObject() {
	RestTemplate restTemplate = new RestTemplate();
    // 랜덤으로 세계 맥주에 대한 정보를 주는 url
    String url = "https://random-data-api.com/api/v2/beers";
    
    BeerGetDto responseBody = restTemplate.getForObject(url, BeerGetDto.class);
    log.info(responseBody.toString());
}

👉 getForObject는 응답을 지정한 타입으로 변환해서 받아줌

🔹 getForEntity → 응답을 좀더 상세하게 받을 때 사용

public void getBeerEntity() {
	RestTemplate restTemplate = new RestTemplate();
    // 랜덤으로 세계 맥주에 대한 정보를 주는 url
    String url = "https://random-data-api.com/api/v2/beers";
    
    ResponseEntity<BeerGetDto> response =
    		restTemplate.getForEntity(url, BeerGetDto.class);
    log.info(response.getStatusCode().toString());
    log.info(response.getHeaders().toString());
}

🌐 POST 요청 보내기

🔹 postForObject → Request Body를 DTO의 형태로 포함해서 요청

public void postBeerObject() {
	RestTemplate restTemplate = new RestTemplate();
    // 랜덤으로 세계 맥주에 대한 정보를 주는 url
    String url = "https://random-data-api.com/api/v2/beers";
    
    BeerPostDto dto = new BeerPostDto();
    dto.setName("Stella Artois");
    dto.setCc(2000L);
    dto.setAlcohol(5.0);
    
    // POST 요청 보낼 때 requestBody를 같이 전달
    MessageDto responseBody = restTemplate.postForObject(
    		url,	// 요청 URL
            dto,	// request Body
            MessageDto.class	// 응답 해석 타입
    );
    log.info(responseBody.toString());
}

🔺 응답 Body가 없을 경우, Void 클래스를 활용

 url = "http://localhost:8081/give-me-beer-204";
        ResponseEntity<Void> response = restTemplate.postForEntity(
                url,
                dto,
                Void.class  // void 의 클래스 (객체화 불가)
        );
        log.info(response.getStatusCode().toString());     

🔹 exchange() 메소드

  • 요청을 보내면서 즉각적으로 메소드를 설정해서 요청을 보냄
  • 좀 더 일반적인 HTTP 요청을 구성할 수 있음
restTemplate.exchange(
		url,
        HttpMethod.Post,
        new HttpEntity<>(dto),
        Void.class
);

✅ WebClient

: Blocking 동기방식인 RestTemplate와 달리 Non-Blocking 방식
→ 현재는 Spring에서 WebClient의 사용을 강력히 권고함

🔸 특징

  • 싱글 스레드(코어당 1개의 스레드)와 Non-Blocking 방식 사용
  • 이벤트에 반응형(Reactive)으로 동작(Spring React 프레임워크 사용)
  • RestTemplate와 같이 HTTP 요청 후 응답받을 수 있는 템플릿 역할
  • Reactor 기반의 Functional API

💡 반응형 웹 관련 은존성 추가

implementation 'org.springframework.boot:spring-boot-starter-webflux'

🌐 GET 요청 보내기

🔹get()

@Service
@Slf4j
public class BeerClientService {
	public void getBeer() {
    	// WebClient는 Builder 패턴 처럼 사용
    	WebClient webClient = WebClient.builder().build();
        String url = "https://random-data-api.com/api/v2/beers";
        
        // 어떤 HTTP 메소드로 요청 보낼지를 get(), post() 메소드 등으로 결정
        // 만일 다른 메소드를 쓰고 싶다면, method()
        String response = webClient.get() // webClient.method(HttpMethod.GET)
        		.uri(url)	// 요청 경로 설정
                .header("x-test", "header") // 요청 헤더 추가
                // body도 메소드에 따라 추가
                .retrieve()	// 여기 전까지가 요청을 정의 한 부분
                // 여기부터 정의하는건 응답을 어떻게 처리할 것인지
                .bodyToMono(String.class)	// 응답이 한번 돌아오고, 응답의 body를 String으로 해석
                .block();	// 동기식으로 처리
       	log.info(response);
   	}
}

🔹post()

	public void postBeer() {
    	WebClient webClient = WebClient.builder().build();
        String url = "https://random-data-api.com/api/v2/beers";
        
        BeerPostDto dto = new BeerPostDto();
        // POST 요청
        MessageDto responseBody = webClient.post()
        		.uri(url)	// url 정의
                .bodyValue(dto)	// requestBody 정의
                .retrieve()	// 응답 정의 시작
                .bodyTOMono(MessageDto.class)	// 응답 데이터 정의
                .block();	// 동기식 처리
                
       	log.info(responseBody.toString());
  	}

👉 .retrieve() 호출 이전까지 요청을 정의하고, 이후부터는 응답을 어떻게 처리할지 결정

profile
웃으며 일할 때, 시너지가 배가 된다고 믿는 개발자

1개의 댓글

comment-user-thumbnail
2024년 3월 15일

잘읽고갑니다

답글 달기