밥과 함께 RxSwift 시작하기

왜 RxSwift를 반응형 프로그래밍과 함께 배울까요?

by Bob Lee
translated by pilgwon

글을 쓰게 된 동기

전 이 글을 제가 봤던 오해의 소지가 있는 웅덩이와 “이것은 그냥 작동합니다” 같은 내용의 인터넷을 떠도는 게시글들에 대한 좌절의 경험을 공유하면서 시작하고 싶습니다. 저는 이젠 구글의 검색 결과 첫 10페이지를 읽은 후 제가 피곤해지는 것을 보는데 지쳤습니다.

저는 1000명이 넘는 iOS 개발자와 학생에게 설문조사를 했습니다. RxSwift도 종종 언급되었습니다. 그러나 대부분은 이 프레임워크를 실제로 사용하기에는 가파른 학습 곡선 때문에 힘들다고 했습니다.

이제 그 이유를 알겠습니다. 이 개념을 비유와 이야기를 사용하여 설명할 수 있는 사람이 많지 않습니다. 당신은 잘 찾아온 것입니다. 저는 RxSwift로 손을 더럽히고 이 놀라운 공동체에 기여하기로 결정했습니다.

사고방식 배우기

저는 “그냥 따라하기”로 시작하는 스타일은 아닙니다. 배움에 있어서 저의 접근 방식은 “어떻게”보단 “왜”의 근본적인 이유를 찾는 것입니다. 많은 사람들은 제품을 만드는데에 초점을 맞추는 경향이 있습니다. 저의 방식은 초반에 더 많은 투자와 노력이 필요하지만 미래에 당신은 코딩할 때 불안감 대신 자신감과 확신을 가지고 할 수 있을 것입니다.

네 당신은 “그냥 해보기”로 배울 수 있습니다. 당신은 계란을 던져서 물리적으론 돌을 꺨 수 없다는 것을 배울 것입니다. 다른 한편으론, 유튜브 비디오로 계란으로 바위치는 영상을 볼 수도 있습니다.

네 당신은 공기역학과 각운동량에 대한 책을 읽는 것으로 자전거 타는 법을 배울 수 없습니다. 실제로 대부분의 사람들은 도구로 사용하기 위해 해당 주제에 대해 신경 쓸 필요가 없습니다.

둘 사이의 균형이 있어야 합니다. 제 학습에 있어서, 저는 시작하기 전에 저의 많은 시간을 이론과 만들어진 예제들을 둘러보는데에 시간을 많이 투자합니다. 저는 제가 왜 우리가 이런 특정한 패러다임이나 아키텍쳐를 6살 어린 아이에게 설명할 수 있기 전까지는 프로젝트에 시도하지 않습니다.

왜라는 질문하기

이 게시글에서, 우리는 우리가 왜 RxSwift를 쓰는지와 왜 무엇을 제공해야 하는지 듣는 것을 지겨워해야 하는지에 대해 강한 이해를 가지게 될 것입니다.

Viktor Frankl의 “Man’s search for meaning”에는 제가 좋아하는 문구가 있습니다.

사는 ‘이유’에 대해 하는 사람은 어떤 ‘어떻게’도 다 포용할 수 있다.

저자는 나치의 강제 수용소에서 살아남을 수 있었습니다. 의사로서, 그는 살아야 할 이유가 있었습니다. 먼저, 살아서 가족을 보기 위해서. 두번째, 지역 사회를 도우기 위해서. 학습에 있어서도 같은 사고방식이 적용됩니다. 만약 당신이 RxSwift를 “왜” 쓰는지 알게 된다면, 당신은 결국 실제 세계 상황에서 이 개념을 예제와 상상력의 균형을 통해 적용하는 방법을 발견하게 될 것입니다.

아인슈타인은 이렇게 말했습니다,

“상상력은 지식보다 더 중요합니다. 지식은 유한하지만, 상상력은 모든 세상을 포옹하고, 진보를 자극하고, 진화를 낳습니다.”

독자

이 글은 숙련된 개발자들을 위해 작성되었습니다. 만약 당신이 OOP, POP, ARC, 델리게이트, 콜백, 함수형 프로그래밍에 대한 이해가 없이 함수형/반응형 프로그래밍을 배우려 한다면, 당신은 나머지를 이해할 수 없을 것입니다. 만약 당신이 모든 것을 한 번에 얻고 싶으시다면. “Bob과 함께 Swift 4 배우기”에 등록하는 것을 망설이지 마세요. 아마도, 이것은 RxSwift의 전제 조건일 것입니다.

<iframe width=100% height=”315” src=”https://www.youtube.com/embed/7lwgI2I-vKU” frameborder=”0” allowfullscreen alt=”Prerequisite”></iframe>

RxSwift의 정의

많은 튜토리얼들이 RxSwift를 개발자들이 반응형과 함수형 패러다임으로 코딩하게 해주는 도구로 설명하면서 시작합니다. 하지만, 대부분의 그 글들은 함수형(functional)반응형(reactive)라는 용어의 의미와 왜 우리가 이 철학을 채택한 이유를 설명하지 못합니다. 그들은 기존의 것에 비해 이득을 설명하지 못합니다. 그들은 단지 “예제를 보여주기” 바쁩니다. - “왜” 보다는 “어떻게”죠.

메모 RxSwift는 스위프트로 만들어진 라이브러리입니다. 다른 언어로는 RxJS, RxKotlin, RxScala, Rx.NET이 있습니다. 그들은 모두 ReactiveX가 정한 패러다임을 따르고 있습니다.

RxSwift에 들어가기 앞서, 반응형함수형에 대해 알아봅시다.

함수형 프로그래밍이 무엇인지 알아보기

RxSwift의 첫 번째 원칙은 함수 기반 코딩 스타일입니다. 저는 이미 밥과 함께 스위프트 함수형 프로그래밍 시작하기에 대한 글을 썼습니다. 그래서 다시 한 번 몇 개의 포인트를 짚고 넘어가겠습니다.

정의 함수형 프로그래밍은 동료와 소통하고 문제를 해결하기 위해 함수를 쓰는 것 이상입니다.

함수형 프로그래밍의 진가를 알아보기 위해, 문제 해결의 “일반적인” 방법부터 보겠습니다.

명령형 프로그래밍

우리가 어떤 배열을 가지고 있다고 해봅시다.

let 나의성적 = ["A", "A", "A", "A", "B", "D"]

우리는 “A”만 받기를 원할 것입니다. 명령형 프로그래밍을 시작해봅시다.

var 행복한성적: [String] = []

for grade in recentGrade {
 if grade == "A" {
  행복한성적.append(grade)
 } else {
  print("우리 엄마가 행복하지 않아")
 }
}

print(행복한성적) // ["A", "A", "A", "A"]

선언형 프로그래밍

위의 8줄의 구현은 끔찍합니다. 이제 함수를 좀 더 함수형스럽게 사용해 봅시다.

stringFilter(grade: 나의성적, returnBool: { $0 == "A" })
// ["A", "A", "A", "A"]

메모 만약 당신이 명령형과 선언형의 차이를 이해하는데 고생을 하고 있다면, 위에 첨부된 글/코스를 보고 돌아오시는 것도 괜찮습니다. 다시 한 번 말씀드리면, 당신은 클로저와 완료 핸들러에 대해 강한 이해를 가지고 있어야 합니다.

함수형 프로그래밍은 다음과 같은 것을 제공합니다:

  1. 확실한 단계적인 관리
  2. $로 인한 전통적인 변수 이름 지정을 통한 간결한 의사소통
  3. 유닛 테스트 가능성
  4. 짧음

메모 저는 이 전의 글에서 이미 각각의 이점을 설명했습니다.

반응형 프로그래밍은 무엇일까요?

RxSwift가 받아들인 두번째 패러다임은 반응형 프로그래밍입니다. 반응형 프로그래밍의 진가를 알아보기 위해, “비 반응형” 코드를 한 번 봅시다.

비 반응형 프로그래밍

숫자 두 개를 더해봅시다.

var a = 1
var b = 3

a + b // 4

결과는 4입니다. 좋고 쉽죠. 하지만, a를 다른 숫자로 바꾸고 싶다면 어떡할까요?

var a = 1
var b = 3

a + b  // 처음(4) -> 마지막(4)

a = 3

지금은 다른 점이 없습니다. a에 할당된 값을 1에서 3으로 변경하더라도, a + b는 여전히 4를 반환합니다. 이것은 스위프트에서 “보통의” 코드입니다. a + b는 아무일도 신경쓰지 않습니다. 그것은 정해진 시간에 그것의 비즈니스만 신경씁니다.

반응형 프로그래밍

이제 반응형 프로그래밍이 무엇을 의미하는지 알아봅시다. 동일한 예제를 사용해보겠습니다. 하지만, 우리는 그 숫자들을 이미 만들어져있는 클래스인 Reactive로 감싸보겠습니다.

var a = Reactive(1)
var b = Reactive(3)

a.value + b.value // 4

메모 위의 예에서 보았듯이, 저는 Reactive라는 가짜 클래스를 사용했습니다. 왜냐하면 String과 같은 기본 값은 스위프트에서 비 반응형이기 때문입니다.

이제 a의 값을 3으로 변경해보겠습니다.

var a = Reactive(1)
var b = Reactive(3)

a.value + b.value // 처음(4) -> 마지막(6)

a.value = 3

이제, 차이점이 생겼습니다. a.value + b.value는 여전히 ab의 값에 신경을 쓰고 있습니다. 이것이 반응형 프로그래밍입니다. 반응형 프로그래밍은 변화를 기다리고 변화가 있으면 나타내고/반응하고/보여주고/표현하고/구독하고/관찰합니다. 이것은 NSNotification과 비슷합니다.

이것이 반응형 프로그래밍의 근본적인 기반입니다.

상상

이 반응형 패러다임이 실제 iOS 프로그래밍에 있어서 얼마나 강력할지 상상하는 시간을 가져봅시다.

반응성의 특성으로 인해, RxSwift는 비동기 코드를 핸들링하는데 왕으로서, 당신이 변경을 할 때마다 무언가가 일어났음을 알려줄 것입니다.

예를 들어, UIViewUIDevice의 속성을 반응형으로 만들면, 당신은 쉽게 변화를 느낄 수 있고 완료 핸들러/콜백을 통해 다른 라인의 코드를 실행할 수 있습니다. 이제 기기 방향 속성을 반응형으로 만들어봅시다.

let 기기방향 = UIDevice.current
let 반응형방향 = Reactive(기기방향)

당신은 기기방향을 “반응형”으로 만들었습니다. 사용자가 기기를 돌리면, 아래의 수도 코드를 통해 변화를 “관찰”할 수 있습니다.

반응형방향.observe { 현재방향 in
  switch 현재방향 {
  case .오른쪽가로모드: print("가로!")
  case .위아래바뀐세로모드: print("이상한 세로!")
  case .왼쪽가로모드: print("가로!")
  case .세로모드 { print("세로!") }
  }
}

이제 유저가 4번 90도씩 기울여 봅시다.

// "Landscape
// "Weird Portrait"
// "Landscape"
// "Portrait"

꽤 쉽죠? 이것이 바로 반응형 프로그래밍의 근본적인 기반입니다.

함수형 프로그래밍

그러면, 함수형 프로그래밍은 어떻게 작동할까요? 당신은 반응형 객체에서 받은 이벤트를 다루기위해 “함수”를 적용할 수 있습니다.

당신이 기기가 세로모드일 때만 출력하길 원한다고 가정해 봅시다.

반응형방향
.filter { $0 == .세로모드 }
.observe { 현재방향 in
  switch 현재방향 {
  case .오른쪽가로모드: print("가로!")
  case .위아래바뀐세로모드: print("이상한 세로!")
  case .왼쪽가로모드: print("가로!")
  case .세로모드 { print("세로!") }
  }

또는 당신이 스위프트의 클로저를 이미 마스터했다면, 아래와 같이 구현할 수 도 있습니다.

반응형방향
.filter { $0 == .세로모드 } // 아니라면 돌아갑니다
.observe {
  switch $0 {
  case .오른쪽가로모드: print("가로!")
  case .위아래바뀐세로모드: print("이상한 세로!")
  case .왼쪽가로모드: print("가로!")
  case .세로모드 { print("세로!") }
  }

폰을 시계방향으로 90도씩 돌려봅시다.

// 출력되지않음
// 출력되지않음
// 출력되지않음
// "세로!"

다시 한 번 말씀드리면, filter 함수는 여러분이 얻는 것을 조종하기 위해 모든 다가오는 데이터/이벤트에 적용됩니다. 함수형/반응형 패러다임은 당신이 적은 라인의 코드를 작성하게 도와주고 가독성을 높여줍니다. 이전의 조건문이 참임을 보기 위해서, 비동기 코드를 처리하기 위한 몇 가지 다른 예제를 살펴보겠습니다.

만약 당신이 스위프트의 비동기 코드에 익숙하지 않다면, iOS의 델리게이트와 콜백을 읽으시면 됩니다. 다시 한 번 말씀드리면, 비동기는 이벤트/실행의 비정기적인 흐름으로 설명할 수 있습니다.

델리게이트랑 비교하기

더 이상 거북한 UIViewController가 수많은 프로토콜에 부딪히는 일은 없을것입니다.

extension 나의뷰컨트롤러: UIViewController, GoogleMapDelegate, GoogleMapDataSource, UIDeviceOrientationDelegate, UITableViewDelegate, AnotherDelegate {
  // 요구되는 수많은 메소드들
  // 다루기 힘듭니다
}

더 이상 objc를 “옵셔널”을 요구하는 함수를 위해 쓸 필요가 없습니다.

@objc protocol UIDeviceOrientationDelegate {
    @objc optional func didChangeDeviceOrientation()
}

이 커플은 훌륭하지만, 거칠 수 있습니다.

KVO와 비교하기

훌륭하고 오래된 Objective-C 개발자들은 특정 객체의 변화를 추적하기 위해 KVO를 사용합니다.

-(void)observeValueForKeyPath:(NSString *)keyPath
                     ofObject:(id)object
                       change:(NSDictionary *)change
                      context:(void *)context

위의 코드를 이해할 필요는 없습니다. 당신은 그저 “keypath”를 선언하는 속성을 관찰하면 됩니다. 걱정하지마세요. 스위프트 개발자로서, 당신은 이 API를 쓸 일은 거의 없을 것입니다.

NSNotification과 비교하기

RxSwift에선 구독하기 위해서 subscribe 메소드 하나를 호출하면 되는 반면에, 관찰자(observer)를 구독하려면, 4개의 파라미터가 필요합니다. 함수형 패러다임은 적용하지 않았습니다.

NotificationCenter.default.addObserver(self,
    selector: #selector(알림이오면실행할함수),
    name: NSNotification.Name(rawValue: 나의노티피케이션키),
    object: nil)
  }

속성 관찰자(Property Observer)와 비교하기

당신은 또한 속성 관찰자를 사용할 것입니다.

var 내기기현재방향 = UIDevice().current {
  willSet(새로운방향) {
    새로운방향 in
      switch 새로운방향 {
      case .오른쪽가로모드: print("가로!")
      case .위아래바뀐세로모드: print("이상한 세로!")
      case .왼쪽가로모드: print("가로!")
      case .세로모드 { print("세로!") }
      }
}

속성 관찰자는 코드가 변화하는 것을 다루는 강력한 방법을 제공합니다. 그렇지만 단점을 가지고 있습니다. 먼저, 만약 두 개 이상의 객체가 새로운 데이터에 신경쓰고 있다면 당신은 아래와 같이 만들어야 할 것입니다.

var 내기기현재방향 = UIDevice().current {
  willSet(새로운방향) {
    // 함수1(새로운방향)
    // 함수2(새로운방향)
    // 함수3(새로운방향)
  }
}

속성 관찰자 영역은 거북해집니다. RxSwift를 쓴다면 나눌 수 있습니다. The property observer area becomes bloated. Using RxSwift, you can split.

내기기현재방향 = UIDevice().current
let 반응형방향 = Reactive(내기기현재방향)
// 함수1
func 함수1() {
  반응형방향.observe { print($0) }
}

// 함수2
func 함수2() {
  반응형방향.observe { print($0) }
}

// 함수3
func 함수3() {
  반응형방향.observe { print($0) }
}

이제 당신의 코드가 더욱 모듈화됐습니다.

마지막으로

이 글의 목적은 RxSwift로 제품을 만들기 시작하는 것이 아닙니다. 오히려, RxSwift의 기본 구성 요소를 이해하는 것이 목적이었습니다. 반응형 프로그래밍은 완전히 새로운 구기 종목입니다. 앞으로는 RxSwift를 통해 계속 상세하게 진행할 계획입니다. 향후 업데이트를 받는 데에 관심이 있으시면 bob@bobthedeveloper에 이메일을 보내주시기 바랍니다.