RestTemplate 기초

StrayCat·2026년 2월 22일

[Spring Boot] RestTemplate이란?

우리 서버가 다른 서버에 HTTP 요청을 보내야 할 때 사용하는 Spring의 도구, RestTemplate을 알아봅니다.


📌 왜 RestTemplate이 필요한가?

지금까지는 브라우저(Client)로부터 요청을 받는 서버 입장에서만 개발을 해왔습니다.
하지만 실제 서비스를 개발하다 보면, 우리 서버가 직접 다른 서버에 요청을 보내야 하는 상황이 생깁니다.

대표적인 예시:

  • 회원가입 시 카카오 주소 검색 API 호출
  • 소셜 로그인 시 카카오/구글 OAuth API 호출
  • 외부 날씨 API, 결제 API 연동

이처럼 우리 서버가 클라이언트 역할을 하게 되는 상황에서,
Spring은 서버 간 HTTP 통신을 간편하게 처리할 수 있도록 RestTemplate을 제공합니다.


📌 RestTemplate이란?

Spring에서 제공하는 서버 → 서버 HTTP 통신 클라이언트입니다.

RestTemplate을 사용하면 다음과 같은 작업을 간편하게 처리할 수 있습니다.

  • GET / POST / PUT / DELETE 등 HTTP 메서드로 외부 서버에 요청
  • 응답 JSON을 자동으로 Java 객체로 역직렬화 (JSON to Object)
  • 요청 Header, Body, Query Parameter 설정

💡 JSON → Java 객체 변환을 직접 구현할 필요 없이 RestTemplate이 자동으로 처리해줍니다.


🏗️ 환경 구성

이번 예제에서는 두 개의 Spring Boot 프로젝트를 동시에 실행하여 서버 간 통신을 실습합니다.

역할프로젝트명포트
요청을 보내는 쪽spring-resttemplate-client8080
요청을 받는 쪽spring-resttemplate-server7070

브라우저에서 Client 서버(8080) 에 요청하면, Client 서버가 내부적으로 Server 서버(7070) 에 RestTemplate으로 요청을 보내는 구조입니다.

브라우저 → (8080) Client 서버 → RestTemplate → (7070) Server 서버

🛠️ RestTemplate 생성 방법

RestTemplateBuilder를 주입받아 build()로 생성하는 것이 권장 방법입니다.

@Slf4j
@Service
public class RestTemplateService {

    private final RestTemplate restTemplate;

    // RestTemplateBuilder를 주입받아 RestTemplate 생성
    public RestTemplateService(RestTemplateBuilder builder) {
        this.restTemplate = builder.build();
    }
}

new RestTemplate()으로 직접 생성하는 방법도 있지만,
RestTemplateBuilder를 사용하면 타임아웃, 인터셉터 등 설정을 유연하게 적용할 수 있어 더 권장됩니다.


📬 GET 요청 - getForEntity()

UriComponentsBuilder로 URI 만들기

RestTemplate으로 요청할 URL은 문자열을 직접 조합하지 않고,
UriComponentsBuilder를 사용하면 가독성 있고 안전하게 URI를 구성할 수 있습니다.

// Query String 방식: http://localhost:7070/api/server/get-call-obj?query=Mac
URI uri = UriComponentsBuilder
    .fromUriString("http://localhost:7070")   // 요청할 서버 주소
    .path("/api/server/get-call-obj")         // 요청 경로
    .queryParam("query", query)               // Query Parameter 추가
    .encode()                                 // 인코딩 처리
    .build()
    .toUri();

단일 객체 반환 (GET)

public ItemDto getCallObject(String query) {
    URI uri = UriComponentsBuilder
        .fromUriString("http://localhost:7070")
        .path("/api/server/get-call-obj")
        .queryParam("query", query)
        .encode().build().toUri();

    // getForEntity: GET 요청 후 응답을 지정한 클래스 타입으로 자동 변환
    ResponseEntity responseEntity = restTemplate.getForEntity(uri, ItemDto.class);
    
    log.info("statusCode = " + responseEntity.getStatusCode());
    return responseEntity.getBody(); // ItemDto 객체로 자동 변환된 응답 반환
}

getForEntity(uri, 타입.class) 파라미터 설명:

  • 첫 번째: 요청할 URI
  • 두 번째: 응답 JSON을 매핑할 클래스 타입 → 자동으로 Object 변환

리스트 반환 (GET) - JSON 직접 파싱

응답이 여러 객체를 담은 JSON 배열일 경우, String으로 먼저 받아온 뒤 직접 파싱합니다.

// build.gradle - JSON 파싱 라이브러리 추가
implementation 'org.json:json:20230227'
public List getCallList() {
    URI uri = UriComponentsBuilder
        .fromUriString("http://localhost:7070")
        .path("/api/server/get-call-list")
        .encode().build().toUri();

    // 다중 JSON은 일단 String으로 받아옴
    ResponseEntity responseEntity = restTemplate.getForEntity(uri, String.class);

    return fromJSONtoItems(responseEntity.getBody());
}

// JSON String → List 변환 헬퍼 메서드
private List fromJSONtoItems(String responseBody) {
    JSONObject jsonObject = new JSONObject(responseBody);     // String → JSONObject
    JSONArray items = jsonObject.getJSONArray("items");       // "items" 배열 추출

    List itemDtoList = new ArrayList<>();
    for (Object item : items) {
        itemDtoList.add(new ItemDto((JSONObject) item));      // 각 JSON → ItemDto 변환
    }
    return itemDtoList;
}

서버에서 내려주는 JSON 형태는 다음과 같습니다.

{
  "items": [
    {"title": "Mac",     "price": 3888000},
    {"title": "iPad",    "price": 1230000},
    {"title": "iPhone",  "price": 1550000}
  ]
}

ItemDtoJSONObject를 받는 생성자를 추가하면 편리하게 변환할 수 있습니다.

@Getter
@NoArgsConstructor
public class ItemDto {
    private String title;
    private int price;

    // JSONObject → ItemDto 변환 생성자
    public ItemDto(JSONObject itemJson) {
        this.title = itemJson.getString("title");
        this.price = itemJson.getInt("price");
    }
}

📮 POST 요청 - postForEntity()

public ItemDto postCall(String query) {
    // Path Variable 방식: /api/server/post-call/{query}
    URI uri = UriComponentsBuilder
        .fromUriString("http://localhost:7070")
        .path("/api/server/post-call/{query}")
        .encode().build()
        .expand(query)  // {query} 자리에 실제 값 삽입
        .toUri();

    User user = new User("Jason", "1234");  // HTTP Body에 담을 객체

    // postForEntity: POST 요청 (uri, body 객체, 응답 타입)
    // Java 객체는 자동으로 JSON으로 직렬화되어 Body에 담김
    ResponseEntity responseEntity = restTemplate.postForEntity(uri, user, ItemDto.class);

    return responseEntity.getBody();
}

postForEntity(uri, body, 타입.class) 파라미터 설명:

  • 첫 번째: 요청할 URI
  • 두 번째: HTTP Body에 담을 Java 객체 → 자동으로 JSON으로 직렬화
  • 세 번째: 응답을 매핑할 클래스 타입

expand(query){query} 같은 Path Variable 자리에 값을 동적으로 삽입할 때 사용합니다.


🔄 exchange() - Header까지 제어하기

GET/POST에서는 Header를 별도로 설정하기 어렵습니다.
exchange()를 사용하면 URI, Header, Body를 한 번에 설정하여 요청을 보낼 수 있습니다.

public List exchangeCall(String token) {
    URI uri = UriComponentsBuilder
        .fromUriString("http://localhost:7070")
        .path("/api/server/exchange-call")
        .encode().build().toUri();

    User user = new User("Jason", "1234");

    // RequestEntity로 URI + Header + Body를 한 번에 구성
    RequestEntity requestEntity = RequestEntity
        .post(uri)
        .header("X-Authorization", token)  // 커스텀 Header 추가
        .body(user);

    // exchange: RequestEntity를 통해 요청, 응답을 String으로 받아 직접 파싱
    ResponseEntity responseEntity = restTemplate.exchange(requestEntity, String.class);

    return fromJSONtoItems(responseEntity.getBody());
}

📊 메서드 비교 정리

메서드HTTP MethodBody 설정Header 설정반환 타입
getForEntity()GETResponseEntity<T>
postForEntity()POSTResponseEntity<T>
exchange()모두 가능ResponseEntity<T>

💡 추가로 알아두면 좋은 것

RestTemplate의 현재 위치

RestTemplate은 Spring 5.0부터 유지 보수 모드(Maintenance Mode) 로 전환되었고, 공식적으로는 WebClient 사용을 권장합니다.

RestTemplateWebClient
방식동기(Synchronous) - 응답 받을 때까지 블로킹비동기(Asynchronous) - 논블로킹 지원
상태유지 보수 모드 (Deprecated 예정)현재 권장
학습 곡선낮음 (직관적)상대적으로 높음

실무에서는 WebClient를 많이 사용하지만, RestTemplate도 여전히 많은 레거시 코드에서 사용되고 있습니다.
개념은 동일하므로 RestTemplate을 이해하면 WebClient로 전환도 어렵지 않습니다.

UriComponentsBuilder를 쓰는 이유

URL을 문자열로 직접 조합하면 Query Parameter 인코딩 처리, 특수문자 이슈 등이 발생할 수 있습니다.
UriComponentsBuilder를 사용하면 이런 문제를 encode()로 자동 처리해줍니다.

// ❌ 직접 문자열 조합 - 인코딩 문제 발생 가능
String url = "http://localhost:7070/api/server?query=" + query;

// ✅ UriComponentsBuilder 사용 - encode() 자동 처리
URI uri = UriComponentsBuilder
    .fromUriString("http://localhost:7070")
    .path("/api/server")
    .queryParam("query", query)
    .encode()
    .build()
    .toUri();
profile
알면 좋은 것보단 잊어버리기 싫은 것들을 기록합니다.

0개의 댓글