서버투서버 통신을 진행할때 응답이 JSON형태이면서 내부 필드가 동적으로 변경되는 경우가 많을때 JSON내부의 값을 동적으로 선택하기 위해 사용한다.

예시코드

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.*;
import org.springframework.web.client.RestTemplate;

public class Example {
    private final RestTemplate restTemplate;
    private final ObjectMapper objectMapper;

    public Example(RestTemplate restTemplate, ObjectMapper objectMapper) {
        this.restTemplate = restTemplate;
        this.objectMapper = objectMapper;
    }

    public void call() throws Exception {
        String url = "<https://api.example.com/users/123>";

        HttpHeaders headers = new HttpHeaders();
        headers.setAccept(java.util.List.of(MediaType.APPLICATION_JSON));
        HttpEntity<Void> entity = new HttpEntity<>(headers);

        ResponseEntity<String> res = restTemplate.exchange(
            url,
            HttpMethod.GET,
            entity,
            String.class
        );

        // 상태코드 체크는 상황에 맞게 처리
        if (!res.getStatusCode().is2xxSuccessful() || res.getBody() == null) {
            throw new IllegalStateException("API call failed: " + res.getStatusCode());
        }

        JsonNode root = objectMapper.readTree(res.getBody());

        // 안전한 탐색: path()는 없으면 MissingNode를 반환(NullPointerException 회피)
        String name = root.path("data").path("user").path("name").asText(null);
        Integer age = root.path("data").path("user").path("age").isMissingNode()
                ? null
                : root.path("data").path("user").path("age").asInt();

        System.out.println("name=" + name + ", age=" + age);
    }
}

지피티가 권장하는 패턴은 아래와 같다.

이론적으로 ResponseEntity<JsonNode> 같은 형태로 “바로” 받게 만들 수도 있지만, RestTemplate의 메시지 컨버터 구성(예: Jackson converter)과 미디어타입/제네릭 처리에 따라 예상치 못한 이슈가 나기 쉬움.

실무에서 안정적으로 가려면 String으로 받은 뒤 readTree로 파싱하는 방식이 가장 단순하고 디버깅도 쉬움(원문 JSON 로깅 가능).

이를 이용해 이런 헬퍼 펑션을 제공할 수도 있음.

	private Optional<JsonNode> getBodyIfHttpStatusOk(ResponseEntity<JsonNode> responseEntity) {
		if (responseEntity.getStatusCode() != HttpStatus.OK)
			throw new IllegalStateException("statusCode [" + responseEntity.getStatusCode() + "]");

		if (responseEntity.getBody().get("exceptionCode") != null)
			return Optional.empty();

		return Optional.of(responseEntity.getBody());
	}