clamp
Clamp
clamp
글쓰기 관리
전체 방문자
오늘
어제
  • 분류 전체보기 (509)
    • IOS (85)
    • SwiftUI+TCA+Combine (9)
    • RxSwift + MVVM (56)
    • Clean Architecture (12)
    • SWIFT (56)
    • iOS - TDD (2)
    • 디자인패턴 (4)
    • CS (56)
      • 알고리즘 (29)
      • 운영체제 (15)
      • 자료구조 (2)
      • 네트워킹 (4)
      • 기타 (6)
    • 회고 (0)
    • Firebase (18)
    • SwiftUI (10)
    • iOS - UIKit (11)
    • iOS - 오픈소스 (6)
    • 코딩테스트 (166)
      • 프로그래머스 (164)
    • 정보처리기사 (14)
    • GitHub (2)
글쓰기 / 관리자

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • Q
  • Swift
  • ㅅ
  • uikit

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
clamp

Clamp

[iOS] Decorator 패턴을 활용한 에러 처리
IOS

[iOS] Decorator 패턴을 활용한 에러 처리

2024. 11. 10. 20:13

디자인 패턴중 구조패턴인 데코레이터 패턴을 활용한 에러처리 방법입니다.

 

데코레이터 패턴

데코레이터 패턴은 디자인패턴중 구조패턴에 속하는 패턴입니다.

Wrapper 패턴이라고도 불립니다.

 

데코레이터 패턴은 새로운 행동을 정의한 Wrapper내부에 Wrapper를 넣어서 행동들을 해당 겍체들이 담당하도록 하는 구조 패턴입니다.

 

 

마트료시카라는 러시아 인형을 예시로 들면 적합할것 같습니다.

이 인형 안에 인형이, 그 인형 안에 또 인형이 들어있는 구조입니다

이 인형 하나가 Wrapper가 되고 그 Wrapper가 또 Wrapper를 가지고 있는 구조입니다. 각자의 Wrapper는 각자의 역할을 담당합니다.

 

데코레이터 패턴에 대해서는 좋은 설명이 많은 글이 많으니 깊게 설명하지는 않겠습니다.

 

데코레이터 패턴의 특징은 두 가지입니다.

1. "동적"으로 런타임에 기능을 변경하고 추가할 수 있습니다.

2. 기존 객체를 건들지 않는 방식으로 기능을 추가할 수 있습니다.

 

1. 핵심 기능을 담당하는 Component Interface(Protocol)

public protocol ErrorHandler {
    func handleError(error: Error)
}

 

2. 이를 채택해서 구현하는 ConcreteComponent 타입을 구현합니다.

class ConcreteErrorHandler: ErrorHandler {
    
    func handleError(error: Error) {
        print(error)
    }
}

여기서 Error를 프린트하는 Concrete 컴포넌트를 작성했습니다.

근데 만약 에러 발생시 UI변경이 필요한 상황이 생기면 어떻게 할까요

UI업데이트를할 데코레이터를 추가해주면 됩니다.

 

3. 데코레이터 구현

class UpdateUIErrorHandlerDecorator: ErrorHandler {
    
    private var wrapped: ErrorHandler
    private var updateUI: (() -> Void)
    
    init(
        wrapped: ErrorHandler,
        updateUI: @escaping (() -> ())
    ) {
        self.wrapped = wrapped
        self.updateUI = updateUI
    }
    
    func handleError(error: Error) {
        wrapped.handleError(error: error)
        DispatchQueue.main.async { [weak self] in
            self?.updateUI()
        }
    }
}

 

이제 Concrete를 꾸며줄 데코레이터들을 추가하면 됩니다.

이 데코레이터들은 Wrapper들을 소유합니다. 

데코레이터를 보면 wrapped라는 Component Interface인 ErrorHandler라는 타입을 소유합니다.

생성자에서는 updateUI라는 클로저를 받는데 이 데코레이터를 사용하는 뷰 혹은 뷰모델에서 업데이트할 UI를 정의해주면 됩니다.

 

만약 뷰 모델에서 통신에 실패하고 서버에서 받은 에러를 뷰에 보여줘야 할 경우 이를 어떻게 사용할 수 있을까요

 

에러가 발생했을 때 광고를 숨겨야 한다고 가정해봅시다.

open class ViewModel: ObservableObject {
    @Published var isAdShowing: Bool = true
    @Published var isShowingError: Bool = false
    @Published var errorMessage: String = ""
    
    private var errorHandler: ErrorHandler?
    
    init() {
        let concreteErrorHandler: ErrorHandler = ConcreateErrorHandler()
        let updateUIDecorator: ErrorHandler = UpdateUIErrorHandlerDecorator(
            wrapped: concreteErrorHandler,
            updateUI: { [weak self] in
                self?.isAdShowing = false
            }
        )
        self.errorHandler = updateUIDecorator
    }
}

 

 

근데 만약 런타임에 에러 처리를 바꿔야 한다면? UI업데이트가 사라져야 한다면?

그 시점에 errorHandler를 변경해주면 됩니다.

// 어떤 시점에 
        let concreteErrorHandler: ErrorHandler = ConcreateErrorHandler()
        self.errorHandler = concreteErrorHandler

 

 

어떤 타입의 에러를 처리하는 데코레이터, 에러 발생 로그를 서버로 보내는 데코레이터 등  필요한 기능을 조합할 수도, 런타임에 변경할 수 있으니 더욱 유연한 에러처리 방법이 될 수 있습니다.

에러 처리 로직을 하나의 객체가 담당하게 하므로 불필요한 보일러플레이트 코드를 줄일 수 있게 되겠죠.

 

Solid 원칙중 하나인 단일 책임 원칙 SRP(Single Responsibility Principle)을 준수하게 됩니다.

 


혹시 틀린점이나 부족한 점이 있으면 알려주시면 감사하겠습니다.

저작자표시 비영리 동일조건 (새창열림)
    'IOS' 카테고리의 다른 글
    • [Xcode] SPM Package 내부에서 CustomFont 사용하기
    • [iOS] DynamicFramework
    • [iOS Swift ] AES-256 암호화
    • [iOS] Alamofire 네트워크 레이어 설계2. 토큰관리(Interceptor)
    clamp
    clamp
    주니어 iOS개발자의 발악!!!!!!!

    티스토리툴바