[iOS]Combine은 왜 써야할까?

Youth·2023년 10월 20일
3

고찰 및 분석

목록 보기
1/21

안녕하세요 거진 2주반만에 돌아온 킴스캐슬입니다
글을 쓰려고 보니 저번포스팅을 하고나서 블로그를 좀 오래쉬었네요...

지금 진행하고있는 프로젝트 리팩터링을 하는데에 집중하느라 이런저런 글을 못썼네요
써야할글이 한 3개정도가 있는데 부지런히 써야곘네요
요즘 알고리즘 공부에 집중을하고있어서 조만간부터는 알고리즘 문제도 포스팅을 할까 고민중입니다(블로그자체가 iOS를 하는데 있어서의 고민을 담고싶은 마음이 커서 안할수도 있긴합니다)

사실 이번주부터 SOPT내에 있는 combine스터디에 들어가서 공부를 시작했습니다
그래서 combine에 대한 내용을 주 1회정도 포스팅을 진행하게 될것같습니다

오랜만에 블로그글을 쓰려니 좀 어색하긴하네요 그래도 늘 그랬던것처럼 저의 생각과 고민의 결과를 적어보겠습니다

Combine을 왜써?

스터디팀원분들하고 정했던 1주차 주제는 굳이 왜 많고많은 것들 중에서 combine을 써야할까에 대해 공부해보는것이었습니다

제가 개발 공부를 하면서 한가지 꼭 가져가고있는 부분이 있다면 왜?에 대한거는 타협하지 말자였습니다. 분명히 스스로 머릿속에 여러가지 의문부호나 궁금증이 생길텐데 그 궁금증을 해결하지 못한채로 단순히 기술스택을 늘리기 위해서 사용법만 익히는 방식의 성장은 제가 원하는 성장의 방향성이 아니었거든요

지금부터 combine을 써야하는 이유에 대한 저의 생각을 정리해보겠습니다

Functional Reactive Programming

보통 우리가 반응형 ui를 짤때 iOS에서 많이 쓰는 라이브러리가 있죠, rx swift라는 친구인데요. combine도 거의 동일한 녀석이라고 보시면됩니다

어떤 의미에서 동일하나면 두가지 tool모두 FRP를 하기 위한 라이브러리입니다
그래서 결국은 우리가 combine을 이해하기 위해서는 FRP에 대한 이해가 필요하다고 생각했고 곰튀김님의 FRP세션에 대한 영상을 보게 되었습니다

우선 해당 영상의 내용을 요약해보겠습니다

FRP의 등장배경

방금 말씀드렸던것처럼 combine과 rx모두 FRP인데 결국 Functional Reactive Programming이라는 하나의 패러다임에 부합하기 때문에 FRP의 등장배경을 아는것이 combine이나 rx의 등장배경을 아는것과 동일합니다

1. 패러다임이란?

패러다임(영어: paradigm)은 어떤 한 시대 사람들의 견해나 사고를 근본적으로 규정하고 있는 테두리로서의 인식의 체계, 또는 사물에 대한 이론적인 틀이나 체계를 의미하는 개념이다.[1] 토머스 쿤이 제안하였다.

프로그래밍이 발전해 왔고 그동안 여러가지 프로그래밍 기법이 등장해왔습니다. 그리고 당시의 개발자들이 공통적으로 동의할만한 생각들을 기반으로 새로운 방식이등장하게됩니다.
현재에서는 프로그램에 하나만 실행되는것이아니라 여러개의 프로그램이 동시에 실행되는것이 당연해진 시대가 되었습니다 그러다보니 하나의 프로그램의 실행이 같이 실행되고있는 다른 프로그램에 영향을끼치면 안된다라는 의견에 대부분의 개발자 동의하게 됩니다

그럼 과연 프로그래밍에 있어서 영향을 끼친다는 말이 무슨말일까요?

위의 사진처럼 다양한 프로그램이 하나의 data를 동시에 읽거나 쓰는 경우가 존재한다고 했을때 문제가 생길수가 있고 이런 상황이 하나의 프로그램으로 인해 다른 프로그램에 영향을 미칠수있게됩니다. 그리고 이런상황을 영향을 끼친다라고 표현할 수 있습니다

위의 코드를 한번 보면 이런 하나의 데이터를 하나의 프로그램(메서드)에서만 사용하면 아무런 문제가 없지만 여러 메서드에서 해당 데이터에 접근하는 경우엔 문제가 발생할수있습니다. 사실 읽기만하면 문제가 없지만 쓰기를 하는경우에 문제가 생기는 거죠 data race같은?

sync를 통해서 동시에 접근하는걸 막을수도 있겠지만 이 문제를 해결하는 가장 쉬운 방법은 data를 immutable하게 바꿔서(예를들자면 let으로 선언해서) 아얘 쓰기를 못하게 하는 것입니다 그리고 이런 특징을 우리는 immutable이라고 합니다

영향을 끼치는 경우를 한가지더 보면 흔히 side-effect라고 불리는 현상도 발생할 수 있습니다

분명히 같은 input을 가진 같은 메서드를 실행했는데 결과가 다르게 나오는 상황이 발생하기도 하죠...
이런 식으로 외부변수인 data로 인해서 같은 input이지만 다른 output이 나오는 상황을 side effect가 발생한다고 이야기합니다

2. 함수형 프로그래밍의 등장

그러면 어떻게 하면 side effect를 방지할수있을까를 생각해보면 위의 예시에서보면 외부변수에의해서 side effect가 발생하므로 외부변수를 사용하지 않으면 되겠죠

이런식으로 함수의 output이 오로지 input에의해서 결정되는 함수를 사용하게되면 side effect로부터 자유로워질 수 있고 이러한 함수를 사용해서 프로그래밍하는것이 현대에와서 중요해지게 됩니다

이러한 함수를 순수함수, pure function이라고 합니다

약간의 추가설명을 덧붙이자면 외부 변수를 메서드 내부에서 사용하더라도 그 변수가 immutable하다면 그또한 pure function이라고 부릅니다, 만약에 위의 예시에서 data가 let으로 선언되었다면 변하지 않을테니 순수함수로 볼 수있습니다

그리고 이러한 순수함수를 이용해 프로그래밍을 하는 여러가지 기법들이 제안되게 됩니다 그중에 하나가 결합을 활용한 프로그래밍입니다

위의 그림처럼 함수형프로그래밍은 pure-function, 그리고 함수자체를 타입으로 취급할수있는 1급객체의 특성(first class citizen)을 가지고 여러개의 함수를 조합해서 사용할수있는 composition(결합)을 통해서 복잡한 프로그래밍을 할수 있게됩니다

당연히 이런게 가능한 이유는 pure function으로 인해 side effect로부터 자유로워졌기 때문이었겠죠

그래서 결과적으로 기존의 data중심의 프로그래밍이 data의 변화에 집중하는 것이 아닌 input이 output으로 변하게하는 과정에 집중하는 프로그래밍의 특성을 띄게 되고 이를 절차지향적 프로그래밍이라고도 이야기를 합니다

결국은 데이터자체를 변화시키지 않고 어떤 output을 만들어내는 프로그래밍으로 인해 immutable함을 유지할수있게 하였고 하나의 프로그램의 실행이 같이 실행되고있는 다른 프로그램에 영향을끼치면 안된다 라는 원칙을 지킬 수 있게된거죠

비동기 패러다임이 중요해진 시기가 오면서 data가 immuable하게 만들어야 비동기에서의 안정성이 유지가 되다보니 data의 변경에 집중해서 프로그래밍하는 방식에서 data의 행위에 집중해서 프로그래밍하는 방식으로의 패러다임 전환이 일어나게 되었다라고 요약을 할 수 있을것같습니다

3. 기존의 비동기처리 방식

순수함수를 통해 URL값을 통해 서버에서 어떤 string값을 받아오는 메서드가 있다고 해볼까요?

아마 이런 모양일겁니다 URL을 가지고 String을 받아오는데 input에의해만 output이결정되는 순수함수의 모양을 가지고 있습니다

하지만 이 코드의 문제점은 실제로 서버에 가서 데이터를 받아와야하기때문에 시간이 걸린다는겁니다

결국은 서버에서 데이터를 가져올때까지 기다려야하는 상황이 벌어지는데 하나의 프로그램의 실행이 같이 실행되고있는 다른 프로그램에 영향을끼치면 안된다 라는 원칙에 위배되는거죠
하나의 프로그램 서버통신하겠다고 같이 실행되고있는 프로그램이 멈추면안되겠죠, 어플로치면 네트워킹한다고 ui가 멈추면안되는거랑 같은 맥락입니다

근데 네트워킹하는 동안 화면은 멈추고 로딩바가 움직이게 하면되는거아닌가요??

HIG를 보면 loading view에관한 이야기가있는데 유저가 loadingview가 움직이는걸 보게함으로써 process가 진행중이구나라는 인식을 할 수 있도록 해야한다고 나와있습니다 결국은 loading view가 움직이는거자체도 ui가 움직이는것이기때문에 이또한 비동기처리를 통한 예시라고 볼 수 있습니다

근데 사실 우리는 비동기 코드를 짤떄 저런 위의 방식으로 return하는 코드를 짤수가 없습니다... 왜냐면 서버에서 언제 데이터가 올지 모르기때문에 return하는 타이밍을 특정지을수가 없거든요

그래서 delegate나 completion handler의 escapting closure를 통해서 추후에 데이터가 왔을때 처리할 수 있도록 개발을 해왔습니다

응? 이제 안그래도 되는데?(async/await이 있는데?)

라는 생각을 하시는분이 있다면 우선 이떄 당시에는 그랬다는 정도로 넘어가주세요 좀이따가 asyn/await에 대한 이야기도 나옵니다

결론적으로는 비동기 처리를 하기위해 위와같은 두가지 방식을 위주로 프로그래밍 해왔습니다

우리가 함수를 사용하는 방식이 input을 가지고 output을 return해주는 방식인데 비동기처리에서는 이런 모양을 사용할 수 없기 때문에 우리가 이런 비동기처리를 우리가 원래쓰던 함수의 모양으로 사용할수있는 방식이없을까를 고민하다가 나온 패러다임이 reactive progarmming입니다

reactive progarmming : async한 상황에서 이 async한 data를 어떻게 처리할것인가
how : stream이라는걸만들어서 연결시키고 데이터를 거기다가 흘려보내자

단순 아이디어이기때문에 구현방식이 매우 다양할 수 있고 여러 구현방식중에 하나가 우리가 너무나 잘 알고있는 rxswift입니다

combine을 이야기하는데 왜 rxswift이야기를 하는거냐라고 하시는 분들도 계실수있지만 결국 rx의 등장배경이 combine의 등장배경과 똑같습니다 combine도 FRP이기 때문이죠

Reactive Programming의 아이디어는 이렇습니다. publisher가 subscriber를 subscribe해서 subscription을 줘서 stream이 연결된 상태를 유지합니다. stream연결자체는 비동기로 처리될이유가 없으니까요 그리고 publisher에 데이터가 들어오면 그값을 stream으로 연결된 subsriber로 보내주고 이때의 행위는 closure에 저장되어있기때문에 비동기적으로 데이터를 처리할 수 있게되는 아이디어입니다

이런 코드는 어떤 문제점을 가지고 있을까요?

기존 completion handler를 통한 비동기처리의 문제점중 하나는 시선이 분산된다는 점입니다. 우리가 보통 함수를 만들고 읽을때 순서대로 위에서부터 읽기때문에 수행 순서와 다른 함수와 엮이더라도 큰 어려움없이 해석을 할 수있게됩니다

하지만 이방식을 해석하는 순서를 보면 코드를 읽기위해 두번정도 위로 올라가야합니다. 어찌보면 수행순서가 일정하지 않은 비동기이기때문에 당연하다고 생각할 수도 있지만 시선이 분산된다는건 좋지 않을 수 있습니다

4. Reactive Programming

그렇다면 reactive programming을 사용하면(rx swift를 사용하면) 어떻게 될까요?

이런식으로 분명히 해당 코드는 시간순서대로 진행되지 않지만(async하게 동작한다)하지만 코드상으로는 순서대로 동작하게 읽고 사용할 수 있습니다

즉, Reactive Programming은 async한 코드를 순서대로 동작하는것처럼 읽고쓸수있게 만들어주는 패러다임이고 combine이나 rxswift는 그 패러다임을 표현할수있게해주는 하나의 도구인겁니다

정리해보면 이렇게 세가지 키워드로 정리할 수 있게됩니다

조금 글이 길어지고 있는데 여기서 FRP의 장점을 크게 두가지정도를 찾아낼 수 있습니다

  1. 코드를 읽을 때 시선의 분산이 사라진다
  2. async한 코드를 sync하게 작성하고 읽을 수 있다

Async/Await의 등장

제가 이 강의를 들었을때 FRP의 장점을 이야기하는 부분에서 들었던 의문이 있었습니다

근데 이거 Async/Await으로도 해결이 가능한 문제아니야?

실제로도 WWDC에서 Async/Await의 도입을 소개할때 시선의 분산과 async한 동작을 실제로는 sync하게 짜고 읽을 수 있다는 장점을 이야기 합니다

async/await자체로 인해 suspend라는 개념으로 비동기 결과를 return할수있게되었으며 Task로 인해 비동기코드의 실행순서를 보장받을수 있게 된거죠

하지만 앞에서 설명드린 FRP의 영상 촬영시점은 2017년이고 Async/Await의 등장은 21년도 WWDC였으니까 현재 시점에서 영상을 보면 단순히 시선의 분산과 async한동작을 sync하게 작성할수있다는 장점은 더이상 FRP프로그램만의 장점이 아니게되었다는 생각이 들었습니다

게다가 Async/Await은 기존의 GCD방식에 비해서 Thread관리측면에서도 장점이 많으니까 Async/Await은 기존의 FRP의 장점을 가지고 있으면서도 Thread관리측면에서도 유리하니까 굳이우리가 rx나 combine을 사용할 필요가 없는게 아닌가라는생각이 들었습니다

그래서 Combine은 왜 써야하는가?

Async/Await의 등장까지 생각해보면 굳이 FRP를 사용할 이유가 없는거같고 그렇다면 FRP인 Combine도 쓸이유가 없지않은거 아니야?라는 생각이 들었고 여기에 약간 쐐기를 박는 공식문서의 문단을 보게 되었습니다

사실 combine 내부가 swift concurrency로 되어있어서 combine자체의 비동기처리가 thread관리측면에서 용이하다고 가정을 해보면(실제 내부구현을 알수가 없으니 가정을 해보는겁니다 ㅎㅎ...)

오히려 async/await을 사용할 이유가 없어보이는 그림이 됩니다..
그런데 저위에 공식문서를 보면 오히려 async/await이 combine을 대체하는 뉘양스로이야기를 하니 제가 생각했을때는 비동기처리에있어서는 async/await이 combine을 대체할것이다가 아닐까라는 생각이 들었습니다

만약에 combine내부에서의 비동기처리가 swift concurrency로 동작하지 않아서 thread관리 측면에서의 이점을 가지고 있지 않다면

오히려 이런 관계가 되지않을까라는 생각이 들었습니다

즉, 단순히비동기 메서드를 호출하는데 있어서는 Async/Await을 채택해서 사용하고 steam을 활용해 데이터의 변화에 반응하는 반응형 UI를 만드는데는 combine같은 FRP메서드가 장점을 가지게 된다고 생각했습니다
combine이 async/await에 비해 가지는 장점을 한가지 더 생각해보면 다양한 operator를 활용한 side effect없는(순수함수니까) 데이터 활용이 있을수있겠네요

결론적으로 왜 combine을 써야하는가?에 대한 제 나름대로의 답은 단순히 비동기처리을 위한 tool로는 async/await과 combine모두 각각의 장점을 가지고 있지만 어쨋든 비동기 처리는 thread를 어떻게 관리하느냐가 성능과 직결되는 부분이기에 thread의 context switching이 발생하는 코드는 async/await을 사용하고 데이터의 변환과정이 존재하면서 stream을 통해서 반응형 ui를 구현하기 위해서는 combine을 사용하는게 좋지 않을까라는 결론을 내리게 되었습니다

애초에 Combine은 비동기 프로그래밍을 위한 프레임워크가 아니며
Combine은 상태 변경으로 인한 값의 흐름에 반응하기 위한 것이다.

실제로 몇몇분들의 의견을 보면 combine은 네트워킹같은 단순 비동기프로그래밍을 하기위한 프레임워크가 아니고 반응형 UI를 위한 프레임워크에 가깝다고 이야기를 하시더라고요

지금 진행하고 있는 프로젝트를 생각해보면 단순히 API호출을 통한 네트워킹의 tool로는 async/await을 사용하고 데이터의 변화에따른 UI의 변화는 combine을 통해서 관리하는 방식을 채택하지 않을까싶습니다


처음에는 async/await과 combine을 같이쓰는 tool이라고 생각헀는데 구글링을해보니까 오히려 둘중하나를 선택한다던지 하는 글들이 오히려 더 많았던것같습니다

그건 아마 제가 combine을 단순히 반응형 UI를 쉽게만들어주는 tool이라고만 생각했지 concurrency관련 처리를 해주는 tool이라는 생각을 못했기 때문이 아니었을까라는 생각이듭니다

초반엔 combine을 공부해야할 이유를 찾고있는데 오히려 써야할 이유를 잃어버리는 시간이었던것같습니다

스터디 팀원들이랑 이야기를 하면서 async/await과 combine을 비교해보면서 combine은 stream과 operator라는 async/await에 없는 강력한 무기가 있기때문에 앱의 상황에따라서 combine도 충분히 좋은 선택지가 될 수 있는 경우가 많을거같다는 결론에 도달하게 되었던것같습니다

오늘 주제는 정말 제 뇌피셜(나름 공부를 하고 조사를 해봤지만...)에 가까운 글이기때문에 틀린부분이나 논리상으로 맞지않는 부분이(아마 없을거지만 존재할수도있죠...?) 있을수도있을거같아요 혹여나 그런 부분이 보이신다면 언제든지 피드백 해주시면 감사하겠습니다

오랜만에 긴 글을 마무리하게되었네요
그럼20000!

profile
AppleDeveloperAcademy@POSTECH 1기 수료, SOPT 32기 iOS파트 수료

0개의 댓글