์ด ๊ธฐ์ฌ์์๋ ํ ํ๋ฆฟ(cookiecutter)์ ์์ฑํ๊ณ docker/docker-compose ๋ฐ conan ํจํค์ง ๊ด๋ฆฌ์๋ฅผ ์ฌ์ฉํ์ฌ C++์์ REST API ์๋น์ค๋ฅผ ์์ฑํ๊ธฐ ์ํ ํ๊ฒฝ์ ์ค์ ํ๋ ๋ฐฉ๋ฒ์ ๋ํด ์ค๋ช ํฉ๋๋ค.
์ ๊ฐ ๋ฐฑ์๋ ๊ฐ๋ฐ์๋ก ์ฐธ๊ฐํ ๋ค์ ํด์ปคํค์์๋ ๋ค์ ๋ง์ดํฌ๋ก์๋น์ค๋ฅผ ์์ฑํ๊ธฐ ์ํด ๋ฌด์์ ์ฌ์ฉํด์ผ ํ ์ง์ ๋ํ ์ง๋ฌธ์ด ์๊ฒผ์ต๋๋ค. ์ง๊ธ๊น์ง ์ด ๋ชจ๋ ๋ด์ฉ์ ์ ์ ์ ๊ฐ ์ด ๊ฒ์
๋๋ค.
๊ทธ๋์ ์ฐ๋ฆฌ๋ ๋ก๋๊ฐ ๋ง์ ์๋น์ค๋ฅผ ์์ฑํ๋ ์์ ์ ์ง๋ฉดํ๊ฒ ๋์๋๋ฐ, ๊ทธ ์ฃผ์ ์์ ์ ๋ค์ด์ค๋ ๋ฐ์ดํฐ๋ฅผ ์ ์ฒ๋ฆฌํ์ฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ฐ๋ ๊ฒ์ด์์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ ๋ ๋ค๋ฅธ ์ฐ๊ธฐ๊ฐ ๋๋ ํ ์น๊ตฌ๊ฐ C++ ๊ฐ๋ฐ์์ธ ๋์๊ฒ ์ ๋ฌธ๊ฐ๋ฅผ ์ฌ์ฉํ์ฌ ์ด ์๋น์ค๋ฅผ ์์ฑํ๋ผ๊ณ ์ ์ํ์ต๋๋ค. ์ด๊ฒ์ด ๋ ๋น ๋ฅด๊ณ ์์ฐ์ ์ผ ๊ฒ์ด๋ผ๊ณ ์ฃผ์ฅํ๋ฉฐ ์ผ๋ฐ์ ์ผ๋ก ์ฌ์ฌ์์๋จ์ ์ฐ๋ฆฌ๊ฐ ํ์ ๋ฆฌ์์ค๋ฅผ ๊ด๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ์๊ณ ์๋ ๋ฐฉ๋ฒ์ ๊ธฐ๋ปํ ๊ฒ์ ๋๋ค. ์ด์ ๋๋ C++์์ ๊ทธ๋ฐ ์ผ์ ํด๋ณธ ์ ์ด ์์ผ๋ฉฐ ๋จ์ 20์๊ฐ ์ด์์ ์ ์ ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๊ฒ์ํ๊ณ ์ปดํ์ผํ๊ณ ์ฐ๊ฒฐํ๋ ๋ฐ ์ฝ๊ฒ ํฌ์ํ ์ ์๋ค๊ณ ๋๋ตํ์ต๋๋ค. ๊ฐ๋จํ ๋งํด์, ๋๋ ๊ฒ์ ๋จน์์ต๋๋ค. ๊ทธ๋ ๊ฒ ๊ฒฐ์ฌํ๊ณ ์ฐจ๋ถํ๊ฒ ๋ชจ๋ ๊ฒ์ ํ์ด์ฌ์ผ๋ก ์์ฑํ์ต๋๋ค.
์ด์ ๊ฐ์ ์๊ฐ ๊ฒฉ๋ฆฌ ์ค์ C++๋ก ์๋น์ค๋ฅผ ์์ฑํ๋ ๋ฐฉ๋ฒ์ ์์๋ณด๊ธฐ๋ก ๊ฒฐ์ ํ์ต๋๋ค. ๊ฐ์ฅ ๋จผ์ ํด์ผ ํ ์ผ์ ์ ํฉํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๊ฒฐ์ ํ๋ ๊ฒ์ด์์ต๋๋ค. ๋ด ์ ํ์ด ๋จ์ด์ก์ด
์ฝ๋ํ์ผ.txt
[ํ์]poco/1.9.3
libpq/11.5
๊ฐ๋จํ ๋ช ๋ น "conan install"์ ์ฌ์ฉํ๋ฉด ๋ฉ๋๋ค. ํ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ค์นํ์ญ์์ค. ๋น์ฐํ ๋ณ๊ฒฝ ์ฌํญ๋ ํ์ํ์ต๋๋ค.
CMakeLists.txt
include(build/conanbuildinfo.cmake)
conan_basic_setup()
target_link_libraries(<target_name> ${CONAN_LIBS})
๊ทธ ํ ์ ๋ PostgreSQL๊ณผ ํจ๊ป ์์ ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฐพ๊ธฐ ์์ํ์ต๋๋ค. ๊ทธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์ ๊ฐ ์์ ํ ๊ฒฝํ์ด ๊ฑฐ์ ์์๊ณ ์ฐ๋ฆฌ Python ์๋น์ค์ ์ํธ ์์ฉํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๊ธฐ๋ ํ๊ธฐ ๋๋ฌธ์ ๋๋ค. ๊ทธ๋ฆฌ๊ณ ๋ด๊ฐ ๋ญ ๋ฐฐ์ ๋์ง ์์ธ์? ํฌ์ฝ์ ์์ด์! ๊ทธ๋ฌ๋ Conan์ ๊ทธ๊ฒ์ด POCO์ ์๋ค๋ ์ฌ์ค๊ณผ ๊ทธ๊ฒ์ ๋น๋ํ๋ ๋ฐฉ๋ฒ์ ๋ชจ๋ฆ ๋๋ค; ์ ์ฅ์์ ์ค๋๋ ๊ตฌ์ฑ ํ์ผ์ด ์์ต๋๋ค(๋๋ ์ด๋ฏธ POCO ์์ฑ์์๊ฒ ์ด ์ค๋ฅ์ ๋ํด ์ผ์ต๋๋ค). ์ด๋ ๋ค๋ฅธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฐพ์์ผ ํจ์ ์๋ฏธํฉ๋๋ค.
๊ทธ๋ฆฌ๊ณ ๋ด ์ ํ์ ๋ ์ธ๊ธฐ ์๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋จ์ด์ก์ต๋๋ค.
๋ค์ ๋จ๊ณ๋ ์์ฒญ์ ์ฒ๋ฆฌํ ์ ์๋ ์๋น์ค ํ
ํ๋ฆฟ์ ์์ฑํ๋ ๊ฒ์ด์์ต๋๋ค.
Poco::Util::ServerApplication์์ TemplateServerApp ํด๋์ค๋ฅผ ์์ํ๊ณ ๊ธฐ๋ณธ ๋ฉ์๋๋ฅผ ์ฌ์ ์ํด์ผ ํฉ๋๋ค.
ํ ํ๋ฆฟ์๋ฒ์ฑ
#pragma once
#include <string>
#include <vector>
#include <Poco/Util/ServerApplication.h>
class TemplateServerApp : public Poco::Util::ServerApplication
{
protected:
int main(const std::vector<std::string> &);
};
int TemplateServerApp::main(const vector<string> &)
{
HTTPServerParams* pParams = new HTTPServerParams;
pParams->setMaxQueued(100);
pParams->setMaxThreads(16);
HTTPServer s(new TemplateRequestHandlerFactory, ServerSocket(8000), pParams);
s.start();
cerr << "Server started" << endl;
waitForTerminationRequest(); // wait for CTRL-C or kill
cerr << "Shutting down..." << endl;
s.stop();
return Application::EXIT_OK;
}
๊ธฐ๋ณธ ๋ฉ์๋์์๋ ํฌํธ, ์ค๋ ๋ ์ ๋ฐ ๋๊ธฐ์ด ํฌ๊ธฐ์ ๊ฐ์ ๋งค๊ฐ๋ณ์๋ฅผ ์ค์ ํด์ผ ํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ ๊ฐ์ฅ ์ค์ํ ๊ฒ์ ๋ค์ด์ค๋ ์์ฒญ์ ๋ํ ํธ๋ค๋ฌ๋ฅผ ์ง์ ํด์ผ ํ๋ค๋ ๊ฒ์ ๋๋ค. ๊ณต์ฅ์ ๋ง๋ค๋ฉด ์ด๋ ๊ฒ ๋ฉ๋๋ค
TemplateRequestHandlerFactory
class TemplateRequestHandlerFactory : public HTTPRequestHandlerFactory
{
public:
virtual HTTPRequestHandler* createRequestHandler(const HTTPServerRequest & request)
{
return new TemplateServerAppHandler;
}
};
์ ๊ฒฝ์ฐ์๋ ๋งค๋ฒ ๋์ผํ ํธ๋ค๋ฌ์ธ TemplateServerAppHandler๋ฅผ ์์ฑํฉ๋๋ค. ์ฌ๊ธฐ์ ๋น์ฆ๋์ค ๋ก์ง์ ๋ฐฐ์นํ ์ ์์ต๋๋ค.
TemplateServerAppHandler
class TemplateServerAppHandler : public HTTPRequestHandler
{
public:
void handleRequest(HTTPServerRequest &req, HTTPServerResponse &resp)
{
URI uri(req.getURI());
string method = req.getMethod();
cerr << "URI: " << uri.toString() << endl;
cerr << "Method: " << req.getMethod() << endl;
StringTokenizer tokenizer(uri.getPath(), "/", StringTokenizer::TOK_TRIM);
HTMLForm form(req,req.stream());
if(!method.compare("POST"))
{
cerr << "POST" << endl;
}
else if(!method.compare("PUT"))
{
cerr << "PUT" << endl;
}
else if(!method.compare("DELETE"))
{
cerr << "DELETE" << endl;
}
resp.setStatus(HTTPResponse::HTTP_OK);
resp.setContentType("application/json");
ostream& out = resp.send();
out << "{"hello":"heh"}" << endl;
out.flush();
}
};
๋ํ PostgreSQL์์ ์๋ํ๋ ํด๋์ค ํ ํ๋ฆฟ๋ ๋ง๋ค์์ต๋๋ค. ํ ์ด๋ธ ์์ฑ๊ณผ ๊ฐ์ ๊ฐ๋จํ SQL์ ์ํํ๋ ๋ฐฉ๋ฒ์ด ์์ต๋๋ค. SQL() ์คํ. ๋ ๋ณต์กํ ์ฟผ๋ฆฌ๋ ๋ฐ์ดํฐ ๊ฒ์์ ์ํด์๋ ๋ค์์ ํตํด ์ฐ๊ฒฐ์ ์ป์ด์ผ ํฉ๋๋ค. GetConnection() libpg API๋ฅผ ์ฌ์ฉํ์ธ์. (์๋ง๋ ๋์ค์ ์ด ๋ถ์๋ฅผ ๋ฐ๋ก์ก์ ๊ฒ์ ๋๋ค).
๋ฐ์ดํฐ๋ฒ ์ด์ค
#pragma once
#include <memory>
#include <mutex>
#include <libpq-fe.h>
class Database
{
public:
Database();
std::shared_ptr<PGconn> GetConnection() const;
bool ExecuteSQL(const std::string& sql);
private:
void establish_connection();
void LoadEnvVariables();
std::string m_dbhost;
int m_dbport;
std::string m_dbname;
std::string m_dbuser;
std::string m_dbpass;
std::shared_ptr<PGconn> m_connection;
};
๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ์ ์ํ ๋ชจ๋ ๋งค๊ฐ๋ณ์๋ ํ๊ฒฝ์์ ๊ฐ์ ธ์ค๋ฏ๋ก .env ํ์ผ๋ ์์ฑํ๊ณ ๊ตฌ์ฑํด์ผ ํฉ๋๋ค.
.env
DATABASE_NAME=template
DATABASE_USER=user
DATABASE_PASSWORD=password
DATABASE_HOST=postgres
DATABASE_PORT=5432
๋ชจ๋ ์ฝ๋๋ ๋ค์์์ ๋ณผ ์ ์์ต๋๋ค.
์ด์ dockerfile๊ณผ docker-compose.yml์ ์์ฑํ๋ ๋ง์ง๋ง ๋จ๊ณ๊ฐ ์์ต๋๋ค. ์์งํ ๋งํด์ ์ด ์์ ์ ๋๋ถ๋ถ์ ์๊ฐ์ด ๊ฑธ๋ ธ์ต๋๋ค. ์ ๊ฐ ๋ฉ์ฒญํด์, ๋งค๋ฒ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ค์ ๋น๋ํด์ผ ํ๊ธฐ ๋๋ฌธ์ด๊ธฐ๋ ํ์ง๋ง, ์ฝ๋์ ํจ์ ๋๋ฌธ์ด๊ธฐ๋ ํ์ต๋๋ค. ์๋ฅผ ๋ค์ด, conan์ด ํ์ํ ์ข ์์ฑ์ ๋ค์ด๋ก๋, ์ค์น ๋ฐ ๋น๋ํ๋ ค๋ฉด "conan install ."์ ๋ค์ด๋ก๋ํ๋ ๊ฒ๋ง์ผ๋ก๋ ์ถฉ๋ถํ์ง ์์ผ๋ฉฐ -scompiler.libcxx=libstdc++11 ๋งค๊ฐ๋ณ์๋ ์ ๋ฌํด์ผ ํฉ๋๋ค. ๊ทธ๋ ์ง ์์ผ๋ฉด ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฐ๊ฒฐํ๋ ๋จ๊ณ์์ ๋ง์ ์ค๋ฅ๊ฐ ๋ฐ์ํ ์ํ์ด ์์ต๋๋ค. ์ ๋ ์ด ์ค๋ฅ๋ก ์ธํด ๋ช ์๊ฐ ๋์ ๊ฐํ ์์์ต๋๋ค. ์ด ๊ธฐ์ฌ๊ฐ ๋ค๋ฅธ ์ฌ๋๋ค์ด ๋ ์งง์ ์๊ฐ์ ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ๋ฐ ๋์์ด ๋๊ธฐ๋ฅผ ๋ฐ๋๋๋ค.
๋ค์์ผ๋ก, ์น๊ตฌ์ ์กฐ์ธ์ ๋ฐ๋ผ docker-compose.yml์ ์์ฑํ ํ ์ง์์ ์ถ๊ฐํ์ต๋๋ค.
์ด ํ
ํ๋ฆฟ์ด ํ๋ฅญํ๊ณ ๊ฐ๋ ฅํ์ง๋ง C++์ ๊ฐ์ ์ํฌ๋ฅธ ์ธ์ด๋ก REST API ์ ํ๋ฆฌ์ผ์ด์
์ ๊ฐ๋ฐํ๋ ์ด๋ ค์ด ๊ธธ์ ์๋ ์ด๋ณด์์๊ฒ ๋์์ด ๋๊ธฐ๋ฅผ ๋ฐ๋๋๋ค.
๋ํ ์ฌ๊ธฐ์์ ์ฝ์ด ๋ณด๋ ๊ฒ์ด ์ข์ต๋๋ค.
์ถ์ฒ : habr.com