일반적으로 프록시는 다른 무언가와 이어지는 인터페이스의 역할을 하는 클래스이다. 프록시는 어떠한 것(이를테면 네트워크 연결, 메모리안의 커다란 객체, 파일, 또 복제할 수 없거나 수요가 많은 리소스)과도 인터페이스의 역할을 수행할 수 있다.
프록시 패턴의 잘 알려진 예로는 참조 횟수 스마트 포인터 객체이다.
복합적인 오브젝트들의 다수의 복사본이 존재해야만 하는 상황에서 프록시 패턴은 애플리케이션의 메모리 사용량을 줄이기 뒤해서 플라이웨이트 패턴과 결합된 형태로 나올 수도 있다.
- 원래 객체에 대한 인터페이스 역할을 한다. 클라이언트는 Proxy를 통해 원래 객체에 액세스 한다.
- 클라이언트의 요구를 할 수 있는 만큼 처리하고, 필요할 경우 RealSubject에게 처리를 맡긴다.
- Remote Proxy
- 요청을 처리하고 서비스 객체에 이를 전달하는 역할을 담당한다.
- Virtual Proxy
- 서비스 객체에 대한 정보를 캐싱하여 접근을 연기시킨다.
- Protection Proxy
- 특정 작업을 요청한 객체가 해당 작업을 수행할 권한을 가지고 있는지 확인한다.
- Proxy에서 요청이 들어왔을때 요청에 대한 응답을 처리한다.
- Proxy(대리) 역할과 RealSubject(실제의 주체) 역할을 동일시 하기 위한 프로토콜을 선언한다. 이 덕분에 클라이언트(요청하는 쪽)는 둘의 역할 차이를 몰라도 된다.
import UIKit
enum Auth {
case owner
case guest
}
class Client {
var auth: Auth
init(_ auth: Auth) {
self.auth = auth
}
}
// Subject
protocol YouTubeDowloadSubject {
func downloadYoutubeVideos() async -> [String]
}
// realSubject
final class RealSubject: YouTubeDowloadSubject {
func downloadYoutubeVideos() async -> [String] {
// 유튜브 서버에서 비디오를 다운로드하는 부분
return []
}
}
// proxy
// proxy는 캐싱을 구현하여 요청을 중간에서 컨트롤 할 수 있다.
// 또한 클라이언트의 권한에 따라 제어를 할 수도 있다.
final class Proxy: YouTubeDowloadSubject {
// 진짜 요청을 받아서 처리하는 객체, 정말로 사용할때만 초기화하기 위하여 lazy
private lazy var realSubject = RealSubject()
// 캐싱 구현
private var videoCache = [String]()
// 클라이언트 권한 받음
private var client: Client
init(_ client: Client) {
self.client = client
}
func downloadYoutubeVideos() async -> [String] {
guard client.auth == .owner else {
print("비디오를 다운로드할 권한이 없습니다.")
return []
}
// 비디오 캐시가 비어있으면 실제 realSubject에 데이터 요청
if videoCache.isEmpty {
videoCache = await realSubject.downloadYoutubeVideos()
return videoCache
} else {
// 비디오 캐시에 데이터가 있으면 리턴
return videoCache
}
}
}
let client = Client(.owner)
let proxy = Proxy(client)
func loadYouTubeVideo(_ service: YouTubeDowloadSubject) {
service.downloadYoutubeVideos()
}
loadYouTubeVideo(proxy)
protocol Image {
func displayImage()
}
class RealImage: Image {
private let filename: String
init(filename: String) {
self.filename = filename
loadImageFromDisk()
}
func loadImageFromDisk() {
print("Loading Image: \(filename)")
}
func displayImage() {
print("Display Image: \(filename)")
}
}
class ProxyImage: Image {
private let filename: String
private var realImage: RealImage?
init(filename: String) {
self.filename = filename
}
func displayImage() {
if realImage == nil {
realImage = RealImage(filename: filename)
}
realImage?.displayImage()
}
}
let image1: Image = ProxyImage(filename: "image1.jpg")
image1.displayImage() // 이미지를 로드하고 표시합니다.
let image2: Image = ProxyImage(filename: "image2.jpg")
image2.displayImage() // 이미지를 로드하고 표시합니다.
image1.displayImage() // 이미 로드된 이미지를 표시합니다.
제가 학습한 내용을 요약하여 정리한 것입니다. 내용에 오류가 있을 수 있으며, 어떠한 피드백도 감사히 받겠습니다.
감사합니다