본문 바로가기
iOS📱/Swift

[ RxSwift ] Observable & Subscribe

by 텅빈비니 2023. 5. 9.
반응형

 

안녕하세요🐶

빈지식 채우기의 비니🙋🏻‍♂️ 입니다.

 

실제 업무를 진행하면서 RxSwift에 대해 접해볼 기회가 없었습니다. 

다만 요즘 트랜드를 보면 RxSwift 사용이 점차 많아지는 것을 확인할 수 있었고

"개념이라도 알아야 실제 써먹던가 하지..." 해서 공부를 하게 되었습니다.

 

이번 포스팅 부터는 RxSwift 에 대해 제가 공부한 것들을 차근 차근 포스팅 하려고 합니당!

 

1. 개요

한가지 샘플 코드를 소개하면서 포스팅을 시작하도록 하겠습니다!

//URL을 통한 데이터 수신 로직
func downloadJson(urlString: String, completion: @escaping (String) -> Void) {
    guard let url = URL(string: urlString) else { return }
    URLSession.shared.dataTask(with: url) { data, response, error in
        guard data != nil || error == nil else { return }   // data가 없거나 error가 있을 시 return

        guard let text = String(data: data!, encoding: .utf8) else { return }
        completion(text)    // 완성된 데이터를 Completion 통해 호출부에 전달

    }.resume()
}
  1. 위와 같이 데이터 가공이 완료되면 보통 Completion 클로저 또는 Delegate, Notification을 통해 데이터를 전달
  2. 전달 받은 데이터를 통해 UI 변경 로직 실행
downloadJson(urlString: "") { text in
    DispatchQueue.main.async {  // UI 로직은 항상 메인 스레드에서 진행
        self.tv.text = text
    }
}

이렇게 Completion을 통해 전달 받은 데이터를 인지하고, 메인 스레드를 통해 UI를 변경을 하고 있습니다.

 

하지만!

이렇게 데이터를 요청받고, UI를 업데이트 하는 일련의 과정을 수행하지 않고,

Property Observer 기능 처럼 어떤 데이터를 관찰하고 변경될 때를 캐치하면 얼마나 좋을까..? 라는 생각을 할 수 있습니다.

 

바로 이런 생각의 프로그래밍이 Reactive Programming 즉 반응형 프로그래밍이라고 합니다.
그리고 이것을 Swift에서 쉽게 하기 위해 지원하는 것이 바로바로바로~~~~~~~~ RxSwift 입니다. 

즉 데이터를 Completion을 통해 데이터 전달하는 것이 아니라, 데이터 변화에 관찰하게 하면 
알아서 변화를 감지해 진행한다는 겁니다.

여기서 관찰이 RxSwift에서 가장 중요한 요소인 Observer 입니다.

 

1-1. RxSwift는 정확히 무엇일까요?

 

GitHub - ReactiveX/RxSwift: Reactive Programming in Swift

Reactive Programming in Swift. Contribute to ReactiveX/RxSwift development by creating an account on GitHub.

github.com

RxSwift의 정의

위 글에 대해 해석해보면 다음과 같습니다.

Rx는 Observable<Element> 라는 인터페이스로 표현되는 Computation의 Generic 추상화이다. 
이를 통해서 Observable(관찰가능한) 흐름으로부터 값이나 다른 이벤트를 구독하고 송출할 수 있게 해준다.

결국 일일히 데이터를 전달하는 것이 아닌 알아서 관찰하고 알아서 적용한다~~~ 라는 뜻으로 간단히 이해하면 될 것 같습니당 ㅎㅎ

 

2. Observable

Observable에 관해서 간단히 아래와 같이 설명할 수 있습니다.

1. 이벤트를 시간 흐름에 따라 전달하는 "전달자" 역할
2. 비동기로 동작하는 일련의 항목들을 나타내는 "시퀀스"
3. Observable은 3가지 타입의 이벤트를 배출하고, ObserverObservable을 "구독"하여 이벤트를 수신 합니다.
4. next : next는 다음 데이터를 배출하는 이벤트, 그 데이터를 Observer가 수신
5. completed : completed는 시퀀스를 성공적으로 종료하고 더이상 이벤트를 배출하지 않음
6. error : error는 오류가 발생하여 시퀀스를 종료하고, 더이상 이벤트를 배출하지 않음

아까 위에서 말씀드린 것처럼 특정 데이터를 "관찰" 한다고 했습니다. 말 그대로 Observable은 "관찰가능한" 기능을 가졌습니다.

위에서 들었던 샘플 코드에 Observable를 적용시켜 보겠습니다.

// Observable을 이용한 통신 구현
func downloadJson(urlString: String) -> Observable<String?> {
    // 비동기로 생기는 데이터를 Observable로 감싸서 리턴
    return Observable.create { emitter in	// (1)
        let url = URL(string: urlString)
        
        let task = URLSession.shared.dataTask(with: url!) { data, response, error in
            guard error == nil else {
                emitter.onError(error!) // (2)
                return
            }
            
            guard data != nil else { return }
            
            guard let text = String(data: data!, encoding: .utf8) else { return }

            // 실제 데이터 전달
            emitter.onNext(text) // (3)
            
            // 완료 처리
            emitter.onCompleted() // (4)
        }
        task.resume()
        
        // 취소되었을때 어떻게 처리할건지 전달
        return Disposables.create() {	// (5)
            task.cancel()
        }
    }
}
  1. Observable.create 을 통해 Observable 객체를 만들고 반환
  2. error 발생 시 Observable을 통해 onError 호출
  3. Observable을 통해 onNext를 호출하여 데이터를 전달
  4. 통신 및 로직 완료를 위해 Observable의 onCompleted를 호출
  5. 통신 및 로직 취소가 있을 시 Disposables.create 을 통해 어떻게 처리할건지 전달

(5)번 로직을 보면 Disposable을 반환하도록 되어 있습니다.

이건 만약에 이 Observable이 취소가 되었을 때 어떻게 동작할건지를 전달하는 것입니다.

즉 위의 예시에서는 통신 Task 취소로 볼 수 있습니다.

 

2-1. Subscribe

한가지 예시를 들어보도록 하겠습니다.

1. 나는 특정 컨텐츠를 좋아하기 때문에 유투브를 시청하고 자주보는 유투버(Observable)가 있다.
2. 매번 새로운 영상이 올라왔나 확인하기 위해 앱을 들어가고 있다.
3. 이러한 행동에 귀찮음을 느낀 나는 그 유투버(Observable)구독(Subscribe)를 하였고 알림 등록도 하였다. 
4. 구독(Subscribe)를 하였으니 새로운 영상이 올라오면 알림을 통해 나한테 알려준다.

RxSwift에서 존재하는 Subscribe 개념도 위와 같은 예시와 동일하다.

Observable(관찰가능한) 대상을 생성하였으니, 이 데이터를 관찰하기 위해 우리는 구독을 하는데 이것이 subscribe이다.

downloadJson(urlString: "")
.debug()    // (1)
.subscribe { event in // (2)
    switch event {
    case .next(let text): // (3)
        DispatchQueue.main.async {
            self.contentsTextView.text = text
            self.setVisibleWithAnimation(status: false)
        }
    case .completed: // (4)
        break
    case .error: // (5)
        break
    }
}
  1. debug() 를 통해 어떤 이벤트를 받았고, 어떤 데이터가 있는지 콘솔로 확인할 수 있음.
  2. subcribe를 통해 observable(관찰가능한) 대상에 대해 구독을 한다.
  3. onNext 를 통해 전달 받은 데이터를 사용한다.
  4. 5. onCompleted, onError 에 대한 로직을 수행한다.

onNext 로직에서 self. 를 사용하고 있다.

이 부분에서 서로 참조하는 강한 순환 참조가 발생할 수 있는데, 그 부분을 해결하기 위해 보통 [weak self] 로 해결하곤 한다.

  • 어? 근데 위의 예시를 보면 [weak self] 를 사용하지 않았는데, 그 이유는 subscribe 클로저가 onCompleted 호출 시 클로저가 삭제되고 참조 카운터도 같이 줄어들기 때문에 사용할 필요는 없다.

강한순한참조에 대해서는 아래 포스팅에 잘 정리되어 있으니 보시면 도움될 것 같습니다.

 

[ Swift ] ARC (2) Retain Cycle, 강한참조, 약한참조 그리고 미소유참조

안녕하세요 🐶 빈 지식 채우기의 비니🙋🏻‍♂️ 입니다. 오늘은 ARC의 두 번째 시간입니다! Retain Cycle과 참조 종류 3가지에 대해 알아보는 시간을 가지겠습니다. 1. 개요 저번 글에서는 ARC의 기

beanistory.tistory.com

 

2-2. Disposable

Disposable에 대해 조금 더 자세하게 알아볼까 합니다.

Observable.create의 정의를 보았을 때, 마지막에 Disposable을 반환하게 되어 있었습니다.

// Disposable의 사용법
let disposable = downloadJson(urlString: "") // (1)
    .subscribe { event in
    ...
    }
    
disposable.dispose() // (2)
  1. Disposable을 반환하기 때문에 변수 할당이 가능합니다.
  2. 또한 dispose() 메서드를 통해 onError, onComplete 가 발생되기 전에 구독을 취소할 수 있습니다.

Q. 만약 이런 식과 비슷하게 여러개의 Observable의 구독을 취소하려면 어떻게 해야 할까요?

1. 배열을 만들어 Disposable을 저장한다. 그리고 하나씩 dispose() 진행한다.

2. DisposBag 을 이용한다.

 

위의 2가지 모두 사용이 가능합니다. 다만 1번의 경우 배열을 만들고 insert 하고... forEach로 일일히 빼서 Dispose() 하고..

여간 귀찮은 일이 아닐 수 없습니다. 그렇기 때문에 우리는 2번 방법으로 주로 사용하고 있습니다.

 

DisposeBag 이란 말그대로 Dispose의 Bag(가방) 이라는 뜻입니다.

// DisposeBag 사용법
var disposeBag = DisposeBag()
disposeBag.insert(disposable)

insert를 통해 넣어줄 수 있으나 이것 또한 배열처럼 매우 귀찮습니다.

그렇기 때문에 우리는 Observable이 생길 때마다 어떤 disposeBag에 넣어줄건지 지정할 수 있습니다.

 

// DisposeBag 사용법2
let disposable = downloadJson(urlString: "")
.subscribe ({
...
})
.disposed(by: disposeBag)	// (1)
  1. .disposed(by: ) 를 통해 DisposeBag을 지정할 수 있습니다.

 

지금까지이 설명을 요약해보면 다음과 같습니다.

Observable(관찰대상)을 만들고, Observer는 그 관찰대상을 subscribe(구독) 합니다.
Observable이 이벤트를 배출하면, Observer가 이벤트를 수신해서 어떤 액션을 수행합니다.

 

이상으로 Observable & Subscribe 포스팅을 마치겠습니다.

다음 포스팅은 Operator에 대해 알아보겠습니다.

감사합니다~~


참고

반응형