본문 바로가기

컴퓨터공학

프록시 패턴 (Proxy Pattern)

스프링이나 JPA를 공부하다보니 프록시(Proxy)라는 개념이 자주 등장한다. 프록시란 진짜 객체를 모방한 가짜객체 정도로 알고 있었는데, 근본적으로 왜 프록시를 사용하고, 프록시가 어떤 부분에 이점이 있는지 알아보자.

 


프록시 패턴

대리인, 대리자라는 뜻을 가진 Proxy라는 단어에서 알 수 있듯이, 프록시 패턴은 진짜 객체의 역할을 대신하는 가짜 객체, 프록시를 이용한다. 그렇다면 굳이 진짜 객체를 두고 가짜 객체를 생성하는 이유는 무엇일까? 이는 프로세스의 실행시간이 실행이 가장 오래걸리는 리소스(혹은 객체)에 의존하기 때문이다. 

 

예를 들어 대부분의 객체는 처리시간이 1초 미만이지만, 아주 큰 객체의 처리시간이 10초가 걸린다고 해보자. 그러면 사용자는 프로그램을 실행하는데 10초가 걸린다. 이는 너무 비효율적이다. 이것보다는 일단 실행을 하고, 실행하는데 오래걸리는 무거운 객체의 생성은 뒤로 미루는 것이 낫다.

 

객체의 생성을 뒤로 미루기위해서는 마치 해당 객체가 있는것처럼 대신 역할을 해줄 객체가 필요한데 이 역할을 프록시 객체가 수행하는 것이다. 그리고 프록시 객체는 진짜 객체가 필요한 시점에 객체를 생성하여 작업을 분산시키는 역할을 해준다. 

 

예를 들어보자. 네이버 주소를 입력하여 메인 창에 접속하면 일단 텍스트들이 먼저 화면에 표시되고, 동영상은 나중에 표시되는 것을 볼 수 있다. 이는 프록시 객체를 통해 처리 속도가 느린 동영상등은 프록시 객체를 생성하여 참조하고, 화면은 먼저 띄운뒤 로딩이 완료되면 동영상이 표시되는 것이다.

일단 리소스가 가벼운 텍스트/이미지를 화면에 표시한뒤, 리소스가 무거운 동영상을 나중에 표시한다.

만약 프록시를 사용하지 않는다면 사용자는 가장 무거운 리소스인 동영상이 로딩될때 까지 빈페이지에서 기다려야 한다. 


프록시 패턴의 구조

 

Client가 RealObject에 관한 메서드(DoWork())를 실행하면 RealObject를 통해 메서드를호출하는것이 아닌 미리 생성해둔 Proxcy 객체를 통해 메서드를 호출하게 된다. Proxy객체의 메서드가 실행되면 RealObject가 생성되고, RealObject의 메서드가 호출된다.

또한 프록시 객체와 진짜 객체 모두 인터페이스를 통해 구현하여 클라이언트는 인터페이스에만 접근하므로 호출하는 객체가 프록시 객체인지 진짜 객체인지 알 수 없다. 이를 통해 객체지향의 DIP 원칙을 위배하지 않는다.

 


 

프록시 패턴 예제

public interface vod {	
    public void doWork();
}

public class RealVod {
    private Long id;
    private string title;
    
    @Override
    public void doWork() {
    	...
    }
}

public class Proxy {
	private Long id;
    private string title;
    
    @Override
    public void doWork() {
    	RealVod realVod = new RealVod();
       	realVod.doWork();
    }
}
public class MainClass {
	public static void main(String[] args) {
    	
        Vod vod = new Proxy();
        
        vod.doWork();
    }
}

MainClass를 보면 클라이언트는 프록시 객체를 생성한다. 물론 인터페이스를 통해 구현체를 생성하므로 클라이언트는 프록시인지 알 수 없다. 또한 이 시점에는 아직 RealVod를 생성하지 않는다. 그리고 실제 클라이언트가 메서드를 호출하면 그 시점에서야 프록시 객체의 메서드를 호출하여 실제 객체를 생성하게 된다.


 

프록시 패턴 in JPA

Member 엔티티와 Team 엔티티가 일대다 연관관계로 매핑이 되어있다. 이때 jpa 메서드를 통해 Member를 조회하면 어떻게 될까?

em.find(1L, Member.class);

Member는 Team 객체를 참조하고 있으므로 JPA는 자동으로 조인쿼리를 통해 Team 엔티티의 정보까지 조회하게 한다. 그런데 상황에 따라 지금 당장 Team 엔티티가 필요가 없을수도 있다. 또한 Team 엔티티를 조회하는 쿼리가 너무 크거나, 느려서 JPA의 성능을 저해할 수 있다. 그렇기 때문에서 JPA는 fetch 전략을 통해 연관엔티티의 조회를 뒤로 미룰수 있는 기능을 제공하데 이것이 바로 지연로딩(Lazy Loading)이다.

@ManyToOne(fetch = LAZY)
@JoinColumn(name = "TEAM_ID")
private Team team;

위와 같이 fecth 설정을 통해 설정할 수 있다. 자세한건 나중에 포스팅을 통해 다루겠다.

 

이때 사용하는 것이 바로 프록시이다. JPA는 진짜 엔티티의 생성을 뒤로 미루기위해 일단 가짜 엔티티를 생성해 놓는다. 이때 이 가짜 엔티티가 바로 프록시이다.

Member member = em.find(Member.class, 1L);
Team team = member.getTeam()  		// team -> proxy 객체
team.getName(); 			// 실제 객체를 생성하여 getTeam() 메서드 실행

지연로딩을 설정할 경우 Member를 조회하면 일단 team객체를 프록시로 만들어놓는다. 그후 team 객체의 메서드를 실행하면 그제서야 실제 객체를 생성하여 메서드를 실행한다.

 


 

'컴퓨터공학' 카테고리의 다른 글

CORS 정책에 의한 예외 처리하기  (0) 2023.02.28
Cascade에 대하여...  (0) 2022.11.22
[Network] Application 계층 - Socket Programming  (0) 2022.09.28
TCP에 대하여  (0) 2022.01.06