[C/C++] 프록시 패턴(Proxy Pattern)

할랑말랑·2026년 3월 25일

C/C++

목록 보기
39/45

프록시 패턴(Proxy Pattern)

어떤 객체를 직접적으로 사용하는 것이 아니라, 그 객체를 대신하는 프록시(대리인) 객체를 앞에 두어 호출을 가로채는 방식

1. 주요 특징

  • 제어권 가로채기 : 실제 객체(RealSubject)가 실행되기 전이나 후에 추가적인 로직(권한 확인, 로그 기록, 캐싱 등)을 수행
  • 투명성 : 프록시는 실제 객체와 동일한 인터페이스를 구현하므로, 클라이언트는 자기가 쓰는 게 프록시인지 실제 객체인지 알 필요가 없다.

2. 구성 요소

  • Subject (인터페이스) : 실제 객체와 프록시가 공통으로 따르는 규칙
  • Real Subject (실제 객체) : 진짜 핵심 로직을 수행하는 본체
  • Proxy : 실제 객체를 내부에 품고 있으면서, 클라이언트의 요청을 대신 받아 전달하거나 거절하는 객체

3. 대표적인 활용 사례

  • 가상 프록시 (Virtual Proxy) : 객체 생성 비용이 너무 클 때(예: 엄청나게 큰 이미지를 불러올 때), 실제로 필요한 순간까지 생성을 미루다가(Lazy Loading) 그때 생성
  • 보호 프록시 (Protection Proxy) : 접근 권한을 체크

4. 장점

  • 접근 제어 : 권한 관리 집중화, 실제 객체 보호
  • 캡슐화 : 구현 세부사항 숨김, 결합도 낮아짐, 유연한 교체 가능

5. 단점

  • 복잡도 증가 : 단순한 구조에서도 프록시 클래스를 하나 더 만들어야 하므로 전체적인 코드 양 증가
  • 객체 간의 강한 결합 : 프록시가 실제 객체의 구현 방식에 너무 의존하게 되면, 실제 객체가 바뀔 때 프록시도 매번 같이 수정

Protection Proxy

  • Subject (인터페이스) : IMonster
  • Real Subject (실제 객체) : ModernMonster
  • Proxy : MonsterProxy

#include <iostream>
#include <string>

// 몬스터의 상태를 나타내는 열거형 클래스
enum class MonsterState
{
    IDLE,
    ATTACK,
    RUN
};

// 몬스터의 행동을 정의하는 인터페이스 클래스
class IMonster
{
public:
    virtual ~IMonster() = default;
    virtual void Attack(int _useenergy) = 0;
    virtual void RecoverEnergy(int _addenergy) = 0;
};

// 실제 몬스터의 기능을 구현한 클래스 (Real Subject)
class ModernMonster : public IMonster
{
private:
    std::string name;
    int attack;
    int energy;

public:
    ModernMonster(const std::string& _name, int _attack = 100, int _energy = 100) : name(_name), attack(_attack), energy(_energy) {}

    void Attack(int _useenergy) override
    {
        if (energy - _useenergy >= 0)
        {
            std::cout << name << " - 공격 : " << attack << std::endl;
            energy -= _useenergy;
        }
        else
        {
            std::cout << "기력이 부족합니다." << std::endl;
        }
    }

    void RecoverEnergy(int _addenergy) override
    {
        energy += _addenergy;
    }
};

// 몬스터에 대한 접근을 제어하는 프록시 클래스 (Proxy)
class MonsterProxy : public IMonster
{
private:
    std::shared_ptr<ModernMonster> monster; // 실제 몬스터 객체에 대한 포인터
    MonsterState state;                     // 몬스터의 현재 상태를 제어

public:
    MonsterProxy(std::shared_ptr<ModernMonster> _monster, const MonsterState& _state) : monster(_monster), state(_state) {};

    // 공격 요청을 중계
    void Attack(int _useenergy) override
    {
        // 상태가 ATTACK일 때만 실제 몬스터의 Attack 함수를 호출
        if (state == MonsterState::ATTACK)
        {
            monster->Attack(_useenergy);
        }
        else
        {
            std::cout << "대기,달리는 상태입니다. 공격불가" << std::endl;
        }
    }

    // 에너지 회복 요청을 중계
    void RecoverEnergy(int _addenergy) override
    {
        // 상태가 IDLE일 때만 실제 몬스터의 RecoverEnergy 함수를 호출
        if (state == MonsterState::IDLE)
        {
            monster->RecoverEnergy(_addenergy);
        }
        else
        {
            std::cout << "달리거나 공격중인상태에서는 기력 회복 불가" << std::endl;
        }
    }

    // 프록시가 관리하는 상태를 변경/조회하는 함수
    void SetState(const MonsterState& _state) { state = _state; }
    MonsterState getState() const { return state; }
};

int main()
{
    // 실제 몬스터 객체를 생성
    std::shared_ptr<ModernMonster> monster = std::make_shared<ModernMonster>("슬라임");

    // 실제 몬스터를 제어할 프록시 객체를 생성. 초기 상태는 IDLE
    MonsterProxy proxy(monster, MonsterState::IDLE);

    proxy.Attack(70); // IDLE 상태이므로 공격 불가
    proxy.SetState(MonsterState::ATTACK); // 상태를 ATTACK으로 변경
    proxy.Attack(70); // ATTACK 상태이므로 공격 가능
    proxy.Attack(70); // 기력이 부족하여 공격 불가

    proxy.RecoverEnergy(10); // ATTACK 상태이므로 회복 불가
    proxy.SetState(MonsterState::IDLE); // 상태를 IDLE로 변경
    proxy.RecoverEnergy(50); // IDLE 상태이므로 회복 가능
    proxy.RecoverEnergy(50); // IDLE 상태이므로 회복 가능

    return 0;
}

  • 인터페이스 공유 : 실제 객체(ModernMonster)와 프록시(MonsterProxy)가 모두 동일한 인터페이스(IMonster)를 구현합니다. 덕분에 main 함수와 같은 클라이언트 코드에서는 실제 객체를 쓰든 프록시를 쓰든 동일한 방식으로 코드를 작성할 수 있다.
  • 접근 제어 : MonsterProxy는 ModernMonster의 메서드를 바로 호출해주지 않는다. 대신, 자신이 가진 state 변수의 값을 먼저 확인하고, 조건이 맞을 때만 실제 객체에게 요청을 전달
  • 부가 기능 : 이 예제에서는 '상태에 따른 접근 제어'라는 부가 기능을 추가했지만, 프록시 패턴은 네트워크 통신, 로깅, 캐싱 등 다양한 부가 기능을 추가하는 데 사용될 수 있다.

0개의 댓글