C++ 프록시 패턴
프로그램의 객체들은 저마다의 역할이 있습니다. 하지만 어떤 객체들은 그 역할을 수행하기 위해 복잡한 과정을 거치는 경우도 있습니다. 이럴 경우 특정 객체에 작업이 몰리거나 과정이 복잡하면 유지 보수하기 어려울 수 있습니다. 이를 위해서 대신 수행해줄 수 있는 객체를 통해 수행한다면 좀 더 유지보수가 나을 수 있습니다.
웹툰 어플을 보면 사용자가 요청을 하면 해당 만화 이미지를 제공해주게 되는데 11시 같은 업로드 시간같이 특정 시간에 몰리게 되면 반응 시간이 느리게 될 수도 있습니다. 서비스 요청이 많아도 추가 자원 없이 서비스 시간 및 반응을 안정되게 유지하기 위해서는 요청 및 제공에 관련한 작업의 단계를 세분화하여 각 단계별로 소요되는 시간을 측정하고 단축시키는 방법이 있습니다. 가장 많은 시간이 걸리는 단계 그리고 구현 상 줄이는 것이 가능한 단계를 줄이는 방법을 찾는 것이 작업 대비 효율이 좋기 때문입니다. 예를 들어 사용자가 원하는 웹툰의 회차를 요청하는 단계가 있고 서버가 해당 이미지를 찾아 불러오는 단계가 있고 서버가 해당 이미지를 사용자에게 전송해주는 단계가 있습니다. 서버에서 이미지를 사용자에게 전송해주는 것은 네트워크 환경을 바꾸지 않는 이상 크게 바뀌지 않으므로 이 단계보다 서버에서 해당 이미지를 찾아 불러오는 단계는 로직을 좀 변경한다면 시간이 줄어들 수 있습니다.
소요 시간을 줄이기 위해 모든 이미지를 디스크에서 로딩하는 방법보다 인기 순위가 높은 웹툰의 이미지 혹은 업로드되는 날의 이미지들은 메모리상에 캐싱하고 있다가 요청 시 활용하면 시간을 줄일 수 있습니다. 이 방법을 사용하기 위해서는 이미지가 메모리에 캐싱되어 있는지 확인하고 그렇다면 그것을 읽어 처리하고 아니라면 디스크로부터 읽어 처리하는 과정을 거칩니다.
서버에서 ImageFileCache를 통해 이미지를 등록하고 등록되어 있는지를 확인합니다. 그리고 ImageFile를 통해 디스크를 통해 이미지를 읽어 들여 처리를 합니다.
그런데 위처럼 Server가 일일이 두 클래스에 모두 접근하여 이미지가 캐싱되어 있는지 확인하고 없다면 처리하고 이런 방법보다 Server는 ImageFileCache에만 접근하여 등록되어 있지 않은 이미지라면 ImageFileCache에서 ImageFile에 접근해서 처리하는 것이 바람직합니다. 두 방법 모두 서버는 원하는 이미지를 불러오지만 서버가 관리해야 하는 객체가 하나 줄어들게 됩니다.
그런데 이 좋은 방법도 이미 모든 서버 프로그램이 디스크로부터 파일을 읽는 방식(ImageFile 클래스를 통해 접근하는 방식)을 하고 있다가 위의 방식을 적용하려 한다면 전부 수정을 해야 합니다. 최종적으로 인터페이스를 수정하여 아래와 같은 구조를 가진다면 모두 만족하게 됩니다.
위처럼 원래 수행하고 있는 클래스가 존재할 때 그 클래스가 제공하는 기능, 역할을 그대로 활용하면서 부가적인 기능이나 역할을 수행하기 위한 새로운 클래스를 정의하고 그 클래스를 거쳐 원래의 클래스 객체에 전달하는 방식의 클래스 구조를 프록시 패턴이라 합니다. 이 패턴은 기존의 프로그램을 수정하지 않고 새로운 클래스를 사용하기 위해 동일한 인터페이스를 제공해야 합니다.
class File { virtual int GetFile(string fn, void *pOut) = 0; };
class ImageFile : public File {
int GetFile(string fn, void *pOut) {
int fd = open();
...; // 파일 open 후 처리하여 return하는 과정
}
};
class ImageFileCache : public File {
int GetFile(string fn, void *pOut) {
int nResult = checkImage(); // 이미지가 등록되어 있는지 확인
if ( !nResult ) {
ImageFile file;
file.GetFile();
} // 등록되어 있지 않다면 디스크에서 이미지 로딩 처리
}
};
int main()
{
File* pFileServer = new ImageFileCache;
void* pImageData;
pFileServer->GetFile("webtoonImage", pImageData);
}
만약 웹툰 서비스에서 이미지뿐 아니라 영상으로 된 콘텐츠도 사용한다면 위 구조에서 아래와 같은 구조로 확장이 될 수 있습니다.
이렇게 프록시 패턴을 사용하면 Client와 기존 클래스 사이에 중간 매개체를 두어 기존 클래스의 기능이나 역할을 어느 정도 대행하게 해서 기존 클래스의 부담이나 유지보수의 비용을 줄일 수 있습니다.