jay's devnote
  • README
  • 자료구조 & 알고리즘
    • 자료구조
    • 알고리즘
  • 디자인 패턴
    • 디자인 패턴 원칙, 요약, 분류
    • IS-A 와 HAS-A
    • 전략 패턴
    • 옵저버 패턴
    • 데코레이터 패턴
    • 팩토리 패턴
    • 싱글턴 패턴
    • 커맨드 패턴
    • 어댑터, 퍼사드 패턴
    • 템플릿 메소드 패턴
    • 반복자, 컴포지트 패턴
    • 상태 패턴
    • 프록시 패턴
    • 복합 패턴
  • WWDC
    • 2015, Building Better Apps with Value Types in Swift
    • 2015, Protocol-Oriented Programming in Swift
    • 2016, Understanding Swift Performance
    • 2016, Protocol and Value Oriented Programming in UIKit Apps
    • 2017, Engineering for Testability
    • 2018, High Performance Auto Layout
    • 2018, Testing Tips & Tricks
    • 2020, Advances in UICollectionView
    • 2020, Lists in UICollectionView
  • 패러다임
    • 객체지향 프로그래밍, SOLID 원칙
      • SRP, 단일 책임 원칙
      • OCP, 개방 폐쇄 원칙
      • LSP, 리스코프 치환 원칙
      • ISP, 인터페이스 분리 원칙
      • DIP, 의존성 역전 원칙
    • 만들면서 느껴보는 POP
    • Swift로 함수형 프로그래밍 시작하기
  • 아키텍쳐
    • ReactorKit
      • Pulse(EN)
      • Pulse(KR)
    • Coordinator Pattern
  • iOS
    • Safari로 웹뷰의 세션/쿠키 정보 확인하기
    • App Icon 동적으로 변경하기
    • WKDataDetectorTypes의 데이터 탐지
    • Xcode에서 메모리 누수 확인하기
    • 개발 인증서 관리하기
    • required init?(coder: NSCoder)
    • UIFontMetrics 와 UIFont.preferredFont
    • 제약조건을 줄여주는 UIStackView
    • UICollectionView.CellRegistration<Cell, Item>
  • Swift
    • Swift API Design Guidelines
    • 패턴 매칭
    • allSatisfy()
    • 생성자
    • 프로토콜의 동적 디스패치와 정적 디스패치
    • Swift 문법 정리
  • RxSwift
    • RxSwift 핸드북
    • Just, From, Of
    • withLatestFrom
  • SwiftUI
    • SwiftUI에서의 마크다운 문법
    • @State, @Binding
    • ObservableObject, @ObservedObject, @Published
    • @ObservedObject vs @StateObject
  • Git
    • Git gitignore
    • Github API Rate limit
    • GitKraken(깃크라켄) 활용하기
    • GitKraken으로 Git-flow 활용하기
  • Etc
    • Struct을 [String: Any]로 변환할 때, Encodable의 Extension을 사용 해야 하나요?
    • Podfile, Dependency Rule(SPM)
    • 맥으로 고정 IP 연결하는 방법
    • SwiftPlantUML으로 UML 다이어그램 쉽게 그리기
    • Playground 가 열리지 않는 오류 해결하기
    • CocoaPods 제거하기
  • Python
    • 파이썬과 스위프트 문법 비교
    • 파이썬과 스위프트 문법 요약
  • Firebase
    • Storage를 API처럼 사용해보기
    • RealTime Database를 API처럼 사용해보기
Powered by GitBook
On this page
  • 1. 프록시 패턴(Proxy Pattern)
  • 2. 앨범 커버 뷰어 설계하기
  • 3. 앨범 커버 뷰어 구현하기
  • 4. 생각해보기
  • 5. 다양한 프록시
  • Reference
  1. 디자인 패턴

프록시 패턴

Previous상태 패턴Next복합 패턴

Last updated 2 years ago

  • 특정 객체로의 접근을 제어하는 대리인(특정 객체를 대변하는 객체)을 제공합니다.

1. 프록시 패턴(Proxy Pattern)

  • 프록시는 대리인이라는 의미입니다. 변형이 정말 많은데, 결론적으로는 실제 작업을 처리하는 클래스가 오픈되지 않고 프록시라는 대리인을 통해 작업을 처리하는 방식이라고 볼 수 있어요.

  • RealSubject와 Proxy는 둘 다 Subject라는 인터페이스를 준수합니다. 그러면, 어떤 클라이언트에서든 Proxy를 RealSubject처럼 대할 수 있어요.

  • Proxy에는 진짜 작업을 처리하는 RealSubject의 레퍼런스가 들어있습니다. 진짜 객체가 필요하면 이 레퍼런스를 통해서 요청을 전달합니다.

  • RealSubject는 진짜 작업을 처리합니다. Proxy는 RealSubject로는 접근을 제어하는 대리인 역할을 하지요.

2. 앨범 커버 뷰어 설계하기

  • 커버 뷰 중에 하나를 선택합니다. 그러면 네트워크 API를 통해 이미지를 가져오고 그걸 화면에 보여주는 앨범 커버 뷰어를 만들려고 합니다.

  • 그런데, 이런 방법을 사용하면 네트워크 상태와 인터넷 연결 속도에 따라서 앨범 커버를 가져오는데 시간이 걸릴 수 있죠. 그래서 이미지를 불러오는 동안 화면에 다른 것을 보여주면 좋겠어요.

  • 또, 이미지를 기다리는 동안 애플리케이션 전체가 작동을 멈춰서도 안됩니다. 비동기 작업으로 처리해야 된다는 거겠죠.

  • 여기서 프록시가 백그라운드에서 이미지를 불러오는 작업을 처리하고, 이미지를 완전히 가져오기 전까지 다른 화면을 보여줍니다. 그리고 이미지를 다 가져오면 RealSubject 객체에게 이제 화면에 그려라!고 작업을 위임하게 되죠.

3. 앨범 커버 뷰어 구현하기

  • 예제가 마땅치 않아서 Swift로 하나 그냥 만들어보았습니다.

  • 음, 일단 Subject 인터페이스를 하나 만듭니다. 그리고 ImageProxy에서 Subject 인터페이스를 준수합니다.

  • ImageProxy는 위에 나와있는 것처럼 이미지 로딩뷰와 실제 작업을 진행하는 UIImageView를 가지고 있습니다. 또 백그라운드에서 이미지를 가져오는 메소드도 있어요.

  • 그래서, url을 생성자로 받아서 draw()를 하게되면 일단은 이미지 로딩뷰를 띄워주다가 이미지가 네트워크를 통해 다운로드가 되면 다운로드 된 이미지를 실제 일을 하는 UIImageView.image = downloadedImage 해줘서 이미지를 바꿔주는 작업을 하지요.

  • 다만 여기서는 ViewController 특성상 이미지로딩뷰와 UIImageView를 먼저 addSubView 해주었습니다.

  • 어떻게 보면 간단하죠? 위에서 계속 설명한대로 프록시는 말그대로 대리인 역할을 하는겁니다. 그리고 UIImageView에 대한 접근 제어의 역할도 하고 있죠.

protocol Subject {
    func draw()
}
    
class ImageProxy: Subject {
    let url: URL
    
    init(url: URL) {
        self.url = url
    }
    
    let realView: UIView = {
        let imageView = UIImageView()
        imageView.contentMode = .scaleAspectFit
        imageView.backgroundColor = .orange
        imageView.frame = CGRect(x: 0, y: 0, width: 0, height: 0)
        return imageView
    }()
    
    let placeholderView: UIView = {
        let imageNotFoundLabel = UILabel()
        imageNotFoundLabel.numberOfLines = 0
        imageNotFoundLabel.textAlignment = .center
        imageNotFoundLabel.text = "이미지를 로드하고 있습니다. 잠시만 기다려주세요."
        imageNotFoundLabel.textColor = .black
        imageNotFoundLabel.backgroundColor = .lightGray
        imageNotFoundLabel.frame = CGRect(x: 0, y: 100, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height/2)
        return imageNotFoundLabel
    }()
    
    func draw() {
        loadImage()
    }
    
    func loadImage() {
        guard let data = try? Data(contentsOf: url),
              let image = UIImage(data: data),
              let imageView = realView as? UIImageView else { return }
        
        DispatchQueue.global().async {
            DispatchQueue.main.async {
                imageView.frame = CGRect(x: 0, y: 100, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height/2)
                imageView.image = image
            }
        }
    }
}
    
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        let imageProxy = ImageProxy(url: URL(string: "https://picsum.photos/5000")!)
        
        view.addSubview(imageProxy.placeholderView)
        view.addSubview(imageProxy.realView)
        
        imageProxy.draw()
    }
}

4. 생각해보기

  • 프록시를 데코레이터와 헷갈리기도 합니다. 데코레이터는 클래스에 새로운 행동을 추가하는 용도로 쓰이죠. 하지만 프록시는 어떤 클래스로의 접근을 제어하는 용도로 쓰입니다. 프록시가 UIImageView로의 접근을 제어하고 있는 것이죠.

  • 어댑터와도 비슷해보일 수 있습니다. 프록시와 어댑터 모두 클라이언트와 다른 객체 사이에서 클라이언트의 요청을 다른 객체에게 전달하는 역할을 합니다. 어댑터는 객체의 인터페이스를 바꿔주지만, 프록시는 똑같은 인터페이스를 사용한다는 차이점이 있죠.

5. 다양한 프록시

  • 프록시는 변형이 아주 많습니다. 기본적으로 원격 프록시, 가상 프록시, 보호 프록시가 있어요.

  • 종류는 많지만 결국에 구조는 유사합니다. 서서히 차근차근 공부해서 익숙해지는게 좋겠습니다.

Reference

https://www.hanbit.co.kr/store/books/look.php?p_code=B6113501223