HttpEntity

이진욱·2024년 12월 3일

이론

목록 보기
2/24

HttpEntity 는 Spring Framework에서 HTTP 요청 또는 응답을 표현하기 위한 객체입니다. 주로 요청 헤더, 본문(body), 상태 정보 등을 다루기 위해 사용됩니다. HttpEntity 클래스는 HTTP 메시지를 추상화하며, 제네릭 타입으로 본문 데이터의 타입을 정의할 수 있습니다.

  • 헤더(Header)
    HTTP 요청이나 응답에 포함될 헤더 정보를 설정할 수 있습니다.
    예: Content-Type, Authorization 등

  • 본문(Body)
    요청 또는 응답에 포함될 데이터를 설정할 수 있습니다.
    JSON, XML, String, 객체 등 다양한 형식의 데이터를 지원합니다.

  • Immutable(불변성)
    HttpEntity 객체는 기본적으로 불변(immutable)입니다. 생성 시 설정된 값을 변경할 수 없습니다.

주요 생성자

HttpEntity는 두 가지 주요 생성자를 제공합니다:

  • 기본 생성자

    HttpEntity() : 빈 헤더와 본문으로 객체를 생성합니다.

  • 헤더와 본문 지정

    HttpEntity(T body, HttpHeaders headers)
    본문과 헤더를 지정하여 객체를 생성합니다.

상속 클래스

HttpEntity는 요청/응답의 헤더와 본문을 캡슐화합니다.
RequestEntity와 ResponseEntity로 구체화되어 HTTP 요청 및 응답에 활용됩니다.
RestTemplate 또는 WebClient와 함께 사용하면 HTTP 클라이언트 통신을 간단하게 처리할 수 있습니다.

  • RequestEntity
    HTTP 요청을 표현하기 위해 사용되며, HTTP 메서드(GET, POST 등)와 URL을 추가로 지정할 수 있습니다.

  • ResponseEntity
    HTTP 응답을 표현하기 위해 사용되며, 상태 코드와 헤더, 본문을 포함합니다.

예제

요청에 사용


HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);

String jsonBody = "{\"name\":\"John\", \"age\":30}";
HttpEntity<String> requestEntity = new HttpEntity<>(jsonBody, headers);

ResponseEntity<String> response = restTemplate.exchange(
    "https://example.com/api",
    HttpMethod.POST,
    requestEntity,
    String.class
);
System.out.println(response.getBody());


응답에 사용



HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);

String responseBody = "{\"status\":\"success\"}";
ResponseEntity<String> responseEntity = new ResponseEntity<>(responseBody, headers, HttpStatus.OK);
return responseEntity;

RestTemplate, WebClient

Spring Framework에서 HTTP 요청/응답 처리를 위한 주요 클래스는 RestTemplateWebClient입니다. 두 클래스 모두 외부 API와의 통신을 쉽게 처리할 수 있도록 지원하지만, 사용 목적과 구조가 다릅니다.

1. RestTemplate
RestTemplate은 동기 방식의 HTTP 클라이언트입니다.

특징: 요청이 완료될 때까지 호출이 블로킹됩니다.
사용 사례: 간단한 HTTP 요청/응답 처리에 적합하며, 레거시 코드에서 많이 사용됩니다.
상태: Spring 5 이후에는 사용이 비추천(deprecated)되고, WebClient로 대체되는 추세입니다.

import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;

public class RestTemplateExample {
    public static void main(String[] args) {
        RestTemplate restTemplate = new RestTemplate();

        // GET 요청
        String url = "https://jsonplaceholder.typicode.com/posts/1";
        ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
        System.out.println("Response: " + response.getBody());

        // POST 요청
        String postUrl = "https://jsonplaceholder.typicode.com/posts";
        Post post = new Post("New Title", "This is the content.", 1);
        ResponseEntity<Post> postResponse = restTemplate.postForEntity(postUrl, post, Post.class);
        System.out.println("Created Post: " + postResponse.getBody());
    }

@Getter			//반드시 있어야 함 (Post 클래스 직렬화)
@Setter			//반드시 있어야 함 (Post 클래스 직렬화)
    static class Post {
        private String title;
        private String body;
        private int userId;

        // Constructors, getters, setters
        public Post(String title, String body, int userId) {
            this.title = title;
            this.body = body;
            this.userId = userId;
        }

public Post() {}		//반드시 기본 생성자가 있어야 함 (Post 클래스 직렬화)
        @Override
        public String toString() {
            return "Post{" +
                    "title='" + title + '\'' +
                    ", body='" + body + '\'' +
                    ", userId=" + userId +
                    '}';
        }
    }
}

2. WebClient
WebClient는 Spring 5부터 제공된 비동기 방식의 HTTP 클라이언트입니다.

특징: 비동기/논블로킹 방식으로 동작하며, Reactive Streams를 기반으로 동시성과 성능이 뛰어납니다.
사용 사례: 고성능이 요구되거나 많은 병렬 요청을 처리해야 할 때 적합합니다.
추천: 최신 Spring 프로젝트에서는 RestTemplate 대신 사용이 권장됩니다.

Post 객체를 JSON으로 변환하려면 Jackson 라이브러리가 필요합니다. 아래 의존성을 추가해야 한다.

implementation 'com.fasterxml.jackson.core:jackson-databind'

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

예제


import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

public class WebClientExample {
    public static void main(String[] args) {
        WebClient webClient = WebClient.create();

        // GET 요청
        String getUrl = "https://jsonplaceholder.typicode.com/posts/1";
        Mono<String> getResponse = webClient.get()
                .uri(getUrl)
                .retrieve()
                .bodyToMono(String.class);

        getResponse.subscribe(response -> System.out.println("GET Response: " + response));

        // POST 요청
        String postUrl = "https://jsonplaceholder.typicode.com/posts";
        Post post = new Post("Reactive Title", "This is reactive content.", 1);
        Mono<Post> postResponse = webClient.post()
                .uri(postUrl)
                .bodyValue(post)
                .retrieve()
                .bodyToMono(Post.class);

        postResponse.subscribe(createdPost -> System.out.println("Created Post: " + createdPost));
    }

@Getter //(Post 클래스 직렬화)
@Setter //(Post 클래스 직렬화)
    static class Post {
        private String title;
        private String body;
        private int userId;

        // Constructors, getters, setters
        public Post(String title, String body, int userId) {
            this.title = title;
            this.body = body;
            this.userId = userId;
        }

		public Post() {} //(Post 클래스 직렬화)

        @Override
        public String toString() {
            return "Post{" +
                    "title='" + title + '\'' +
                    ", body='" + body + '\'' +
                    ", userId=" + userId +
                    '}';
        }
    }
}


윗 코드를 적용하였지만 콘솔 결과창에는 아무 것도 출력이 되지 않았다

콘솔에 아무것도 출력되지 않는 이유는 WebClient의 비동기 처리 특성 때문일 가능성이 높습니다. WebClient는 비동기로 동작하므로 요청이 완료되기 전에 main 메서드가 종료될 수 있습니다.

이를 해결하려면 Mono 또는 Flux의 블로킹 방식을 사용하거나, 테스트 목적으로 적절한 동기화를 추가해야 합니다.

코드 수정

package com.cos.book.httptest;
import org.springframework.web.reactive.function.client.WebClient;

import lombok.Getter;
import lombok.Setter;
import reactor.core.publisher.Mono;

public class HttpTest {
    public static void main(String[] args) {
        WebClient webClient = WebClient.create();

        // GET 요청
        String getUrl = "https://jsonplaceholder.typicode.com/posts/1";
        Mono<String> getResponse = webClient.get()
                .uri(getUrl)
                .retrieve()
                .bodyToMono(String.class);

        getResponse.subscribe(response -> System.out.println("GET Response: " + response));

        // POST 요청
        String postUrl = "https://jsonplaceholder.typicode.com/posts";
        Post post = new Post("Reactive Title", "This is reactive content.", 1);
        Post postResponse = webClient.post()
                .uri(postUrl)
                .bodyValue(post)
                .retrieve()
                .bodyToMono(Post.class)
                .block();

    System.out.println("Created Post: " + postResponse);
    }

@Getter // (Post 클래스 직렬화)
@Setter//(Post 클래스 직렬화)
    static class Post {
        private String title;
        private String body;
        private int userId;

        // Constructors, getters, setters
        public Post(String title, String body, int userId) {
            this.title = title;
            this.body = body;
            this.userId = userId;
        }

		public Post() {} //(Post 클래스 직렬화)

        @Override
        public String toString() {
            return "Post{" +
                    "title='" + title + '\'' +
                    ", body='" + body + '\'' +
                    ", userId=" + userId +
                    '}';
        }
    }
}

수정 후 콘솔 창 결과가 출력되었음을 확인

추가 참고사항

  • 블로킹 사용 주의: 실제 비동기 환경에서는 block()을 사용하는 것이 권장되지 않습니다. 하지만, 간단한 테스트나 콘솔 애플리케이션에서는 유용합니다.

  • subscribe() 사용: subscribe()를 사용하고 비동기 처리를 유지하려면 별도의 Thread.sleep() 또는 다른 방법으로 비동기 작업이 완료될 시간을 확보해야 합니다.

getResponse.subscribe(response -> System.out.println("GET Response: " + response));
Thread.sleep(2000); // 테스트 목적으로 추가

WebClient : Mono VS Flux

  • Mono: 단일 값을 비동기로 처리할 때 사용합니다. (예: HTTP 응답 본문이 JSON 객체인 경우)
  • Flux: 다중 값을 비동기로 처리할 때 사용합니다. (예: HTTP 응답 본문이 JSON 배열이거나 스트리밍 데이터인 경우)
예시
  • Mono를 반환하는 경우
    • 단일 응답 데이터 처리:
WebClient client = WebClient.create("https://api.example.com");

Mono<ResponseDTO> response = client.get()
    .uri("/single-response")
    .retrieve()
    .bodyToMono(ResponseDTO.class);

response.subscribe(data -> {
    System.out.println("응답 데이터: " + data);
});
  • Flux를 반환하는 경우
    • 다중 응답 데이터 처리:
WebClient client = WebClient.create("https://api.example.com");

Flux<ItemDTO> response = client.get()
    .uri("/multi-response")
    .retrieve()
    .bodyToFlux(ItemDTO.class);

response.subscribe(item -> {
    System.out.println("응답 데이터: " + item);
});

적절한 타입 선택 기준

  • 단일 값: 응답 데이터가 단일 객체라면 Mono를 사용합니다.

  • 다중 값: 응답 데이터가 배열, 리스트, 또는 스트리밍 데이터라면 Flux를 사용합니다.

  • bodyToMono를 사용하면 단일 값을 처리하는 Mono를 반환합니다.

  • bodyToFlux를 사용하면 여러 값을 처리하는 Flux를 반환합니다.

  • 따라서 상황에 따라 Mono나 Flux를 선택해서 사용하면 됩니다.

profile
열심히 하는 신입 개발자

0개의 댓글