본문 바로가기
웹개발/스프링

스프링 RestTemplate으로 HTTP Get 방식 요청하기

by 인생여희 2020. 12. 6.

 

스프링 RestTemplate으로 HTTP Get 방식 요청하기

스프링에는 HTTP 통신을 할 수 있는 여러가지 모듈이 있는데, 그중에 하나가 RestTemplate 이다. RestTemplate org.springframework.web.client 패키지에 존재한다. 이 RestTemplate 클래스를 사용하면 HTTP 요청과 응답이 간단해진다. 

 

Get 방식으로 요청 1 - 컨트롤러 

RestTemplate을 이용해서 HTTP GET 방식으로 요청하는 로직은 아래와 같다. 먼저 RequestMapping 어노테이션으로 /rest/resttwo 로 요청이 들어오는 Post 방식의 url을 처리하겠다고 작성해준다. 그리고 Post 방식으로 요청할때 넘겨주는 json 형식의 파리미터를 RequestBody 어노테이션을 사용해서 HashMap을 이용해서 받아준다. 이때 RequestBody 어노테이션을 사용해서 json형식의 데이터를 자바의 Map형식으로 받으려면 특정 라이브러리를 pom.xml에 작성해주어야 한다. 그 다음 RestTemplate 객체로 Get 방식으로 요청할 때 넣어줄 파라미터를 Map 형식으로 담아준다. 그 후 RestTemplate 객체를 생성하고 getForObject 메소드를 호출할 때 인자 값으로 요청 URL과 위에서 만든 Map 형식의 파라미터를 넣어준다. 예외 발생 처리를 해주고 마지막으로 응답값을 Json 형식으로 클라이언트로 던져주면 종료가 된다.

	@RequestMapping(value = "/rest/testtwo", method = RequestMethod.POST , produces="application/json;charset=utf-8")
	@ResponseBody
	public String home2(@RequestBody HashMap<String,String> paramss) {

		//파라미터 받기
		String id = (String)paramss.get("id");
		
		//url 메소드 파라미터 셋팅
		Map<String, Object> params = new HashMap<>();
		params.put("storesId", id);

		//RestTemplate 객체 생성
		RestTemplate template = new RestTemplate();
		String response = "";
		
		try {

			 response = 
					template.getForObject("https://jsonplaceholder.typicode.com/todos/{storesId}",String.class, params);

		} catch (HttpStatusCodeException e) {
			if (e.getStatusCode() == HttpStatus.NOT_FOUND) {

//				HttpStatusCodeException 
//				HttpClientErrorException 
//				HttpServerErrorException

			}

		}
		return response.toString();
	}
    
    
    /*
    
    <!-- json parser 라이브러리 추가-->
	<dependency>
		<groupId>com.googlecode.json-simple</groupId>
		<artifactId>json-simple</artifactId>
		<version>1.1</version>
	</dependency>


	<!-- jackson-databind 라이브러리 추가 -->
	<dependency>
		<groupId>com.fasterxml.jackson.core</groupId>
		<artifactId>jackson-databind</artifactId>
		<version>2.9.6</version>
	</dependency>

	<!-- jackson 라이브러리 추가 -->
	<dependency>
		<groupId>org.codehaus.jackson</groupId>
		<artifactId>jackson-mapper-asl</artifactId>
		<version>1.4.2</version>
	</dependency>	
		   
    */

 

Get 방식으로 요청 1 - 클라이언트

클라이언트 쪽 소스는 아래와 같다. test2 버튼을 눌렀을 때 ajax를 이용해서 post 방식으로 위에서 만든 /rest/testtwo 컨트롤러를 호출해준다. 응답이 성공적으로 왔다면 응답데이터를 id가 result인 div에 뿌려준다.

<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<html>
<head>
 	<meta charset="UTF-8">
	<title>Spring Rest Template 예</title>
</head>
<body>
<input type="button" id="test1" onclick="test2();" value="test2 버튼 " /> 
<br>

<div id = "result"></div>

<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script>

function test2() {
	
var param = {"id" : "1"}
	
    $.ajax({
        url: "/rest/testtwo",
        type:"post",
        data: JSON.stringify(param), 
        dataType: "json", 
        contentType: "application/json; charset=UTF-8", //반드시 써주어야 한다.
        success: function(data) {
            console.log("통신성공");
            
            console.log(data);
            document.getElementById('result').innerText = JSON.stringify(data, null, 3);
        
        },
        error: function(request, status) {
        	console.log(request);
        	
        	
        }
    })
	
}

//http://javakorean.com/%EC%8A%A4%ED%94%84%EB%A7%81-ajax-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0-%EB%B0%9B%EA%B8%B0/
//https://victorydntmd.tistory.com/172
</script>

</body>
</html>

 

아래와 같이 응답이 온 것을 알 수가 있다.

 

{
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
}

 


 

Get 방식으로 요청 2 - 컨트롤러

이번에는 restTemplate을 이용해서 Get 방식으로 요청할 때 HTTP 커넥션 풀 설정을 하는 법을 살펴본다. 먼저 메이븐 리파짓토리에서 apache httpClient 라이브러리를 받아서 pom.xml에 의존성 주입을 시켜준다. 그리고 HttpComponentsClientHttpRequestFactory 객체를 만들어서 readTimeout 설정과 connectTimeout 설저을 해준다음 HttpClient 객체를 만들어서 커넥션 풀 설정을 해준다. 그 후 HttpComponentsClientHttpRequestFactory 객체에 httpClient 객체를 주입시켜준다. 그 후 위와 동일하게 호출 해주면 된다. 참고로 클라이언트에서 요청할때 스프링 컨트롤러에 @RequestBody가 붙어있으면 클라이언트에서 Get방식으로 요청할 수 없다. 오류가 난다. Get방식으로 요청했을 때 "요청 타겟에서 유효하지 않은 문자가 발견되었습니다. 유효한 문자들은 RFC 7230과 RFC 3986에 정의되어 있습니다." 라는 오류를 발생시킨다. 

@RequestMapping(value = "/rest/testone", method = RequestMethod.POST,produces="application/json;charset=utf-8")
	@ResponseBody
	public String home(@RequestBody HashMap<String,String> params) {
		
		String id = (String)params.get("id");
		
		HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
		factory.setReadTimeout(5000);    										// 읽기시간초과, ms
		factory.setConnectTimeout(3000); 										// 연결시간초과, ms
		HttpClient httpClient = HttpClientBuilder.create().setMaxConnTotal(100) // connection pool 적용
				.setMaxConnPerRoute(5)  										// connection pool 적용
				.build();

		factory.setHttpClient(httpClient); 										// 동기실행에 사용될 HttpClient 세팅
		RestTemplate restTemplate = new RestTemplate(factory);

String url = 
"http://www.kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json?key=430156241533f1d058c603178cc3ca0e&targetDt=" + id; // 예제니까 애초에 때려박음..

		//요청 후 응답
		String response = restTemplate.getForObject(url, String.class);

		return response.toString();
	}

 

Get 방식으로 요청 2 - 클라이언트

클라이언트는 아래와 같다. 

<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<html>
<head>
 	<meta charset="UTF-8">
	<title>Spring Rest Template 예</title>
</head>
<body>

<input type="button" id="test1" onclick="test1();" value="test1 버튼" /> 

<br>

<div id = "result"></div>


<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script>
   

function test1() {
	
	var param = {"id" : "20201110"}
	
    $.ajax({
        url: "/rest/testone",
        type:"post",
        data: JSON.stringify(param), //json형식의 문자열 타입으로 설정
        dataType: "json", 
        contentType: "application/json; charset=UTF-8", //반드시 작성
        success: function(data) {
            console.log("통신성공");
            
            console.log(data);
            document.getElementById('result').innerText = JSON.stringify(data, null, 3);
        
        },
        error: function(request, status) {
        	console.log(request);
        	
        }
    })
	
}
</script>

</body>
</html>

 


 

Get 방식으로 요청 3 - RestTemplate Bean 생성

마지막으로 RestTemplate객체를 빈으로 만들어서 서버가 시작될때 읽어서 스프링 컨테이너에 주입시켜놓는 방식으로 사용해보자. RestTemplate은 요청할 때마다 소켓을 열고, 접속을 하는 과정을 거치기 때문에 자원비용이 많이 발생한다. 그래서 datasource connection pool 방식 처럼 미리 만들어서 컨테이너에 주입해 놓고 사용해야 자원낭비를 줄일 수 있다.

package com.spring.resttemplate;

import java.io.IOException;
import java.util.Collections;

import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.BufferingClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

@Configuration
public class RestTemplateConfig {

	
	public RestTemplateConfig() {
		System.err.println("RestTemplateConfig 초기화");
	}
	
	
	@Bean
	public RestTemplate restTemplate() {
	
		
		System.err.println("restTemplate bean");
		
		/*
		 사용중 -  서버에서 응답 늦게 줄때 -> Thread 급증 -> 문제 차단( Connection 수 제한)
		 */
		
	HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
	
	HttpClient client = HttpClientBuilder.create()
			.setMaxConnTotal(50)
			.setMaxConnPerRoute(20)
			.build();
	
	System.err.println("client : " + client);
	
			
			factory.setConnectTimeout(5000);
			factory.setReadTimeout(5000);
			factory.setHttpClient(client);
			//https://stackoverflow.com/questions/49744638/java-lang-unsupportedoperationexception-when-set-connection-timeout-to-httpcompo
			
			System.err.println("factory : " + factory);
			
			
			//RestTemplate restTemplate = new RestTemplate(factory);
			RestTemplate restTemplate = new RestTemplate(new BufferingClientHttpRequestFactory(factory));
			restTemplate.setInterceptors(Collections.singletonList(new RequestResponseLoggingInterceptor()));
			
			System.err.println("객체 : " + restTemplate);
		
		return restTemplate;
		
	}
	
}

//로그 출력
class RequestResponseLoggingInterceptor implements ClientHttpRequestInterceptor{

	@Override
	public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
			throws IOException {
		
		// 전
		
		System.out.println("url 호출 전 ");
		
		ClientHttpResponse response = execution.execute(request, body);
		
		// 후
		System.out.println("url 호출 후 ");
		return response;
	}
	
	
	
	
}

 

Get 방식으로 요청 3- 컨트롤러

RestTemplate을 Bean으로 만들어서 주입을 했으면 이제 컨트롤러로 가서 위에서 만든 Bean을 사용하겠다고 어노테이션으로 선언해준다. 그리고 컨트롤러를 작성하고 위에서 주입해 놓은 RestTemplate사용해서 Get 방식으로 호출하는 방법은 위에서 작성한 컨트롤러와 동일하다.

package com.spring.resttemplate;
import java.util.HashMap;
import java.util.Map;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.RestTemplate;

@Controller
public class RestTemplateController {

	@Autowired
	private RestTemplate restTemplate;
	
	
	@RequestMapping(value = "/rest/testthree", method = RequestMethod.POST, produces="application/json;charset=utf-8")
	public String home3(@RequestBody HashMap<String,String> paramss) {

		String id = (String)paramss.get("id");
		
		Map<String, Object> params = new HashMap<>();
		params.put("storesId", id);

		String response = "";
		try {

			 response = 
					 restTemplate.getForObject("https://jsonplaceholder.typicode.com/todos/{storesId}",String.class, params);

		} catch (HttpStatusCodeException e) {
			if (e.getStatusCode() == HttpStatus.NOT_FOUND) {

			}

		}
		return response.toString();
	}
	
}

 

Get 방식으로 요청 3- 클라이언트

클라이언트도 요청 1, 2 방식에서 작성했던 것과 동일하다.

<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<html>
<head>
 	<meta charset="UTF-8">
	<title>Spring Rest Template 예</title>
</head>
<body>

<input type="button" id="test3" onclick="test2();" value="test3 버튼" /> 

<div id = "result"></div>

<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script>
   
function test3() {
	
	var param = {"id" : "2"}
	
    $.ajax({
        url: "/rest/testthree",
        type:"post",
        data: JSON.stringify(param), 
        dataType: "json", 
        contentType: "application/json; charset=UTF-8", 
        success: function(data) {
            console.log("통신성공");
            
            console.log(data);
            document.getElementById('result').innerText = JSON.stringify(data, null, 3);
        
        },
        error: function(request, status) {
        	console.log(request);
        	
        	
        }
    })
}
</script>
</body>
</html>

 

테스트

서버를 작동시켜서 위에서 만든 컨트롤러를 화면에서 호출해보자. 버튼을 하나씩 누르면 서버에서 응답받은 데이터가 화면에 JSON형식으로 뿌려진다.

json 방식으로 응답

 

 

예제파일

rest.jsp
0.00MB
RestTemplateConfig.java
0.00MB
RestTemplateController.java
0.00MB

 

참고 

https://zepinos.tistory.com/34

http://javakorean.com/%EC%8A%A4%ED%94%84%EB%A7%81-ajax-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0-%EB%B0%9B%EA%B8%B0/

https://victorydntmd.tistory.com/172