개발 기록/Java

[Spring Framework] RestTemplate을 이용한 서버간 API 통신과 예외처리

JasonM 2023. 6. 8. 16:23
반응형

배경

java - 서버 대 서버 Rest API 호출

 
위의 흐름도와 같이 Application 2는 순수 REST 서비스만 제공하고 있는 상태이며, 브라우저에서 Application 2의 endpoint를 직접 엑세스를 시도하면 CORS 정책 위반 에러가 발생한다. (CORS란 Cross-Origin Resource Sharing의 약자로, 브라우저에서 다른 출처의 리소스를 공유하는 것인데 기본은 공유 불가이며 별도 설정을 통해 공유를 할 수 있게 만들 수도 있다. 하지만, 위 상황에서는 공유가 불가능한 상태다)
 
어쨋든 브라우저는 Application 2에서 제공하는 서비스들을 이용할 수 없기 때문에 서버 간 REST API 통신환경이 필요했고, Application 2와 통신이 가능한 Application 1을 별도로 구축해서 사용자는 Application 1에서 제공하는 GUI를 통해 Application 2에서 제공하는 서비스를 이용하는 예제 프로그램을 작성하게 되었다.
 
CORS가 막혀 있는 상황에서 서버간 통신이나 Postman 같은 Client 프로그램을 이용한 API 호출이 가능한데, 여기서는 java의 RestTemplate을 이용한 서버 간 통신 방법과 에러 발생 시 예외 처리를 하는 방법을 알아보자.
 
 
 
 
위 상황에서 Application 1에 Deploy 될 Springboot 앱 이라고 보면 된다.
 

소스코드

Spring Framework RestTemplate의 간단한 사용예를 위해 Controller에 모든 기능을 구현했다.

import org.springframework.web.client.RestTemplate;


@RestController
public class ServiceController {    
	@RequestMapping(value = "/api/send", method = RequestMethod.POST)
	@ResponseBody
	public JSONResponse inputsend(@RequestBody(required=true) Map<String, Object> param) throws Exception  
	{
        
        Map<String, Object> rtnMap = new HashMap<String, Object>();
        String answer = "";
        
        try{   
	    	    String url = (String)param.get("url");
	    	    String input_type = (String)param.get("input_type");
	    	    String input_text = "";
	    	    
	    	    HttpHeaders headers = new HttpHeaders();
	            //headers.setContentType(MediaType.APPLICATION_JSON);
	    	    if(input_type.equals("TEXT")){
	    	    	headers.setContentType(MediaType.TEXT_PLAIN);
	    	    	input_text = (String)param.get("input_text");
	    	    }else if(input_type.equals("JSON")){
	    	    	headers.setContentType(MediaType.APPLICATION_JSON);
	    	    	input_text = (String)param.get("input_json");
	    	    }

	            RestTemplate restTemplate = new RestTemplate();
	            //JSONObject jsonObject = new JSONObject();
	            //jsonObject.put("stringItem", bytes);
	            
                HttpEntity<String> entity = new HttpEntity<String>(input_text, headers);
                answer = restTemplate.postForObject(url, entity, String.class);

                Instant instant = Instant.now();
                long timeStampMillis = instant.toEpochMilli();

                rtnMap.put("after", answer);
                rtnMap.put("timestamp", timeStampMillis);
    	    
        } catch (HttpClientErrorException e) {
    	    	logger.error("error -- " + e.getStatusCode());
    	    	logger.error(e.getMessage());
    	    	logger.error(e.getResponseBodyAsString());
    	    	logger.error(e.getStatusText());
    	    	    	    	
    	    	if(!"".equals(e.getResponseBodyAsString()) ){
    	    		throw new Exception(e.getResponseBodyAsString());
    	    	}else{
	    	    	for(HttpStatus httpStatus : HttpStatus.values()){
	    	    		if(httpStatus == e.getStatusCode())
	    	    			throw new Exception("[HTTP Status Code: " + e.getStatusCode() + "] - " + httpStatus.name());
	    	        }
    	    	}
    	        
        }catch (Exception e) {
    	    	logger.info(e.getMessage());
    	    	throw new Exception(e.getMessage());
   	    }
        
        return new JSONResponse(200, "ok", rtnMap);
	}
}

 

반응형


작동 흐름

  1. 브라우저에서 1번 서버로 "/api/send" endpoint로 API 요청을 한다.
  2. 1번 서버는 브라우저의 요청을 받고, HTTP 헤더 정보와 Input Text를 포함한 HTTP Entity 객체를 만든다.
  3. URL과 HTTP Entity를 RestTemplate에 담아서 2번 서버의 REST API를 호출한다.
  4. 2번 서버가 1번 서버로부터 REST API 요청을 받으면 2번 서버는 내부의 서비스를 실행해서 1번 서버가 요청한 내용을 1번 서버에게 응답해 준다.
  5. 1번 서버가 2번 서버로부터 정상 응답을 받은 경우 해당 내용을 브라우저에 응답해 준다.
  6. 1번 서버가 2번 서버로부터 에러를 받은 경우 HttpClientErrorException 객체를 통해 예외 상황을 파악해서 브라우저에 어떤 내용의 에러가 발생했는지 응답해 준다. 

 
 

[참고] RestTemplate의 주요 메서드

 
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html

RestTemplate (Spring Framework 6.0.9 API)

postForLocation Create a new resource by POSTing the given object to the URI template, and returns the value of the Location header. This header typically indicates where the new resource is stored. URI Template variables are expanded using the given URI v

docs.spring.io

 
 

[참고] RestTemplate 지원 중단

WebFlux 스택과 함께 Spring Framework 5에서 Spring은 WebClient라는 새로운 HTTP 클라이언트를 도입했습니다.

WebClient는 RestTemplate에 대한 최신 대체 HTTP 클라이언트입니다. 기존의 동기식 API를 제공할 뿐만 아니라 효율적인 비차단 및 비동기식 접근 방식도 지원합니다.

즉, 새 애플리케이션을 개발하거나 이전 애플리케이션을 마이그레이션하는 경우 WebClient를 사용하는 것이 좋습니다. 앞으로 RestTemplate은 향후 버전에서 더 이상 사용되지 않습니다.

반응형