프록시 패턴(Proxy Pattern)은 다른 객체에 대한 접근을 제어하기 위해 대리자 역할을 하는 객체를 사용하는 디자인 패턴입니다. 이 패턴은 객체의 접근을 통제하거나, 실제 객체를 대신해 간접적인 인터페이스를 제공할 때 유용합니다. 프록시는 클라이언트의 요청을 받아 실제 객체에 전달하거나, 요청을 가로채어 추가적인 작업을 수행할 수 있습니다.
프록시 패턴의 구성 요소
- Subject (주체): 실제 객체와 프록시 객체가 공유하는 인터페이스입니다. 여기에는 클라이언트가 호출할 메서드들이 정의됩니다.
- Real Subject (실제 객체): 실제 작업을 수행하는 객체입니다.
- Proxy (프록시 객체): 실제 객체에 대한 접근을 제어하는 역할을 합니다.
프록시 패턴의 종류와 예시
1. 가상 프록시 (Virtual Proxy)
- 목적: 실제 객체의 생성이 비용이 많이 드는 경우, 객체를 실제로 필요할 때까지 지연하여 생성합니다.
- 예시: 큰 이미지 파일을 로드할 때 가상 프록시를 사용하여, 실제로 이미지가 필요할 때만 로드합니다.
interface Image {
void display();
}
class RealImage implements Image {
private String filename;
public RealImage(String filename) {
this.filename = filename;
loadImageFromDisk();
}
private void loadImageFromDisk() {
System.out.println("Loading image from disk: " + filename);
}
@Override
public void display() {
System.out.println("Displaying image: " + filename);
}
}
class ProxyImage implements Image {
private RealImage realImage;
private String filename;
public ProxyImage(String filename) {
this.filename = filename;
}
@Override
public void display() {
if (realImage == null) {
realImage = new RealImage(filename);
}
realImage.display();
}
}
public class Main {
public static void main(String[] args) {
Image image = new ProxyImage("large_image.jpg");
image.display(); // 이미지가 처음 호출될 때 로드됨
image.display(); // 이미 로드된 이미지를 다시 표시
}
}
2. 보호 프록시 (Protection Proxy)
- 목적: 객체에 대한 접근을 제어하여, 사용자의 권한에 따라 특정 메서드의 접근을 제한합니다.
- 예시: 접근 제어를 위해 보호 프록시를 사용하여, 사용자의 권한에 따라 문서의 내용을 볼 수 있도록 합니다.
interface Document {
void display();
}
class RealDocument implements Document {
private String content;
public RealDocument(String content) {
this.content = content;
}
@Override
public void display() {
System.out.println("Displaying document: " + content);
}
}
class ProtectionProxyDocument implements Document {
private RealDocument realDocument;
private String userRole;
public ProtectionProxyDocument(String content, String userRole) {
this.realDocument = new RealDocument(content);
this.userRole = userRole;
}
@Override
public void display() {
if ("Admin".equals(userRole)) {
realDocument.display();
} else {
System.out.println("Access Denied: You do not have permission to view this document.");
}
}
}
public class Main {
public static void main(String[] args) {
Document document = new ProtectionProxyDocument("Confidential Report", "Guest");
document.display(); // 접근 거부 메시지 출력
Document adminDocument = new ProtectionProxyDocument("Confidential Report", "Admin");
adminDocument.display(); // 문서 내용 출력
}
}
3. 원격 프록시 (Remote Proxy)
- 목적: 실제 객체가 원격 서버에 있을 때, 이를 대신해 로컬에서 작업을 수행합니다.
- 예시: 원격 서버의 계산 작업을 프록시를 통해 수행합니다.
interface Calculator {
int add(int a, int b);
}
class RemoteCalculator implements Calculator {
@Override
public int add(int a, int b) {
System.out.println("Performing remote addition");
return a + b;
}
}
class CalculatorProxy implements Calculator {
private RemoteCalculator remoteCalculator;
@Override
public int add(int a, int b) {
if (remoteCalculator == null) {
remoteCalculator = new RemoteCalculator();
}
return remoteCalculator.add(a, b);
}
}
public class Main {
public static void main(String[] args) {
Calculator calculator = new CalculatorProxy();
System.out.println("Result: " + calculator.add(10, 20)); // 원격 계산 수행
}
}
4. 캐싱 프록시 (Caching Proxy)
- 목적: 반복적인 요청에 대해 동일한 결과를 반환할 때, 결과를 캐싱하여 성능을 향상시킵니다.
- 예시: 자주 조회되는 데이터를 캐싱하여 데이터베이스에 대한 반복적인 쿼리를 줄입니다.
interface DataFetcher {
String fetchData();
}
class RealDataFetcher implements DataFetcher {
@Override
public String fetchData() {
System.out.println("Fetching data from the database...");
return "Data from database";
}
}
class CachingProxyDataFetcher implements DataFetcher {
private RealDataFetcher realDataFetcher;
private String cachedData;
@Override
public String fetchData() {
if (cachedData == null) {
realDataFetcher = new RealDataFetcher();
cachedData = realDataFetcher.fetchData();
} else {
System.out.println("Returning cached data");
}
return cachedData;
}
}
public class Main {
public static void main(String[] args) {
DataFetcher dataFetcher = new CachingProxyDataFetcher();
System.out.println(dataFetcher.fetchData()); // 실제 데이터베이스에서 데이터 가져옴
System.out.println(dataFetcher.fetchData()); // 캐시된 데이터 반환
}
}
5. 스마트 프록시 (Smart Proxy)
- 목적: 객체에 대한 접근을 제어하면서, 객체에 추가적인 기능을 제공합니다.
- 예시: 객체의 참조 횟수를 관리하는 스마트 프록시를 구현합니다.
interface Resource {
void use();
}
class RealResource implements Resource {
@Override
public void use() {
System.out.println("Using resource");
}
}
class SmartProxyResource implements Resource {
private RealResource realResource;
private int referenceCount = 0;
@Override
public void use() {
if (realResource == null) {
realResource = new RealResource();
}
referenceCount++;
System.out.println("Resource is being used. Reference count: " + referenceCount);
realResource.use();
}
public void release() {
if (referenceCount > 0) {
referenceCount--;
System.out.println("Resource released. Reference count: " + referenceCount);
if (referenceCount == 0) {
realResource = null;
System.out.println("Resource is no longer needed and has been deallocated.");
}
}
}
}
public class Main {
public static void main(String[] args) {
SmartProxyResource resourceProxy = new SmartProxyResource();
resourceProxy.use(); // 참조 횟수 증가
resourceProxy.use(); // 참조 횟수 증가
resourceProxy.release(); // 참조 횟수 감소
resourceProxy.release(); // 참조 횟수 0이 되어 리소스 해제
}
}
프록시 패턴의 장점
- 객체 생성 지연: 가상 프록시를 사용하면, 실제 객체의 생성이 지연되어 메모리 사용량과 성능을 최적화할 수 있습니다.
- 예시: 큰 이미지 파일을 가상 프록시를 통해 필요할 때만 로드하여 메모리 사용을 줄임.
- 접근 제어: 보호 프록시를 통해 객체에 대한 접근을 제어하고, 보안 수준을 높일 수 있습니다.
- 예시: 보호 프록시를 사용하여, 사용자 권한에 따라 문서의 접근을 제한함.
- 추가 기능 제공: 스마트 프록시를 사용하면, 객체에 대한 추가 기능을 제공하거나, 객체의 상태를 관리할 수 있습니다.
- 예시: 스마트 프록시를 통해 객체의 참조 횟수를 관리하여 리소스를 효율적으로 사용함.
- 원격 객체 접근: 원격 프록시를 사용하여, 원격 서버에 있는 객체에 쉽게 접근할 수 있습니다.
- 예시: 원격 프록시를 통해 원격 서버에서 계산 작업을 수행함.
- 성능 향상: 캐싱 프록시를 사용하여 반복적인 요청에 대해 동일한 결과를 반환할 때, 결과를 캐싱하여 성능을 향상시킬 수 있습니다.
- 예시: 캐싱 프록시를 사용하여 데이터베이스에 대한 반복적인 쿼리를 줄임.
프록시 패턴을 사용하면 시스템의 유연성을 높이고 성능을 최적화할
프록시 패턴의 단점
- 복잡성 증가: 프록시 객체를 추가하면 시스템의 복잡성이 증가할 수 있으며, 유지보수가 어려워질 수 있습니다.
- 성능 오버헤드: 프록시를 통해 모든 요청이 처리되므로, 프록시 객체의 성능이 중요한 경우에는 성능 오버헤드가 발생할 수 있습니다.
- 코드 중복: 프록시 객체와 실제 객체 간의 인터페이스가 동일해야 하므로, 일부 코드 중복이 발생할 수 있습니다.
Share article