ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • RestTemplate Get Request - RestTemplate Hands On 2
    Spring Framework/RestTemplate 2023. 9. 21. 00:01

    개요

    이전의 RestTemplate 생성과정에 이어 RestTemplate Get Request를 실습해보려 합니다.

     

    글에서는 다음과 같은 테스트를 수행해 봅니다.

    • Get으로 String 객체 호출해 보기
    • getForEntity와 getForObject의 차이점
    • String을 returnType으로 받지만 MyObject가 오는 경우
    • 내가 원하는 필드만 발라내기
    • 중첩 객체 필드도 받을 수 있는가

     

    외부 호출 Controller 준비

    @RestController
    class TestController {
    
        @GetMapping("/outer-call")
        fun outerCall(): String{
            sleep(1000)
            return "process 1s job"
        }
    }
    
    // intellij HTTP plugin을 활용하여 호출해보기
    // GET localhost:8080/outer-call

     

    getForEntity 활용해서 외부 호출하기

    @GetMapping("/test")
    fun test(): String{
    	val baseUrl = "http://localhost:8080"
    	val apiPath = "/outer-call"
    	val restTemplate = RestTemplate()
    	val response: ResponseEntity<String> = restTemplate.getForEntity(
    		baseUrl + apiPath,
    		String::class.java,
    	)
    	println(response.statusCode)
    	println(response.headers)
    	println(response.body)
    	return "hello"
    }
        
    // intellij HTTP plugin을 활용하여 호출해보기
    // GET localhost:8080/test
    
    //결과
    //200 OK
    //[Content-Type:"text/plain;charset=UTF-8", Content-Length:"14", Date:"Fri, 01 Sep 2023 11:57:59 GMT", Keep-Alive:"timeout=60", Connection:"keep-alive"]
    //process 1s job

    RestTemplate 메서드의 getForEntity()를 활용하여 url과 responseType을 넣어 response를 원하는 타입으로 얻을 수 있습니다.

     

    응답 결과인 ResponseEntity<String>의 결과를 출력해 보면 순차적으로 statusCode에 대한 결과, 헤더에 대한 정보, body에 대한 정보를 얻을 수 있습니다.

    body의 값으로는 outer-call을 호출했을 때 반환하는 process 1s job이 그대로 반환됩니다.

     

    getForEntity 메서드 내부

    	public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {
    		RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
    		HttpMessageConverterExtractor<T> responseExtractor =
    				new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
    		return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
    	}

    내부적으로는 HTTPMethod.GET 인자를 넘겨서 execute 메서드를 호출해 주는 모습을 볼 수 있습니다.

     

     

    getForEntity vs getForObject

    val responseByGetForObject: String? = restTemplate.getForObject(
    	baseUrl + apiPath,
    	String::class.java,
    )
    
    println(responseByGetForObject)

    getForObject는 getForEntity와 다르게 헤더정보나 status 정보가 없습니다.

     

     

    String으로 responseType을 받으려 하지만 MyObject가 반환되는 경우

    data class MyObject(
        val col1: String = "col1",
        val col2: String = "col2",
        val col3: String = "col3",
    )
    
    
    @GetMapping("/object-outer-call")
    fun objectOuterCall(): ResponseEntity<MyObject>{
    	sleep(1000)
    	return ResponseEntity.ok(MyObject())
    }

    MyObject를 선언하고 MyObject 타입으로 반환하도록 object-outer-call api를 구현합니다.

     

    이제 String Type으로 받게 되면 어떻게 될까요?

    개인적으로는 타입이 맞지 않아 에러가 날것이라고 생각했습니다.

        @GetMapping("/object-test")
        fun objectTest(): String{
    
            val baseUrl = "http://localhost:8080"
            val apiPath = "/object-outer-call"
            val restTemplate = RestTemplate()
            val response: ResponseEntity<String> = restTemplate.getForEntity(
                baseUrl + apiPath,
                String::class.java,
            )
            println(response.statusCode)
            println(response.body)
    
            return "hello"
        }
        
        
    //결과
    //200 OK
    //{"col1":"col1","col2":"col2","col3":"col3"}

    하지만 결과는 Stirng으로 잘 보입니다.

     

    String 대신 MyObject로 반환받는 경우

    val response: ResponseEntity<MyObject> = restTemplate.getForEntity(
    	baseUrl + apiPath,
    	MyObject::class.java,
    )
    
    //결과
    //200 OK
    //MyObject(col1=col1, col2=col2, col3=col3)

    String 클래스 대신 MyObject 클래스로 받는 경우에는 형변환까지 자동으로 됩니다.

     

    여기까지 되자 2가지 궁금증이 생겼습니다.

    • 내가 관심 있는 필드만 받는 것도 가능할까? (예를 들어 col1 ... col100까지 있는데 나는 col2, col3 만 관심이 있는 경우)
    • 객체내부에서 객체를 가지는 경우도 잘 받아질까?

    바로 테스트해 보겠습니다.

     

    내가 관심 있는 필드만 받아 보기

    data class _MyObject(
        val col2: String = "col2",
    )
    
    val response: ResponseEntity<_MyObject> = restTemplate.getForEntity(
    	baseUrl + apiPath,
    	_MyObject::class.java,
    )
    //MyObject -> _MyObject로 변환
    
    
    //결과
    //200 OK
    //_MyObject(col2=col2)

    호출하는 쪽에서는 col2에만 관심이 있다고 _MyObject 클래스를 col2 만 가지도록 해보았습니다.

    결과로는 _MyObject에 col2에 대한 부분만 매핑이 잘 된 모습을 볼 수 있습니다.

     

    객체내부에서 객체를 가지는 중첩 구조도 잘 받아질까?

    val response: ResponseEntity<_NestedMyObject> = restTemplate.getForEntity(
    	baseUrl + apiPath,
    	_NestedMyObject::class.java,
    )
    
    data class _NestedMyObject(
        val nestedMyObject2: _NestedMyObject2,
        val test: String,
    )
    
    data class _NestedMyObject2(
        val nestedCol2: String,
    )
    
    data class NestedMyObject(
        val nestedMyObject2: NestedMyObject2 = NestedMyObject2(),
        val test: String = "nestedTest",
    )
    
    data class NestedMyObject2(
        val nestedCol1: String = "nestedCol1",
        val nestedCol2: String = "nestedCol2",
    )
    
    //결과
    //200 OK
    //_NestedMyObject(nestedMyObject2=_NestedMyObject2(nestedCol2=nestedCol2), test=nestedTest)

    NestedMyObject라는 클래스를 만들었고 받는 쪽에서는 _NestedMyObject2 클래스의 필드하나까지 생략해 보았습니다 (위에서 이미 테스트해 보긴 했음)

     

    중첩된 구조도 잘 받아지는 것을 확인할 수 있습니다.

     

    마무리

    Get에 대한 응답을 어떻게 받을지 다루어 보았습니다.

    다음글으로는 Get을 호출할 때 활용할 QueryParam, PathVariable에 대해 알아보고자 합니다.

     

     

    참고자료

    https://www.baeldung.com/rest-template

     

    댓글

Designed by Tistory.