프록시 패턴
- 다른 객체에 대한 대리자(Proxy) 또는 대변자 역할을 하는 객체를 제공하여 해당 객체에 대한 접근을 제어하거나 보완하는 데 사용
- 프록시는 실제 객체에 대한 인터페이스 역할을 하면서, 실제 객체에 대한 접근을 제어하거나 필요한 추가 기능을 제공할 수 있다.
- 객체의 생성 시점을 지연시키거나, 접근 제어, 캐싱, 로깅, 인증, 네트워크 통신 등의 기능을 추가할 수 있다.
활용성
- 원격지 프록시
- 서로 다른 주소 공간에 존재하는 객체를 가리키는 대표 객체
- 가상 프록시
- 보호용 프록시
- 원래 객체에 대한 실제 접근을 제어
- 객체별로 접근 제어 권한이 다를 때 유용
구조
요소
- Proxy
- 실제로 참조할 대상에 대한 참조자 관리
- Service와 동일한 인터페이스를 제공하여 대체 될 수 있어야함
- 실제 대상에 대한 접근을 제어하고 실제 대상의 생성과 삭제를 책임진다.
- 원격지 프록시
- 요청 메시지와 인자를 인코딩하여 이를 다른 주소 공간에 있는 실제 대상에게 전달
- 가상의 프록시
- 실제 대상에 대한 추가적 정보를 보유하여 실제 접근을 지연할 수 있어야 함.
- 보호용 프록시
- 요청한 대상이 실제 요청할 수 있는 권한이 있는지 확인
- Service
- RealService와 Proxy에 공통적인 인터페이스를 정의하여, RealService가 요청되는 곳에 Proxy를 사용할 수 있게 함.
- RealService
협력 방법
- 프록시는 자신이 받은 요청을 RealService 객체에 전달.
특징
- 원격지 프록시는 객체가 다른 주소 공간에 존재한다는 사실을 숨길 수 있다.
- 가상 프록시는 요구에 따라 객체를 생성하는 등 처리를 최적화할 수 있다.
- 보호용 프록시는 객체가 접근할 때마다 추가 관리를 책임, 객체를 생성할 것인지 삭제할 것인지를 관리
- 프록시 패턴이 사용자에게 숨길 수 있는 또 다른 최적화는 copy-on-write (요구가 들어올 때만 객체를 생성하는 개념) → 프록시를 사용해서 복사 절차를 미루고 사본이 수정될 때만 실제 복사
- 개방/폐쇄 원칙
단점
- 서비스의 응답이 늦어질 수 있다.
- 코드가 복잡해짐.
예시 코드
import Foundation
protocol DataService {
func fetchData() -> String
}
class RealDataService: DataService {
func fetchData() -> String {
return "실제 데이터를 가져옴"
}
}
class DataProxy: DataService {
private var realService: RealDataService
init(_ realService: RealDataService) {
self.realService = realService
}
func fetchData() -> String {
if checkAccess() {
let data = realService.fetchData()
logAccess()
return data
} else {
return "접근이 거부되었습니다."
}
}
private func checkAccess() -> Bool {
return true
}
private func logAccess() {
print("Proxy: Logging the time of request.")
}
}
class Client {
static func clientCode(service: DataService) {
let data = service.fetchData()
print("Client: Data received - \(data)")
}
}
print("Client: Executing the client code with a real service:")
let realService = RealDataService()
Client.clientCode(service: realService)
print("\nClient: Executing the same client code with a proxy:")
let proxy = DataProxy(realService)
Client.clientCode(service: proxy)
protocol NetworkService {
func fetchData(url: URL) -> String
}
class RealNetworkService: NetworkService {
func fetchData(url: URL) -> String {
return "실제로 \(url)에서 데이터를 가져옴"
}
}
class NetworkServiceCacheProxy: NetworkService {
private let realService = RealNetworkService()
private var cache = [URL: String]()
func fetchData(url: URL) -> String {
if let cachedData = cache[url] {
print("Proxy: Returning cached data for URL - \(url)")
return cachedData
} else {
print("Proxy: Requested URL - \(url), fetching data...")
let newData = realService.fetchData(url: url)
cache[url] = newData
return newData
}
}
}
let client = NetworkServiceCacheProxy()
let url = URL(string: "https://www.example.com/api/data")!
let data1 = client.fetchData(url: url)
print(data1)
let data2 = client.fetchData(url: url)
print(data2)
참고