순간을 성실히, 화려함보단 꾸준함을

HttpClient 를 사용해서 Rest API 웹서비스 구현해보기!!! 본문

나의 개발 메모장

HttpClient 를 사용해서 Rest API 웹서비스 구현해보기!!!

폭발토끼 2023. 4. 18. 13:15

안녕하세요!!!
원래는,,,,글또 6번째 글이어야 하지만 ㅠㅠㅠ 한번을 스킵했습니다.
이번에는 기한에 쪼달리지 않게 미리미리 글을 조금씩 쓰기 위해서 미리 글을 작성해보려고 합니다!
5번째 글입니다!!!!

 

이번 글의 주제는 사이드 프로젝트는 아닌 회사일을 하면서 마주했던 문제에 대해 해결(?!) 했던 방법을 적어보려고 합니다.


바로 HttpClient class 를 사용하여 Rest 웹 서비스를 구현한 경험입니다.

웹서비스가 무엇인지 간단하게 설명하자면 HTTP/HTTPS 프로토콜을 사용하여 데이터만을 주고받을 수 있도록 설계된 모듈을 말합니다.

즉, 현재 내가 관리하고 있는 시스템에서 외부 API 를 호출하기 위한 작업이라고 생각하시면 될 것 같습니다.
여러 글들을 찾아보니 크게 4가지의 방법이 존재한다고 합니다.

  1. HttpUrlConnection
  2. HttpClient
  3. Spring RestTemplate
  4. WebClient

보통 spring 프레임워크 를 사용하시는 분들은 WebClient를 사용하신다고 알고 있습니다.(저도 말만 들었지 이번 기회에 무엇인지 공부하게 되었습니다)

 

그러나 정말정말 안타깝게도....회사에서는 spring 이 아닌 struts 라는 프레임워크를 사용하고 있고 WebClient 와 RestTemplate spring 프레임워크에서 지원하는 라이브러리 입니다.


그래서 선택지가 2가지로 줄어들었는데요 전 2번 HttpClient class 를 사용하기로 했습니다.

이유는 이렇습니다.

 

  1. HttpUrlConnection 은 I/O stream 을 처리하는 것이 익숙하지 않았습니다.
  2. HttpUrlConnection 은 4xx or 5xx 의 status code 가 응답으로 오면 IOException 이 발생합니다.
  3. Httpclient 를 사용해서 json response 를 String 형으로 받을 수 있습니다.

특히 3번 때문에 HttpClient 를 사용하였는데요, 우리가 보통 spring 프레임워크를 사용하면 json 문자열 데이터를 직렬화/역직렬화 하여 요청/응답 하게 됩니다.

 

이때, 우리는 별다른 행동을 하지 않아도 json 데이터를 Object class 로 변환이 됩니다. 대체 누가 그런걸까요??
이것저것 여러 과정들을 거치지만 jackson library 의 objectMapper 가 변환해주는 역할을 수행합니다.
(중요 Http body 에 json 형태로 요청/응답이 이루어질때만 해당됩니다. 즉, @RequestBody or @ResponseBody 일때만 해당되고 @ModelAttribute 나 @RequestParam 으로 요청을 받을때는 해당되지 않습니다!!)
참고 : https://jojoldu.tistory.com/407

 

@Request Body에서는 Setter가 필요없다?

회사에서 근무하던중 새로오신 신입 개발자분이 저에게 하나의 질문을 했습니다. POST 요청시에 Setter가 필요없는것 같다고. 여태 제가 알던것과는 달라서 어떻게 된 일인지 궁금했습니다. 정말 P

jojoldu.tistory.com

그래서 HttpClient 를 사용해서 json 문자열을 객체로 '직렬화/역직렬화' 하여 '요청/응답'을 구현해주는 것이 io stream 으로 처리하는 것 보다 훨씬 익숙하고 효율적이라고 생각했기 때문에 선택하게 된 것 입니다.

어떻게 구현했는지 설명해드리도록 하겠습니다.!

 

먼저 json 객체를 요청하고 응답받을 수 있는 사이트를 사용해서 테스트를 진행했습니다.http://jsonplaceholder.typicode.com 여기인데요 방법은 간단합니다.

 

Get : http://jsonplaceholder.typicode.com/posts/1
Post : http://jsonplaceholder.typicode.com/posts (http body 정의필요)

 

이런식으로 테스트할 수 있습니다.

 

전 post 요청으로 진행해보았는데요. 먼저 jackson library 를 사용하기 위해선 의존성을 먼저 주입 받아야 합니다. spring boot 를 사용하시는 분은 implementation 'org.springframework.boot:spring-boot-starter-web' 를 gradle 에 추가해주면 사용할 수 있지만 그렇지 않은 분들은 maven 공식 홈페이지에 들어가셔서 의존성을 부여해주어야 합니다.

https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind
https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core

 

하나는 jackson-datbind 나머지 하나는 jackson-core 인데 처음에는 objectMapper 클래스가 jackson-databind 패키지 안에 존재하길래 core는 따로 의존성을 추가해주지 않았더니 에러가 발생하더라구요. 그래서 웬만하면 두개다 넣어주시는걸 추천드립니다.

자, 준비가 다 끝난것 같네요 ㅎㅎ
과정은 이렇습니다.

  1. controller 선언 후 코드 작성
  2. postman 혹은 intellij http 파일로 controller 로 요청
  3. controller 에서는 http://jsonplaceholder.typicode.com/posts 에 정의되어있는 외부 api 호출
  4. response 값 확인

controller 내부에서 request 보낼 Dto 클래스를 정의해주었습니다.

@Setter
@Getter
@NoArgsConstructor
public class JsonTestDto {

    private String title;
    private String body;
    private Long userId;

    @Builder
    public JsonTestDto(String title, String body, Long userId) {
        this.title = title;
        this.body = body;
        this.userId = userId;
    }
}

Dto 객체를 생성해준 후 objectMapper 를 사용하여 json 문자열로 변환해주었습니다(직렬화)

//request 객체
JsonTestDto jsonTestDto = JsonTestDto.builder()
       .title("testTitle")
       .body("testBody")
       .userId(1L)
       .build();
//직렬화
String json = objectMapper.writeValueAsString(jsonTestDto);

그 후 httpClient 객체를 생성해 준 뒤 외부 API 를 호출해줍니다.

//hhtpClient 객체 생성
CloseableHttpClient httpClient = HttpClients.createDefault();

//외부 api 가 존재하는 url
String url ="http://jsonplaceholder.typicode.com/posts";
HttpPost httpPost = new HttpPost(url);

//content-type 정의 및 http body에 json 문자열 정의
httpPost.addHeader("Content-Type","application/json");
StringEntity entity = new StringEntity(json);
httpPost.setEntity(entity);

요청을 보냈으니 응답값을 받아와야겠죠?

ResponseHandler<String> responseHandler = new BasicResponseHandler();
String response = httpClient.execute(httpPost, responseHandler);
ResponseTestObject object = objectMapper.readValue(response, ResponseTestObject.class);
log.info(object.toString());
return object;

결과값은?!

짠 성공했습니다~~

참고로 전 postman 대신 intellij http 파일을 사용해서 요청을 보냈습니다.

이렇게 HttpClient 를 사용해서 외부 API 와 통신을 한번 해봤는데 사실 요즘은 거의 쓰지 않는 방법이긴 합니다. spring 프레임워크를 사용하는 분들은 WebClient 를 사용하시는걸 추천드리지만 언제 어느상황이 발생할지 모르니 혹시나 제 글이 도움이 되셨으면 하는 바램입니다.

 

다음에는 Webclient 를 사용해서 외부 API와 통신하는 방법을 글로 써보도록 하겠습니다. 언제가 될지는 모르겠지만 열심히 공부를 해봐야겠네요.

 

총 소스를 마지막으로 글을 마치겠습니다.

package jipdol2.spring.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import jipdol2.spring.dto.JsonTestDto;
import jipdol2.spring.dto.ResponseTestObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Slf4j
@Controller
@RequestMapping("/api/test")
public class TestController {

    private ObjectMapper objectMapper = new ObjectMapper();

    @ResponseBody
    @GetMapping
    public ResponseTestObject testObject(){
        try{
            //request 객체
            JsonTestDto jsonTestDto = JsonTestDto.builder()
                    .title("testTitle")
                     .body("testBody")
                     .userId(1L)
                     .build();
            //직렬화
            String json = objectMapper.writeValueAsString(jsonTestDto);

            //hhtpClient 객체 생성
            CloseableHttpClient httpClient = HttpClients.createDefault();

            //외부 api 가 존재하는 url
            String url ="http://jsonplaceholder.typicode.com/posts";
            HttpPost httpPost = new HttpPost(url);

            //content-type 정의 및 http body에 json 문자열 정의
            httpPost.addHeader("Content-Type","application/json");
            StringEntity entity = new StringEntity(json);
            httpPost.setEntity(entity);

            //response 반환
            ResponseHandler<String> responseHandler = new BasicResponseHandler();
            String response = httpClient.execute(httpPost, responseHandler);
            ResponseTestObject object = objectMapper.readValue(response, ResponseTestObject.class);
            log.info(object.toString());
            return object;
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }
}

RestTemplate 이 Deprecated 된다!!! 라는 글들을 정말 많이 보이고 그렇게 저도 알고 있었는데 공식적으로는 Deprecated 되지 않는다고 합니다.
참고 : https://www.youtube.com/watch?v=S4W3cJOuLrU&t=627s