프록시 패턴 기초
1. Proxy?
프록시는 대리자, 대변인
을 말합니다. 대리자는 누군가의 역할을 대신 수행
하는 것을 말합니다. 한마디로 비서 역할을 한다고 이야기할 수 있습니다. 사장이 직접 행동하는 것이 아닌 비서가 역할을 대신 수행하는 것과 같은 의미입니다.
프로그래밍 또한 마찬가지 입니다. 어떤 객체를 직접 참조(사용)하는 것이 아니라 프록시 객체를 참조하여 대신 역할을 수행합니다.
그렇다면 기존 객체를 참조하는 방식 대신 프록시를 사용할 때의 장점은 무엇일까요? 프록시를 사용의 대표적인 장점은 실제 객체의 기능이 반드시 필요한 시점까지 실제 객체 생성 또는 데이터 호출을 미룰 수 있습니다. 프록시를 사용하는 대표적인 케이스는 JPA입니다. JPA는 Lazy Loading
옵션을이용하여 연관 관계에 있는 데이터를 즉시 DB에서 호출하지 않고, 실제 객체를 상속한 프록시를 호출하고 실제 사용하는 시점에 실제 객체를 로딩해서 사용합니다.
2. code로 확인해보자.
먼저 다이어그램을 통해 살펴보도록 하겠습니다. video interface를 상속받은 RealVideo와 ProxyVideo 구현체를 작성하고 ProxyVideo 객체를 RealVideo 객체를 생성하는 의존성을 갖게 됩니다. 또한 ProxyVideo는 RealVideo을 필드로 인스턴스를 참조합니다.
코드를 통해 확인해보겠습니다. 우선 Video Interface을 선언하고 추상메서드인 displayVideo를 선언하겠습니다.
interface Video {
void displayVideo();
}
ProxyVideo은 displayVideo 메서드를 호출 시점에 RealVideo의 존재 여부를 확인하여 존재하지 않을 경우, RealVideo 객체를 생성하여 RealVideo 객체의 display method를 대신 실행합니다.
class ProxyVideo implements Video {
private String name;
private RealVideo realVideo;
public ProxyVideo(String name) {
this.name = name;
}
@Override
public void displayVideo() {
if (realVideo == null) {
realVideo = new RealVideo(name);
}
realVideo.displayVideo();
}
}
class RealVideo implements Video {
private String name;
public RealVideo(String name) {
this.name = name;
loadVideo();
}
private void loadVideo() {
System.out.println("loading : " + name);
}
@Override
public void displayVideo() {
System.out.println("displaying : " + name);
}
}
ProxyVideo 생성 및 메서드를 호출하여 어떤 결과를 갖는지 확인해보겠습니다. ProxyVideo.displayVideo 메서드의 첫 호출 시점에는 RealVideo 객체가 존재하지 않기 때문에 RealProxy객체를 생성하고 이후에 메서드를 호출하면 RealVideo의 메서드만 대리 호출하는 것을 확인할 수 있습니다.
위와 같이 프록시 패터을 사용하면 메서드 호출 시점에 실제 객체를 호출을 미룰 수 있고 필요한 데이터를 필요한 시점에 값을 불러올 수 있는 역할을 합니다.
class Main {
public static void main(String[] args) {
Video video1 = new ProxyVideo("인터스텔라");
Video video2 = new ProxyVideo("다크나이트");
System.out.println("-----------------------");
video1.displayVideo();
video2.displayVideo();
System.out.println("-----------------------");
System.out.println("-----------------------");
video1.displayVideo();
video2.displayVideo();
System.out.println("-----------------------");
System.out.println("-----------------------");
video1.displayVideo();
System.out.println("-----------------------");
}
}
-----------------------
loading : 인터스텔라
displaying : 인터스텔라
loading : 다크나이트
displaying : 다크나이트
-----------------------
-----------------------
displaying : 인터스텔라
displaying : 다크나이트
-----------------------
-----------------------
displaying : 인터스텔라
-----------------------
3. In JPA
사전에 예시로 들었던 JPA에서도 위와 같은 프록시 패턴을 채택해 사용하고 있습니다. JPA에는 Lazy Loading이라는 것이 존재하는데 이는 필요한 시점에만 엔티티를 생성 또는 호출하여 사용하고 그 이전까지는 Proxy 객체를 참조하여 사용합니다.
프록시 객체를 사용하면 오히려 프록시 객체를 생성하는 비용도 발생하는데 이게 더 비효율적일 수도 있지 않나?라고 생각할 수도 있지 않을까하는 의문이 생겨 프록시 패턴의 사용 이유에 대해 고민해보았습니다. 프록시는 대리자 역할을 하는 객체이므로 많은 정보를 가지기 보다는 실제 객체의 행동을 위임합니다. 프록시 객체가 메서드 호출하는 비용도 존재하지만 실제 객체의 데이터를 로딩할 때의 비효율성을 더 중점적으로 고려하여 도입된 패턴이라고 생각합니다.
4. Further Study
프록시 패턴은 JPA 외에 Spring AOP에도 사용하는 패턴으로 알고 있습니다. 크게 JDK Dynamic Proxy
와 CGLib(Code Generator Library)
를 사용하며 이 내용을 조금 더 학습을 해보고 나서 정리해보도록 하겠습니다.😃
Author And Source
이 문제에 관하여(프록시 패턴 기초), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@pbg0205/프록시-패턴-기초저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)