프록시 패턴

bp.chys·2021년 12월 20일
0

TIL(Today I Learned)

목록 보기
10/11

프록시라는 말이 나오면 '대리자'를 떠올려 보자.
어떤 서버에 직접 요청을 보내는 것이아니라 대리자를 통해 요청을 가져와달라고 간접 요청을 보낼 수 가 있다.

이 순서가 중요한 이유는 proxy 객체가 개발중간에 추가되거나 빠져도 클라이언트와 원본 객체에는 아무런 수정이 필요하지 않다는 점에 있다.
이렇게 중간에서 대리자 역할을 하는 프록시 객체는 다음과 같은 기능을 할 수 있다.

  1. 접근 제어
    • 권한에 따른 접근 차단 캐싱
    • 지연 로딩
  2. 부가 기능 추가
    • 원래 서버가 제공하는 기능에 더해서 부가 기능을 수행한다.
      예) 요청 값이나, 응답 값을 중간에 변형한다.
      예) 실행 시간을 측정해서 추가 로그를 남긴다.

GOF 디자인 패턴에서는 이 둘을 의도에 따라서 프록시 패턴과, 데코레이터 패턴으로 구분하여 소개한다.

  • 프록시 패턴 : 접근 제어가 목적
  • 데코레이터 패턴 : 새로운 기능 추가가 목적

그럼 접근 제어를 주 목적으로 하는 프록시 패턴에 대한 간단한 예제를 살펴보자.

예제

interface Subject {
    fun operation(): String
}

class RealSubject : Subject {

    override fun operation(): String {
        log.info("실제 객체 호출")
        sleep(1000)
        return ""
    }

    private fun sleep(millis: Int) {
        try {
            Thread.sleep(millis.toLong())
        } catch (e: InterruptedException) {
            e.printStackTrace()
        }
    }
}

class CacheProxy(
    private val target: Subject,
    private var cacheValue: String? = null,
) : Subject {

    override fun operation(): String {
        log.info("프록시 호출")
        if (cacheValue == null) {
            cacheValue = target.operation()
        }
        return cacheValue!!
    }
}


class ProxyPatternClient(
    private val subject: Subject,
) {
    fun execute() {
        subject.operation()
    }
}

---
@Test
fun nonProxyTest() {
    val realSubject = RealSubject()
    val client = ProxyPatternClient(realSubject)

    client.execute() // 프록시 호출 + 실제 객체 호출
    client.execute() // 프록시 호출
    client.execute() // 프록시 호출
}


@Test
fun proxyTest() {
    val realSubject = RealSubject()
    val cacheProxy = CacheProxy(realSubject)
    val client = ProxyPatternClient(cacheProxy)

    client.execute() // 프록시 호출 + 실제 객체 호출
    client.execute() // 프록시 호출
    client.execute() // 프록시 호출
}

위와 같은 예제 코드에서 프록시 객체를 의존하는 ProxyPatternClient 인스턴스를 생성하여 execute 함수를 여러번 호출 해보자.
처음에는 cacheValue가 null 이기 때문에 실제 객체를 호출하여 cacheValue를 생성하고 그 이후로는 실제 객체 호출 없이 프록시에서 값 반환을 끝내고 있다. 즉, 요청에 대한 실제 객체로의 접근을 제거하는 모습을 확인할 수 있다.

결론

프록시 객체의 역할과 GOF에서 소개하는 프록시 패턴의 구분된 의미에 대해서 학습했다.
중요한 점은 프록시를 사용했을 때와 사용하지 않았을 때 클라이언트 코드가 전혀 변하지 않았다는 점이고, 유지보수 뿐만아니라 성능적인 이점을 확보하기 위해서도 프록시 객체가 유용하게 사용된다는 점을 이해할 수 있다.

profile
하루에 한걸음씩, 꾸준히

0개의 댓글