근래에는 TCP/IP로 바로 통신하는 프로그램보다는 REST API를 이용하여 통신하는 프로그램이 대세인 듯 보입니다. 여기서는 C++로 REST API를 사용하는 방법을 알아보도록 하겠습니다. 강좌는 boost를 사용하였으며 boost에서 HTTP 통신 관련 상위 라이브러리인 boost beast를 사용합니다.
REST API의 기본이 되는 GET 메소드를 이용하여 데이터를 가져오는 방법을 살펴보겠습니다. GET 메소드를 이용하여 데이터를 가져오는 소스는 아래와 같습니다. 소스를 보면서 계속 설명하겠습니다.
#include <iostream>
#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/version.hpp>
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
using namespace std;
int main(int argc, char* argv[])
{
boost::asio::io_context ioc;
boost::asio::ip::tcp::resolver resolver(ioc);
boost::beast::tcp_stream stream(ioc);
try
{
auto const host = "jsonplaceholder.typicode.com";
auto const port = "80";
auto const target = "/posts/1";
bool isVer1_0 = false;
int version = isVer1_0 ? 10 : 11;
auto const results = resolver.resolve(host, port);
stream.connect(results);
string urlHost = host;
urlHost += ":";
urlHost += port;
boost::beast::http::request<boost::beast::http::string_body> req{ boost::beast::http::verb::get, target, version };
req.set(boost::beast::http::field::host, urlHost);
req.set(boost::beast::http::field::user_agent, BOOST_BEAST_VERSION_STRING);
boost::beast::http::write(stream, req);
boost::beast::flat_buffer buffer;
boost::beast::http::response<boost::beast::http::dynamic_body> res;
boost::beast::http::read(stream, buffer, res);
string json = boost::beast::buffers_to_string(res.body().data());
cout << json << endl;
boost::beast::error_code ec;
stream.socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
if (ec && ec != boost::beast::errc::not_connected)
{
clog << "error: " << ec.message() << endl;
return -1;
}
}
catch (std::exception const& ex) {
clog << "exception: " << ex.what() << endl;
return -1;
}
return 0;
}
실행 결과는 아래와 같습니다.
{
"userId": 1,
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
}
소스 설명
소스는 동기적인 통신을 하는 예제입니다. 소스에서 주의할 점은 port를 문자열로 사용하고 있다는 점이며 target에 API url을 입력한다는 점입니다. 또한 version 필드로 10(http version 1.0)과 11(http version 1.1)을 지정한다는 점입니다. buffers_to_string는 다중 버퍼를 하나로 합치는 역활을 합니다. 이 부분은 구현 내부와 관계 있어 특별한 이유 없으면 이렇게 처리하면 됩니다.
HTTP
boost beast를 활용하여 REST API를 처리하려면 HTTP에 대한 기본적인 지식이 있는 것이 좋습니다. HTTP의 version을 지정하는 것은 HTTP의 version에 따라 연결에 대한 기본 동작이 틀려지기 때문입니다. HTTP 1.0은 특별히 헤더에서 설정하지 않으면 요청 후 연결을 종료하는 것이 기본이며 1.1은 특별히 헤더에서 설정하지 않으면 요청 후 연결을 일정 시간 유지하는 것이 기본입니다.
boost::beast::http::request는 프로토콜의 헤더만을 설정하여 보냅니다. 이 부분은 HTTP 프로토콜에 대한 간단한 이해만 있으면 이해하기 쉬울 듯 합니다.
한글 & UTF-8
한글 출력이 있을 경우 요즘은 거의 인코딩이 UTF-8입니다. 그런 관계로 화면이나 디버그 출력에서 한글이 제대로 나오게 하기 위해서는 UTF-8을 멀티바이트나 아스키 코드로 변환하여 주어야 합니다. 이 부분은 기존 아래 포스트 참고 부탁드립니다.
비동기적 처리
비동기적으로 변경하고 싶으시면 비동기 예제를 바탕으로 해당 소스를 수정하시면 됩니다. 그런데 HTTP 프로토콜 자체가 1.1에서 연결성을 기본적으로 지원하여 준다고는 하나 비연결성을 기반으로 시작된 프로토콜이고 REST API도 비연결성을 기본으로 만들어져 있어 호출할 때 연결하고 호출이 끝난 후에는 연결을 종료하는 것이 좋습니다. 이런 관계로 개인적으로는 비동기적으로 만들때 Network IO 레벨에서 비동기적으로 처리하는 것도 좋지만 C++ future등을 이용하여 동기적인 처리를 백그라운드 쓰레드에서 동작하게 만들어 비동기를 구현하는 것이 더욱 쉽게 개발하는 방법인 듯 싶습니다.
개인적으로는 라이브러리를 개발할 때 예제를 바탕으로 한 클래스를 만들고 C++ future를 이용하여 비동기를 구현하였습니다.
이 외의 또 다른 내용은 다음 강좌에서 살펴보도록 하겠습니다.
'강좌' 카테고리의 다른 글
C++ 코루틴(coroutine) 활용 강좌: #3 : co_return (0) | 2022.05.23 |
---|---|
C++ 코루틴(coroutine) 활용 강좌: #2 : co_await (0) | 2022.05.20 |
C++ 코루틴(coroutine) 활용 강좌: #1 시작하며 그리고 기본 개념 (0) | 2022.05.19 |
C++ REST API 사용 강좌 : #2 HTTP POST (boost beast 활용) (2) | 2022.05.12 |
C++ boost 메모리풀 강좌 #5: boost 메모리풀 할당자를 적용하여 STL 컬렉션의 성능 향상시키기 (0) | 2021.11.15 |
C++ boost 메모리풀 강좌 #4: object_pool 클래스를 이용하여 new 재정의 없이 일반 클래스에 메모리풀 적용하기 (0) | 2021.11.14 |
C++ boost 메모리풀 강좌 #3: singleton_pool 클래스를 이용하여 일반 클래스에 메모리풀 적용하기 (0) | 2021.11.13 |
C++ boost 메모리풀 강좌 #2: pool 클래스를 이용하여 버퍼에 메모리풀 적용하기 (0) | 2021.11.12 |