[DISCUSSION] ViewModel에서 navigation관련 로직에 MainActor를 쓰는 방식 제안 #62
kimscastle
started this conversation in
Ideas
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
MainActor를 사용해보자!
@ffalswo2 @cchanmi @kimscastle
민재와 MainActor에 관해 이야기하다가 좋은 아이디어를 떠올리게되었습니다
MainActor를 도입하면 기존 코드가 많이 간결해지고 편해져서 해당 제안을 위한 discussion을 작성하게되었습니다
1.기존 코드
지금 현재 모든 viewModel에서 공통적으로 가지고 있는 subject가 있습니다. 바로 navigationSubject죠
각각의 ViewModel에서 coordinator에게 navigation type을 만들어서 넘겨줍니다
예를 들어서 온보딩의 경우엔 backbuttonTap이 있고 nextButtonTap이있습니다
그렇기때문에 위와같은 코드를 viewModel이 가지고 있게됩니다
하지만 우리가 이렇게 공통적으로 navigationSubject를 만들었던 가장 큰 이유는 실행되는 thread를 결정하기 위해서였습니다
비동기적으로 데이터를 호출하고 해당 Task블럭에서 coordinator메서드를 호출하면 background thread에서 동작하기에 UI작업인 해당 작업은 main thread에서 작업을 해야한다는 오류가 발생하게 됩니다
그래서 저희가 navigationSubject를 만들어서 당연히 해당 subject에 값 자체가 background에서 오기에 receive(on:)을 사용해서 sink의 클로저가 실행되는 메서드를 main thread로 명시적으로 변경해줬습니다
물론 비동기코드 내부에서 메서드의 호출자체를 DispatchQueue.main.async로 main thread로 명시해줘도 되지만 매번 해당 클로저를 만들어주게되면 컴파일단에서 코드가 15%정도 더 증가를 하게된다고 합니다 당연히 클로저 블럭을 할당을 따로해야하고 context를 저장해야하기에 약간 찜찜한 코드가 됩니다
2. MainActor도입
그래서 navigation관련로직을 모으는 subject를 만들어서 receive(on:)을 한번만하면되기에 이런방식으로 문제를 해결했지만
MainActor를 사용하면 편하게 문제를 해결할 수 있으리라 생각이 들었습니다
MainActor에 관련해 아주 간단하게 설명하면 메서드나 객체에
@MainActor
이렇게 어노테이션을 달게되면 mainthread에서 동작해야하는 상황이 오면(UI작업같은) 자동으로 mainthread에서 동작을 할 수 있게 컴파일러가 알아서 thread를 변경해줍니다Tip
MainActor의 경우엔 GCD에서는 동작하지 않고 swift concurrency에서만 동작합니다
위의 코드에서
여기를 보세요!
를 보면 Task블럭에서 await때문에 thread가 임의의 background thread를 할당받게되기에 애초에 adator의 signInButtonTapped가 background thread에서 동작해서 문제가 되었습니다근데 만약에 해당 transform메서드에
@MainActor
를 사용하게되면 컴파일러가 알아서라고 명령을 해줍니다
Warning
뭐야 여기는 combine의 futuere인데 GCD에서는 동작안하는거아닌가요?라고할수있지만 Task라는 swift concurrency블럭내에서 동작하는것이기에 swift concurrency라고 보는게 맞고 그렇기에 MainActor가 동작할 수 있습니다
그러면 backbuttonTapped를 sink한 쪽을 볼까요
여기서도 보면 adaptor.backButtonTapped()라는 메서드는 sink의 클로저가 실행되는 thread에서 실행되고 recevie(on:)이 없으니까 기본적으로 input.backButtonTapped라는 subject에 void라는 값이 어느 thread에서 왔는지에 따라서 sink가 실행될 thread가 결정됩니다
우선 backbutton자체는 UI고 그걸 tab할때 void를 보내주는 쪽을 보면
기본적으로 navigationBar에서 void를 받아서 sink에서 보내주기에 void가 main thread에서 옵니다
그러니까 위의 self.adaptor.backButtonTapped()는 main thread에서 동작해서 문제가 없는겁니다
만약에 어거지로
이렇게 void자체를 backgroundthread에서 보내준다면 당연히 sink는 background thread라 recevie(on:Runloop.main)을 안해주면 main thread 에서 작업하라는 오류가 발생할겁니다
3. 정리
정리를 해보면 단순 버튼 action을 통한 navigation메서드 호출은 그냥 coordinator의 메서드를 호출하면되는데 어떤 비동기호출 내부에서 UI작업을 하게되면 mainthread로 작업하는 thread를 변경해주는 개발자의 작업이 필요한데
@MainActor
를 사용하면 알아서 컴파일러가 알아서 thread를 바꿔준다는겁니다그리고 우리 viewModel interface구조상 transform내부에서 비동기 작업을 하고 비동기 메서드내부에서 UI작업을 할 가능성이생기이에 사실 transform메서드에
@MainActor
만 명시해주면되고 확인해보니 interface 명시만 해줘도 자동으로 구현부에서 MainActor가 동작한다는걸 알게되었습니다4. 결론
Beta Was this translation helpful? Give feedback.
All reactions