C++ Builder 패턴
복잡한 객체를 단계적으로 구성하는 Builder 패턴입니다. Fluent API 메서드 체이닝과 Director 클래스를 활용한 HTTP 요청 빌더를 예시로 구현합니다.
#include <iostream>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include <sstream>
#include <stdexcept>
// ─────────────────────────────────────────────
// Product: HTTP 요청 객체
// Builder가 최종적으로 생성하는 복잡한 객체
// ─────────────────────────────────────────────
class HttpRequest {
public:
void print() const {
std::cout << method_ << " " << url_ << " HTTP/1.1\n";
for (const auto& [k, v] : headers_) {
std::cout << " " << k << ": " << v << "\n";
}
if (!queryParams_.empty()) {
std::cout << " 쿼리 파라미터:\n";
for (const auto& [k, v] : queryParams_) {
std::cout << " " << k << "=" << v << "\n";
}
}
if (!body_.empty()) {
std::cout << " 본문: " << body_ << "\n";
}
std::cout << " 타임아웃: " << timeoutMs_ << "ms\n";
}
// Builder만 내부 상태를 설정할 수 있도록 friend 선언
friend class HttpRequestBuilder;
private:
std::string method_{"GET"};
std::string url_;
std::unordered_map<std::string, std::string> headers_;
std::unordered_map<std::string, std::string> queryParams_;
std::string body_;
int timeoutMs_{5000};
};
// ─────────────────────────────────────────────
// Builder: Fluent API로 HttpRequest를 단계별 구성
// 각 메서드가 *this를 반환 → 메서드 체이닝 가능
// ─────────────────────────────────────────────
class HttpRequestBuilder {
public:
// HTTP 메서드 설정
HttpRequestBuilder& method(const std::string& m) {
request_.method_ = m;
return *this;
}
// 대상 URL 설정
HttpRequestBuilder& url(const std::string& u) {
request_.url_ = u;
return *this;
}
// 헤더 추가 (누적 가능)
HttpRequestBuilder& header(const std::string& key, const std::string& value) {
request_.headers_[key] = value;
return *this;
}
// Bearer 토큰 인증 헤더 편의 메서드
HttpRequestBuilder& bearerAuth(const std::string& token) {
return header("Authorization", "Bearer " + token);
}
// JSON Content-Type 설정 편의 메서드
HttpRequestBuilder& jsonContentType() {
return header("Content-Type", "application/json");
}
// 쿼리 파라미터 추가
HttpRequestBuilder& queryParam(const std::string& key, const std::string& value) {
request_.queryParams_[key] = value;
return *this;
}
// 요청 본문 설정
HttpRequestBuilder& body(const std::string& b) {
request_.body_ = b;
return *this;
}
// 타임아웃 설정 (밀리초)
HttpRequestBuilder& timeout(int ms) {
if (ms <= 0) throw std::invalid_argument("타임아웃은 양수여야 합니다.");
request_.timeoutMs_ = ms;
return *this;
}
// 유효성 검사 후 최종 객체 반환 (빌더 소비)
HttpRequest build() {
if (request_.url_.empty()) {
throw std::runtime_error("URL은 필수 값입니다.");
}
return std::move(request_);
}
private:
HttpRequest request_;
};
// ─────────────────────────────────────────────
// Director: 자주 사용하는 요청 조합을 미리 정의
// Builder의 단계 순서를 캡슐화
// ─────────────────────────────────────────────
class HttpRequestDirector {
public:
// JSON API 인증 GET 요청 템플릿
static HttpRequest makeAuthenticatedGet(
const std::string& url, const std::string& token)
{
return HttpRequestBuilder{}
.method("GET")
.url(url)
.bearerAuth(token)
.header("Accept", "application/json")
.timeout(3000)
.build();
}
// JSON POST 요청 템플릿
static HttpRequest makeJsonPost(
const std::string& url,
const std::string& token,
const std::string& jsonBody)
{
return HttpRequestBuilder{}
.method("POST")
.url(url)
.bearerAuth(token)
.jsonContentType()
.body(jsonBody)
.timeout(10000)
.build();
}
};
// ─────────────────────────────────────────────
// 사용 예시
// ─────────────────────────────────────────────
int main() {
std::cout << "=== 직접 빌더 체이닝 ===\n";
// Fluent API로 세밀하게 커스터마이징
HttpRequest customReq = HttpRequestBuilder{}
.method("GET")
.url("https://api.example.com/users")
.bearerAuth("eyJhbGciOiJIUzI1NiJ9.token")
.header("Accept-Language", "ko-KR")
.queryParam("page", "1")
.queryParam("limit", "20")
.timeout(8000)
.build();
customReq.print();
std::cout << "\n=== Director를 통한 표준 GET 요청 ===\n";
// Director: 미리 정의된 패턴 재사용
HttpRequest getReq = HttpRequestDirector::makeAuthenticatedGet(
"https://api.example.com/products",
"my-secret-token"
);
getReq.print();
std::cout << "\n=== Director를 통한 JSON POST 요청 ===\n";
HttpRequest postReq = HttpRequestDirector::makeJsonPost(
"https://api.example.com/orders",
"my-secret-token",
R"({"productId": 42, "quantity": 3})"
);
postReq.print();
return 0;
}