스프링 부트

[SpringBoot] RestTemplate - getForObject, postForObject 로 통신하기!

h__hj 2022. 11. 19. 22:46

# RestTemplate - getForObject, postForObject

  RestTemplate의 가장 기본적인 getForObject와 postForObject 예제 코드 입니다!

통신 프로세스

View ↔ [ ajax ] ↔ Controller ↔ [ RestTemplate ] ↔ RestController ↔ Service ↔ Mapper ↔ DataBase

1. getForObject 통신

2. postForObject 통신, ContentType: application/json

3. postForObject 통신, ContentType: application/x-www-form-urlencoded 

# 환경

Tool  : STS 4.13.0
Ver   : 2.7.5 [GA]
java  : 11
Repo  : MAVEN
DB    : ORACLE XE (11g)
View  : Thymeleaf
jQuery: 3.6.0
Type  : Client(WEB), Server(API)

# Page

# View 

<body>
    <h1>REST TEMPLATE V2 테스트 페이지 입니다.</h1>
    <div>
        <h3>SAMPLE REST CODE</h3>
        <input type="button" onclick="call('get')" value="GET" style="display: inline;"/>
        <input type="button" onclick="call('post-json')" value="POST-JSON" style="display: inline;"/>
        <input type="button" onclick="call('post-form')" value="POST-FROM" style="display: inline;"/>
        <br>
        <input type="button" onclick="call('exchange-post')" value="POST" style="display: inline;"/>
        <input type="button" onclick="call('exchange-put')" value="PUT" style="display: inline;"/>
        <input type="button" onclick="call('exchange-delete')" value="DELETE" style="display: inline;"/>
        <br>
    </div>
</body>
<script type="text/javascript" th:inline="javascript">
    function call(path) {
        const target = `/test/rest-v2/${path}`;
        
        $.ajax(target, 
            {
                dataType: "json",
            }).done(function(output) {
                console.log(`OUTPUT[${path}]:`, JSON.stringify(output.result));
            }).fail(function(jqXHR) {
                console.log("ERROR:", jqXHR);
            }
        );
    }
</script>

# Controller - Client(WEB)

@Controller
@RequestMapping("/test/rest-v2")
public class TestRestV2Controller {

    @Value("${api.url.api}")
    private String apiUrlApi;
    @Autowired
    private RestTemplate template;
    
    @RequestMapping("/view")
    public ModelAndView view(ModelAndView mav) {
        mav.setViewName("test/restView2");
        return mav;
    }
    /**
     * RestTemplate getForObject( URI, responseType, [uriVariables] )
     */
    @RequestMapping("/get")
    public ModelAndView get() {
        // 요청하려는 URL 설정.
        String target = apiUrlApi.concat("/api-v1/test/rest/get/{pattern}");
        // 요청하려는 DATA 설정.
        String reqeust = "?data=한글";
        // 요청 URL에 전달 할 uriVariables 설정.
        Map<String, String> uriVariables = new HashMap<String, String>();
        uriVariables.put("pattern", "YYYY-MM-DD HH24:MI:SS");
        // GET 전송.
        RestMessage message = template.getForObject(target.concat(reqeust), RestMessage.class, uriVariables);
        // 응답 데이터 설정.
        ModelAndView mav = new ModelAndView("jsonView");
        mav.addObject("result", message.getData());
        return mav;
    }
    
    /**
     * RestTemplate postForObject( URI, request, responseType, [uriVariables] )
     */
    @RequestMapping("/post-json")
    public ModelAndView postJson() {
        // 요청하려는 URL 설정.
        String target = apiUrlApi.concat("/api-v1/test/rest/post-json");
        // 요청 시 본문에 담을 데이터.
        Map<String, String> request = new HashMap<String, String>();
        request.put("pattern", "YYYY-MM-DD HH24:MI:SS");
        // POST 전송.
        RestMessage message = template.postForObject(target, request, RestMessage.class);
        // 응답 데이터 설정.
        ModelAndView mav = new ModelAndView("jsonView");
        mav.addObject("result", message.getData());
        return mav;
    }
    
    /**
     * RestTemplate postForObject( URI, request, responseType, [uriVariables] )
     */
    @RequestMapping("/post-form")
    public ModelAndView postForm() {
        // 요청하려는 URL 설정.
        String target = apiUrlApi.concat("/api-v1/test/rest/post-form");
        // 요청 시 본문에 담을 데이터.
        MultiValueMap<String, Object> request = new LinkedMultiValueMap<String, Object>();
        request.add("pattern", "YYYY-MM-DD HH24:MI:SS");
        request.add("data", "테스트");
        request.add("data", "TEST");
        // POST 전송.
        RestMessage message = template.postForObject(target, request, RestMessage.class);
        // 응답 데이터 설정.
        ModelAndView mav = new ModelAndView("jsonView");
        mav.addObject("result", message.getData());
        return mav;
    }
}

# RestController - Server(API)

@Slf4j
@RestController
@RequestMapping("/api-v1/test/rest")
public class TestRestController {

    @Autowired
    private TestService testService;
    
    /**
     * PathVariable Annotation 사용
     */
    @GetMapping("/get/{pattern}")
    public RestMessage path(@PathVariable String pattern, @RequestParam String data) {
        log.debug("[PARAMETER] [get/pattern] pattern: {}, data: {}", pattern, data);
        String time = testService.time(pattern);
        
        RestMessage message = new RestMessage();
        message.setOk();
        message.setData(time);
        return message;
    }
    /**
     * application/json 사용.
     */
    @PostMapping("/post-json")
    public RestMessage json(@RequestBody Map<String, String> input) {
        log.debug("[PARAMETER] [post-json] pattern: {}", input);

        String pattern = input.get("pattern");
        String time = testService.time(pattern);
        
        RestMessage message = new RestMessage();
        message.setOk();
        message.setData(time);
        return message;
    }
    /**
     * application/x-www-form-urlencoded 사용.
     */
    @PostMapping("/post-form")
    public RestMessage form(@RequestParam MultiValueMap<String, String> input) {
        log.debug("[PARAMETER] [post-form] input: {}", input);
        log.debug("[PARAMETER] [post-form] data : {}", input.get("data").get(0));
        log.debug("[PARAMETER] [post-form] data : {}", input.get("data").get(1));
        String pattern = input.get("pattern").get(0);
        
        String time = testService.time(pattern);
        
        RestMessage message = new RestMessage();
        message.setOk();
        message.setData(time);
        return message;
    }
}

# Service

@Service
public class TestService {

    @Autowired
    private TestMapper testMapper;
    
    public String time(String pattern) {
        return testMapper.time(pattern);
    }
    
    public List<TestRVO> domains(TestPVO testPVO) {
        return testMapper.domains(testPVO);
    }
}

# Mapper - Interface

@Mapper
public interface TestMapper {

    String time(String pattern);

    List<TestRVO> domains(TestPVO testPVO);
}

# Mapper - xml

<mapper namespace="com.prjt.blog.test.mapper.TestMapper">
    
    <select id="time" parameterType="string" resultType="string">
/** 현재시간 **/ SELECT TO_CHAR(SYSDATE, #{pattern, jdbcType=VARCHAR}) as time FROM DUAL
    </select>
    
    <select id="domains" parameterType="com.prjt.blog.test.model.TestPVO" resultType="com.prjt.blog.test.model.TestRVO">
/** 도메인 **/
    WITH domain_table AS (
        SELECT null AS kr_name, null AS en_name, null AS addr_url, null AS delegator FROM dual
      UNION ALL 
        SELECT '네이버', 'NAVER', 'https://www.naver.com', '최수연' FROM dual
      UNION ALL 
        SELECT '구글', 'GOOGLE', 'https://www.google.com', '선다 피차이' FROM dual
      UNION ALL 
        SELECT '카카오', 'KAKAO', 'https://www.kakocorp.com', '홍은택' FROM dual
    )
    SELECT * 
      FROM domain_table
  <where>
       AND kr_name is not null
    <if test="krName != null and krName != ''">
       AND kr_name = #{krName, jdbcType=VARCHAR}
    </if>
  </where>
    </select>

</mapper>

# 테스트 로그

/* getForObject(): get 시간 순으로 나열 */
Client - [WEB] [REQ]: null
Client - [WEB] [URL]: (GET) http://localhost:8888/test/rest-v2/get
Client - [REST] [CALL]: data=한글
Client - [REST] [EXEC]: (GET) http://localhost:9999/api-v1/test/rest/get/YYYY-MM-DD%20HH24:MI:SS?data=%ED%95%9C%EA%B8%80

Server - [WEB] [REQ]: data=%ED%95%9C%EA%B8%80
Server - [WEB] [URL]: (GET) http://localhost:9999/api-v1/test/rest/get/YYYY-MM-DD%20HH24:MI:SS
Server - [PARAMETER] [get/pattern] pattern: YYYY-MM-DD HH24:MI:SS, data: 한글

DB SQL - ==>  Preparing: /** 현재시간 **/ SELECT TO_CHAR(SYSDATE, ?) as time FROM DUAL
DB SQL - ==> Parameters: YYYY-MM-DD HH24:MI:SS(String)
DB SQL - <==      Total: 1

Server - [WEB] [RES]: {"code":"0000","data":"2022-11-19 21:39:02"}

Client - [REST] [BACK]: (200) {"code":"0000","message":"정상적으로 처리 되었습니다.","data":"2022-11-19 21:39:02","args":null,"success":true}
Client - [WEB] [RES]: {"view":"jsonView","model":{"result":"2022-11-19 21:39:02"},"cleared":false}

View   - OUTPUT[get]: "2022-11-19 21:39:02"
/* postForObject(): post-json 시간 순으로 나열 */
Client - [WEB] [REQ]: null
Client - [WEB] [URL]: (GET) http://localhost:8888/test/rest-v2/post-json
Client - [REST] [CALL]: {pattern=YYYY-MM-DD HH24:MI:SS}
Client - [REST] [EXEC]: (POST) http://localhost:9999/api-v1/test/rest/post-json

Server - [WEB] [REQ]: [{"pattern":"YYYY-MM-DD HH24:MI:SS"}]
Server - [WEB] [URL]: (POST) http://localhost:9999/api-v1/test/rest/post-json
Server - [PARAMETER] [post-json] pattern: {pattern=YYYY-MM-DD HH24:MI:SS}

DB SQL - ==>  Preparing: /** 현재시간 **/ SELECT TO_CHAR(SYSDATE, ?) as time FROM DUAL
DB SQL - ==> Parameters: YYYY-MM-DD HH24:MI:SS(String)
DB SQL - <==      Total: 1

Server - [WEB] [RES]: {"code":"0000","data":"2022-11-19 21:50:08"}

Client - [REST] [BACK]: (200) {"code":"0000","message":"정상적으로 처리 되었습니다.","data":"2022-11-19 21:50:08","args":null,"success":true}
Client - [WEB] [RES]: {"view":"jsonView","model":{"result":"2022-11-19 21:50:08"},"cleared":false}

View   - OUTPUT[post-json]: "2022-11-19 21:50:08"
/* postForObject(): post-form 시간 순으로 나열 */
Client - [WEB] [REQ]: null
Client - [WEB] [URL]: (GET) http://localhost:8888/test/rest-v2/post-form
Client - [REST] [CALL]: pattern=YYYY-MM-DD+HH24%3AMI%3ASS&data=%ED%85%8C%EC%8A%A4%ED%8A%B8&data=TEST
Client - [REST] [EXEC]: (POST) http://localhost:9999/api-v1/test/rest/post-form

Server - [WEB] [REQ]: [{"pattern":["YYYY-MM-DD HH24:MI:SS"],"data":["테스트","TEST"]}]
Server - [WEB] [URL]: (POST) http://localhost:9999/api-v1/test/rest/post-form
Server - [PARAMETER] [post-form] input: {pattern=[YYYY-MM-DD HH24:MI:SS], data=[테스트, TEST]}
Server - [PARAMETER] [post-form] data : 테스트
Server - [PARAMETER] [post-form] data : TEST

DB SQL - ==>  Preparing: /** 현재시간 **/ SELECT TO_CHAR(SYSDATE, ?) as time FROM DUAL
DB SQL - ==> Parameters: YYYY-MM-DD HH24:MI:SS(String)
DB SQL - <==      Total: 1

Server - [WEB] [RES]: {"code":"0000","data":"2022-11-19 21:55:37"}

Client - [REST] [BACK]: (200) {"code":"0000","message":"정상적으로 처리 되었습니다.","data":"2022-11-19 21:55:37","args":null,"success":true}
Client - [WEB] [RES]: {"view":"jsonView","model":{"result":"2022-11-19 21:55:37"},"cleared":false}

View   - OUTPUT[post-form]: "2022-11-19 21:55:37"

# 내용

 3가지 방식으로 나누어 테스트 해보았고, 설정된 부분을 보면 조금 씩 다르다는 걸 알 수 있다.

1. getForObject

  • HttpMethod: GET
  • ContentType: application/x-www-form-urlencoded;charset=UTF-8
  • URI: /api-v1/test/rest/get/{pattern}
  • PathVariable: pattern
  • 파라미터 맵핑: @RequestParam
  • 파라미터 타입: String

2. postForObject - json

  • HttpMethod: POST
  • ContentType: application/json
  • URI: /api-v1/test/rest/post-json
  • 파라미터 맵핑: @RequestBody
  • 파라미터 타입: Map<String, String>

3. postForObject - form

  • HttpMethod: POST
  • ContentType: application/x-www-form-urlencoded;charset=UTF-8
  • URI: /api-v1/test/rest/post-form
  • 파라미터 맵핑: @RequestParam
  • 파라미터 타입: MultiValueMap<String, String>

 

  첫번째로 RestTemplate은 기본적으로 application/json으로 설정되는데,

GET 방식으로 전송하거나, 전송 데이터의 타입을 MultiValueMap 으로 전송할 때에는 ContentType이 application/x-www-form-urlencoded;charset=UTF-8 으로 전송되는 걸 확인할 수 있다.

 

 두번째로 ContentType이 json일 경우 @RequestBody로 Map이나 VO로 맵핑하여 받을 수 있고, 

x-www-form-urlencoded일 결우 @RequestParam으로 원시형타입으로 하나씩 변수를 맵핑하거나, MultiValuMap으로 여러개의 변수를 맵핑할 수 있다.

 

 모든 통신을 할 때 마찬가지 겠지만, RestTemplate을 이용하여 통신할 때에도 요청이 오는 ContentType이 뭔지 파악하고, ContentType에 따라 어떤 방식으로 변수를 맵핑할 지 생각하고 코드를 작성해야 된다.

# RestTemplate 시리즈

https://hjho95.tistory.com/35 getForObject, postForObject
https://hjho95.tistory.com/36 exchange (POST, PUT, DELETE)
https://hjho95.tistory.com/37 Configuration 
https://hjho95.tistory.com/38 ClientHttpRequestInterceptor, ResponseErrorHandler 

# RestTemplate 시리즈 참조 페이지

RestTemplate: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html
RestTemplate: https://www.baeldung.com/rest-template
Interceptor: https://www.baeldung.com/spring-rest-template-interceptor
ErrorHandling: https://www.baeldung.com/spring-rest-template-error-handling