C++ Proxy 패턴
실제 객체에 대한 대리자를 제공하여 접근을 제어합니다. 가상 프록시(지연 로딩)와 보호 프록시(접근 권한)를 통해 리소스 낭비 없이 안전한 객체 접근을 구현합니다.
#include <iostream>
#include <memory>
#include <string>
#include <unordered_map>
#include <stdexcept>
#include <chrono>
// ──────────────────────────────────────────────
// 주제 인터페이스: 실제 객체와 프록시가 공유
// ──────────────────────────────────────────────
class Image {
public:
virtual ~Image() = default;
virtual void render() const = 0;
virtual std::string name() const = 0;
virtual std::size_t sizeBytes() const = 0;
};
// ──────────────────────────────────────────────
// 실제 객체: 고해상도 이미지 (로딩 비용이 큼)
// ──────────────────────────────────────────────
class HighResolutionImage : public Image {
public:
explicit HighResolutionImage(const std::string& filePath)
: filePath_(filePath) {
loadFromDisk(); // 생성 시 즉시 디스크 I/O 발생
}
void render() const override {
std::cout << " [HighResolutionImage] '" << filePath_
<< "' 렌더링 완료 (" << sizeBytes_ / 1024 << " KB)\n";
}
std::string name() const override { return filePath_; }
std::size_t sizeBytes() const override { return sizeBytes_; }
private:
void loadFromDisk() {
// 실제로는 파일 I/O 수행 — 여기선 시뮬레이션
std::cout << " [HighResolutionImage] 디스크에서 로딩: "
<< filePath_ << " (대용량 파일 — 시간 소요)\n";
sizeBytes_ = 8 * 1024 * 1024; // 8MB 시뮬레이션
}
std::string filePath_;
std::size_t sizeBytes_{0};
};
// ──────────────────────────────────────────────
// [가상 프록시] 지연 로딩(Lazy Loading)
// 실제로 render() 호출 시점까지 HighResolutionImage 생성 지연
// ──────────────────────────────────────────────
class LazyImageProxy : public Image {
public:
explicit LazyImageProxy(std::string filePath)
: filePath_(std::move(filePath)) {}
void render() const override {
// 최초 접근 시에만 실제 이미지 로드 (지연 초기화)
if (!realImage_) {
std::cout << " [LazyImageProxy] 최초 접근 — 실제 이미지 로드 시작\n";
realImage_ = std::make_unique<HighResolutionImage>(filePath_);
} else {
std::cout << " [LazyImageProxy] 캐시된 이미지 사용\n";
}
realImage_->render();
}
std::string name() const override { return filePath_; }
std::size_t sizeBytes() const override {
// 아직 로드 안 됐으면 0 반환 (실제 로드 없이)
return realImage_ ? realImage_->sizeBytes() : 0;
}
private:
std::string filePath_;
mutable std::unique_ptr<HighResolutionImage> realImage_; // 지연 초기화
};
// ──────────────────────────────────────────────
// 데이터베이스 서비스 인터페이스
// ──────────────────────────────────────────────
enum class UserRole { GUEST, USER, ADMIN };
class DatabaseService {
public:
virtual ~DatabaseService() = default;
virtual std::string query(const std::string& sql) const = 0;
virtual void execute(const std::string& sql) = 0;
};
class RealDatabaseService : public DatabaseService {
public:
std::string query(const std::string& sql) const override {
std::cout << " [RealDB] 쿼리 실행: " << sql << "\n";
return R"({"rows": [{"id":1,"name":"홍길동"}]})";
}
void execute(const std::string& sql) override {
std::cout << " [RealDB] 명령 실행: " << sql << "\n";
}
};
// ──────────────────────────────────────────────
// [보호 프록시] 접근 권한 제어
// 역할(Role)에 따라 쿼리/실행 권한을 차별화
// ──────────────────────────────────────────────
class ProtectionDatabaseProxy : public DatabaseService {
public:
ProtectionDatabaseProxy(std::shared_ptr<RealDatabaseService> db,
UserRole role)
: realDb_(std::move(db)), role_(role) {}
std::string query(const std::string& sql) const override {
// GUEST 이상이면 읽기 허용
if (role_ == UserRole::GUEST || role_ == UserRole::USER
|| role_ == UserRole::ADMIN) {
logAccess("QUERY", sql);
return realDb_->query(sql);
}
throw std::runtime_error("[보호 프록시] 읽기 권한 없음");
}
void execute(const std::string& sql) override {
// ADMIN만 쓰기 허용
if (role_ != UserRole::ADMIN) {
std::cout << " [ProtectionProxy] 접근 거부 — ADMIN 권한 필요\n";
throw std::runtime_error("[보호 프록시] 쓰기 권한 없음: "
+ roleToString());
}
logAccess("EXECUTE", sql);
realDb_->execute(sql);
}
private:
void logAccess(const std::string& type, const std::string& sql) const {
std::cout << " [ProtectionProxy] 접근 로그 [" << roleToString()
<< "] " << type << ": " << sql << "\n";
}
std::string roleToString() const {
switch (role_) {
case UserRole::GUEST: return "GUEST";
case UserRole::USER: return "USER";
case UserRole::ADMIN: return "ADMIN";
}
return "UNKNOWN";
}
std::shared_ptr<RealDatabaseService> realDb_;
UserRole role_;
};
// ──────────────────────────────────────────────
// 사용 예시
// ──────────────────────────────────────────────
int main() {
std::cout << "=== Proxy 패턴 ===\n\n";
// ── 가상 프록시: 지연 로딩 ──
std::cout << "[1] 가상 프록시 — 이미지 지연 로딩\n";
LazyImageProxy proxy1("photo_4k_001.jpg");
LazyImageProxy proxy2("photo_4k_002.jpg");
std::cout << " 프록시 생성 완료 (아직 실제 이미지 미로드)\n";
std::cout << " proxy1 크기: " << proxy1.sizeBytes() << " bytes\n\n"; // 0
std::cout << " -- 첫 번째 render() 호출 --\n";
proxy1.render(); // 이 시점에 실제 로드
std::cout << "\n -- 두 번째 render() 호출 (캐시 사용) --\n";
proxy1.render(); // 캐시에서 렌더링
// ── 보호 프록시: 접근 제어 ──
std::cout << "\n[2] 보호 프록시 — 역할 기반 접근 제어\n";
auto realDb = std::make_shared<RealDatabaseService>();
ProtectionDatabaseProxy adminProxy(realDb, UserRole::ADMIN);
ProtectionDatabaseProxy userProxy(realDb, UserRole::USER);
std::cout << "\n [ADMIN 접근]\n";
adminProxy.query("SELECT * FROM users");
adminProxy.execute("DELETE FROM logs WHERE date < '2026-01-01'");
std::cout << "\n [USER 접근]\n";
userProxy.query("SELECT id, name FROM users");
try {
userProxy.execute("DROP TABLE users"); // 권한 없음 → 예외
} catch (const std::exception& e) {
std::cout << " 예외 처리: " << e.what() << "\n";
}
return 0;
}