본문 바로가기
Dev/고민과 삽질의 기록들🤔

[Combine] Combine을 왜 써야할까 ? (async/await과 비교)

by Mintta 2023. 11. 1.

이전글

https://codingmon.tistory.com/62

 

[Reactive Programming] Intro: Functional Reactive Programming ?

RxSwift, Combine를 시작하기 앞서 RxSwift라고 적었던 모든 단어들이 Combine으로 대체해서 보아도 되기 때문에 제목을 RxSwift보다 "Reactive Programming"으로 좀 더 제네릭한 이름으로 수정했습니다. RxSwift를

codingmon.tistory.com

 

MVC프로젝트를 MVVM으로 바꾸기 위한 리팩토링의 초읽기에 들어서면서 현재 Combine에 대한 공부, 그리고 Combine을 대체 왜 써야하는지에 대해서 공부를 하고 있습니다. 저번 글이 사실 시작이고 이번이 두번째 글이고, 앞으로 써야할 주제들이 너무나 많으니 힘내서 달려봐야겠습니다..!

 

이번 글 주제는 Combine이 왜 필요한지에 대해서 공부하다가 정말 얘 왜 필요하냐.. 라는 의구심을 가지게 됐던 과정과 Combine을 쓴다면 이렇게 써야겠다 ! 하는 방향성에 대해서 이야기해보고자 합니다. (async await도 함께)



레츠고

렛츠고!


저번 Functional Reactive Programming에 대한 아티클에서 FRP에 대해서 이야기를 나눴습니다.

 

사실 저 아티클에서 공감이 크게 되진 않아서 언급을 안했던 장점이 있었는데요 그 이야기를 시작으로 Combine이 정말로 필요한가에 대해 의문을 품게 된 계기와 Combine을 사용하게 된다면 그 방향성에 대해서 이야기해보겠습니다.

 

저번 아티클의 주요 레퍼런스가 된 let us go 곰튀김님의 영상에서 곰튀김 님이 이런 말씀을 하십니다.

 

기존에 존재하는 @escaping closure를 이용한 비동기 처리 방식은 시선이 매우 분산되지만 RxSwift(영상 속에서 RxSwift가 기준이기에)를 쓰게 되면 실제로는 비동기로 처리되는 작업을 순서대로 읽을 수 있게 된다

무슨 말인지 곰튀김님이 말씀해주신 예시를 바탕으로 한번 봐볼까요 ?

 

화살표 어지럽다

딱봐도 코드를 읽는 순서가 순서대로 읽히는 것이 아니기 때문에 계속해서 코드의 위 아래를 왔다갔다하면서 시산이 분산되는 것이 느껴집니다.

 

그렇다면 여기에 Reactive programming(RxSwift)를 사용하게 되면 어떻게 될까요 ??

이렇게 비동기작업이고, 실제로 비동기로 처리되는 작업들이지만 위에서 아래로 순서대로 코드를 읽을 수 있게 되어서 시산이 분산되지 않습니다.

 

그렇다면 Reactive programming, 그리고 패러다임을 접목시켜 만든 라이브러리인 RxSwift와 Combine을 사용하게 되면

얻을 수 있는 장점들을 저번 아티클에서의 내용과 총합해서 정리해본다면 아래와 같습니다.

 

  • Function programming의 side effect이 없는  순수함수의 조합으로 비동기 데이터를 data stream으로 관리할 수 있게 해준다 !
  • 비동기로 작업함에도 동기 코드처럼 시선의 분산을 없애고 위에서 아래로 코드를 읽을 수 있게끔 해준다 ! (🤔)

 

하지만 두번째 장점이 제게는 너무 익숙한 문구였습니다. 분명 저 문제를 해결한 개념이 이미 존재했던 것이죠 🤔

바로 async/await입니다.

 

sync하게 읽을 수 있다 ?? 우리에겐 async / await가 있는데 ? 🤔

저 영상이 올라온 시점이 2017년이였고, async await의 등장은 Swift 5.5부터였으니까 2021년 입니다.

 

사실 영상에서 말씀하신 시점에서 보면 비동기 코드를 동기 코드처럼 읽을 수 있다는 분명 엄청나게 큰 장점으로 다가왔을 것 입니다.

 

하지만 지금 우리는 async/await를 사용하여 위와 완전히 동일한 문제를 해결하고 있었던 것이죠.

 

그렇다면 사실 두번째 장점은 설득력이 약하다고 생각했습니다.

 

async await의 모습도 한번 보기 위해 async await가 적용된 제 프로젝트에서 코드를 가져와봤습니다.

inquiryTodayArticle()fetchImage()등과 같은 API들은 비동기 작업임에도 불구하고 async await를 통해서 위 코드를 동기적으로 위에서부터 아래까지 자연스럽게 읽을 수 있습니다. 위에서 RxSwift를 써서 얻을 수 있었던 장점과 동일한 것 입니다.

 

또한 async await를 쓰게되면 Swift Concurrency의 이점을 살릴 수 있습니다. 

이 부분에 대한 설명은 WWDC21 Swift Concurrency: Behind the Scenes에 나옵니다.

사실 wwdc에 대한 정리는 블로그글에 따로 추가로 올리진 않고 github에만 올리기 때문에 여기에는 제가 정리했던 노션링크를 첨부하겠습니다. (저도 이 영상을 다시 봐야겠네요!)

https://complex-rook-29b.notion.site/Swift-Concurrency-Behind-the-scenes-1e87c3afefe34e5ea374c635253de93e

 

그래도 그냥 넘어가기에는 서운하니까 살짝 얘기해보자면 !

 

Swift Concurrency를 사용하면 cooperative thread pool을 이용해서 thread explosion을 막을 수 있고, context switching없이 continuation을 활용한 스레드 전환이 가능하기 때문에 성능상 이점이 존재합니다. Context Switching은 분명히 비용이 많이 드는 일이기 때문이죠.

 

다시 정리해보자면..

  • Function programming의 side effect이 없는  순수함수의 조합으로 비동기 데이터를 data stream으로 관리할 수 있게 해준다 !
  • 비동기로 작업함에도 동기 코드처럼 시선의 분산을 없애고 위에서 아래로 코드를 읽을 수 있게끔 해준다 ! (🤔)

RxSwift나 Combine을 사용하여 얻을 수 있는 위의 두가지 장점들 중 두번째는 async await가 완전히 상위호환이라고 생각했습니다.

Combine은 시선분산의 효과만 제거해주지만, async/await는 Swift Concurrency의 이점까지 챙기면서 시선 분산까지 없애는 효과를 주기 때문이죠.

 

그럼 아래와 같은 벤다이어그램이 만들어집니다.

이렇게 명확하게 자기 진영의 장점을 가진 두가지 방법이 존재하는 것입니다.

 

따라서 async await나 combine이나 둘 중 하나가 다른 하나를 완전히 잡아먹는 그림은 아니라고 생각했습니다.

결론! 네트워크 layer에서는 async await를, ViewModel과 UI간의 binding은 combine !

제목그대로 둘 중 하나를 사용하는 방향이 아닌 둘 다 사용하는게 좋을 것이다라고 판단했습니다.

 

네트워크 layer에서는 아무래도 성능과 직결될 수 있는 부분이기에 성능적인 이점을 살리기 위해 Swift Concurrency를 사용하는 async await를 가져갑니다.

 

그리고 이 후에 반응형 UI, 그리고 로직의 연속으로 복잡해질 수 있는 문제들에서는 Combine의 data stream을 통해서 보다 쉽게 풀어나가고자 하였습니다.

 

async await의 장점은 장점대로 가져가고 Combine의 장점은 장점대로 가져가는 방향으로 정한 것 입니다.


마무리

이렇게 프로젝트의 리팩토링 방향을 정하게 되었습니다. 처음에는 그냥 무조건 Combine을 써야지 하는 생각이었는데, 공부를 하면 할수록 의구심이 들었고 결국 조금은 다른 방향으로 가게되었는데, 이게 더 맞는(?) 방향인 것 같아서 마음에 듭니다.

 

이 후에 Combine을 공부하면서 정말 정말 쉽지 않은 개념들의 연속이였지만 그래도 하루하루 깨닫게될 때마다 성취감을 느끼고 있어 요새는 공부가 그래도 재미있는 때인 것 같습니다.

 

아직 아티클로는 안썼지만 그간 겪었던 험난했던 과정들을 여기에서 미리 스포해보자면..

  • Publisher와 Subscriber간의 연결과정을 딥다이브한 과정
  • Publisher중 Future만 Reference type인 이유와 Subscriber가 Reference type인 이유
  • cancel에 대한 동작 원리 딥다이브

아직 아티클 쓸게 너무 많이 남았네요..하하ㅏ

 

 

댓글