애플 개발자 아카데미에서 SwiftUI로 개인 프로젝트를 진행하면서 Redux 아키텍처를 적용해보았습니다.
Redux는 전역 상태관리에 중점을 두고 React에서 출발한 아키텍처였기 때문에, 중점적으로 보았던 부분은 크게 세가지였습니다.
SwiftUI에서 Redux를 적용했을 때 장단점은 무엇일까?
SwiftUI에서 Redux를 제대로 적용할 수 있을까?
어떤 프로젝트에서 어떻게 Redux를 적용하는 것이 좋을까?
이번 프로젝트에서는 비동기 처리와 사이드이펙트에 대해서는 고민할 필요가 없었기 때문에,
미들웨어를 뺀 State, Action, Store, Dispatcher 로 이루어진 Redux의 기본 형태만을 가져와 사용해보았습니다.
아직 Server Response 처리와 같은 Redux의 강력한 기능을 맛보지 못했어요..ㅋㅋ 하지만 상태관리에 대한 해답을 찾기 위해 해당 아키텍처를 적용한 만큼 고민은 해볼 수 있었습니다.
** React를 한번도 사용해본적은 없지만... Redux자체가 React에서 나온거다보니 참고한 대부분의 자료들이 React의 문서들이어서 React에서 사용하는 용어들이 많이 등장할 수 있습니다.
1. SwiftUI에서 Redux를 적용했을 때의 장단점은 무엇일까?
1 - 1. Redux를 사용했을 때 장점
Redux의 장점으로는 단방향 Data flow이기 때문에 오류관리가 쉽다는 장점이 있었습니다.
앱의 모든 상태 변경은 Store에서 일어나기 때문에 앱의 비즈니스 로직에서 문제가 일어나면 어떤 곳에서 문제가 일어났는지 비교적 한눈에 확인할 수 있습니다. 책임 소재가 Store에 있는게 명확하기 때문이죠.
다음은 데이터의 흐름을 도식화 한 것입니다.
UI에서 State를 변경해주어서 다시 그 State가 UI에 반영되기 까지 오직 한 방향으로 이루어지기 때문에 복잡성을 줄인 상태에서 로직 관리가 가능합니다.
또 전역 State 변수를 한 곳에서 관리할 수 있다는 큰 장점이 있습니다.
어떤 아키텍처를 사용하든 State를 관리하는 가장 중요한 원칙 중 하나는 "서로 다른 곳에서 한가지 State를 다룬다면, 해당 State는 한개의 State여야 한다"입니다.
WWDC 영상에서는 Source of Truth라고 표현하죠. 단일한 State에서 뽑아 써야한다는 것입니다.
예를 들어, 다음 앱에서 즐겨찾기에 항목을 추가하면 "즐겨찾기의 개수"가 1이 늘어날텐데, 이 즐겨찾기의 개수를 "지도 페이지"과 "즐겨찾기 페이지"에서 각각 개별 변수로 사용하게 된다면 해당 변수의 신뢰도는 매우 떨어질 것입니다. 만약 두 변수가 서로 다르다면 어떤 변수를 신뢰해야 할지 알 수 없기 때문이죠.
보통 이를 위해서 Binding을 이용해서 Properties Drilling 해주는 방식을 많이 이용하는데, Redux와 같은 전역 State관리 아키텍처를 사용하게 되면 골치 아프던 Properties Drilling을 피할 수 있다는 장점이 있습니다.
1 - 2. Redux를 사용했을 때 단점
가장 큰 불편함은 State 값 하나를 변경하기 위해 다루어야 할 코드가 많아진다는 점입니다.
함수 하나를 실행해서 값을 변경했던 로직이, Dispatcher -> Reducer, Action 을 거치게 되면서 보아야 할 파일이 많아졌습니다.
그리고 State가 바뀌었을 때 Rendering이 앱 전체적으로 일어난다는 문제가 있습니다.
State의 값이 변경될 때 마다 Store의 State값과 관련된 모든 UI들이 새롭게 로드 되는데, 단순히 숫자 하나를 올려도 모든 뷰가 새로고침 되기 때문에 성능 관리가 힘들어질 수도 있습니다.
SwiftUI도 렌더링 최적화가 가능할까요?
React는 PureComponent와 같은 렌더링 최적화가 가능하더라고요...
아직까지는 SwiftUI의 렌더링은 프레임워크에서 전담하고 있는 것 같습니다.
UZILOG
uzihoon.com
2. SwiftUI에서 Redux를 제대로 적용할 수 있을까?
"Redux스럽다"에 대해서는 매우 주관적일 수도 있지만, 앞서 이야기한 Redux의 가장 큰 특징인 데이터 플로우가 한 방향을 가지고 Store을 통해서만 전역 상태관리를 하는 것이라고 나름대로 결론을 내렸습니다.
이 결론에 따르면, SwiftUI에 Redux를 적용함에 있어서 문제가 몇개 있었습니다.
2 - 1.
양방향 Binding component 문제
SwiftUI에서는 양방향 Binding을 사용하지 않고 앱을 만들 수 없습니다.
가장 대표적인 예시로 TextField와 Picker, Toggle 등을 들 수 있는데, Picker의 예시를 들면 다음과 같습니다.
Picker(selection: $tempForPicker, label: Text("Please choose Noodle")) {
ForEach(0 ..< noodleName.count, id: \.self) { Text(noodleName[$0].rawValue) }
}.pickerStyle(.wheel)
Redux의 단위 컴포넌트가 State를 변경하기 위해서는 dispatcher을 통해 전역상태 Action을 관리하고 reducer를 통해서 Action을 실행해주어야 하지만,
Picker와 같은 컴포넌트는 selection의 Index값을 가르키는 Int형의 State 값을 불러오고, 이를 유저 ineteratction에 따라서 해당 State를 수정해줍니다. 즉 Binding된 값을 Get하는 것 뿐만 아니라 Set까지 해주고 있는거죠. Picker 자체에서 State 값을 변경시키는 것입니다.
양뱡향 Binding을 사용하는 component를 통해 앱에서 전역으로 사용되는 State를 변경은 반드시 Store을 통해서만 State의 값을 변경한다는 Redux의 원칙에 위배되고 사용하기에 힘든 상황이 됩니다. (애초에 Store에서 State를 Private(set)으로 State를 선언할테니 말이에요),
만약 이를 굳이 지역 State변수로 선언하고 기존의 Property Drilling 방식으로 해당 컴포넌트까지 State를 전달하게 된다면 코드의 일관성과 가독성이 매우 떨어지게 됩니다.
양방향 Binding component 문제의 애매한 해결 상황
Swift5에서 등장한 Property Wrapper을 통해 해당 문제 해결의 실마리를 찾을 수 있었습니다.
해당 라이브러리는 임시 State변수를 만들어서, Store에 있는 State값과 reducer을 통해 synchronize를 하면서 실제 타겟 State를 업데이트 하는 방식으로 양방향 Binding 문제를 해결하였습니다.
즉, 양방향 Binding을 사용하는 Component에서 변경하는 값을 Reducer을 통해 전역 State 변수까지 한번 더 변경해줌으로서 Redux의 원칙을 지키는 것이죠.
하지만 문제는 해당 라이브러리가 스스로 아직 프로덕션에 적용하기를 권장하지 않고 있고, 가장 최근 업데이트가 SwiftUI의 초기 버전이 등장할 때인 2년 전이라는 점에서 크게 발전을 이루지 못한 상황에 있습니다.
정말 사용하기에 부족한 라이브러리인지 혹은 너무 겸손한 라이브러리인지는 추후에 한번 사용해보고 후기를 작성해볼게요ㅎㅎ
GitHub - BenedictQ/SwiftUI-Bindable-Redux: This repo contains a Swift package for a Redux implementation allowing you to use Swi
This repo contains a Swift package for a Redux implementation allowing you to use SwiftUI data binding - GitHub - BenedictQ/SwiftUI-Bindable-Redux: This repo contains a Swift package for a Redux im...
github.com
2 - 2.
Local(Components) State 관리 문제
Redux는 전역 State관리를 중점으로 사용되는 아키텍처이기에 지역 State에 대해서는 또 다른 고민이 필요했습니다.
Local State의 단방향 데이터플로우를 강제할 수 없고 component간 property를 Binding해주는 도구가 없습니다.
React에는 Context API와 같은 Local State관리 도구가 존재하는 것 같은데, SwiftUI에서는 이를 어떻게 관리해야 할지 아직 답이 나오지 않은 상태인것 같습니다.
Use React Hooks & Context API to build a Redux style state container
Originally published on my blog. Update This approach is best suited for low frequency st...
dev.to
Local(Components) State 관리 나름대로의 해결 방안
고민 끝에 결론을 내린 것은 지역과 컴포넌트 단위에서 단방향 데이터 플로우 강제를 포기하고...
ObservableObject를 사용해서 StateHolder를 사용하는 것입니다. 우리가 MVVM에서 ViewModel을 사용했던 것처럼요!
차이점이 있다면 ViewModel의 (잘못된) 기존 역할과는 다르게 정말 여러 State를 쓰임에 따라 묶어주는 역할만 합니다.
또한 데이터 바인딩은 eventClosure을 사용해서 처리하는 것으로 결론을 내렸습니다.
자세한 방식은 다음 링크에서 확인할 수 있습니다.
Building a Responder Chain Using the SwiftUI View Hierarchy
Harnessing EnvironmentValues to easily respond to events generated throughout the view hierarchy
betterprogramming.pub
데이터 플로우까지 고려한 더 나은 방법이 있을까요??
이 부분은 더 나은 해결책을 찾아보도록 하겠습니다. 업데이트 해볼게요!
3. 어떤 프로젝트에서 어떻게 Redux를 적용하는 것이 좋을까?
일단 Redux는 소규모 프로젝트에는 굳이 필요하지 않다는 판단을 했습니다.
단방향 플로우와 비즈니스 로직을 한곳에 모아서 디버깅을 한다는 것은 단순한 앱에서는 노력 대비 효과가 떨어지기 때문이죠.
하지만 App 전체 상태를 컨트롤해줘야 하는 경우가 많은 경우, 그리고 페이지가 너무 많아 Binding이 타고타고 들어가야 하는 경우에는 위와 같이 Redux를 SwiftUI에 적용함에 있어서 먼저 해결해야 하는 부분들을 충분히 고민한다면 Redux가 도움이 될 수도 있겠다고 생각했습니다.
여전히 SwiftUI는 발전하고 있는 상황이기 때문에 올해 혹은 내년의 WWDC에서 더 좋은 대안이 나올 수 있기를 기대하면서 글을 마치겠습니다.
아래는 제가 Redux를 공부했던 자료들 중 도움이 되었던 자료를 남기겠습니다ㅎㅎ
Redux-like architecture with SwiftUI: Basics | Daniel Bernal
Learn the basics of Redux and how to implement in on your first iOS app, using Combine, SwiftUI and the latest XVode.
danielbernal.co
Redux-like architecture with SwiftUI: Side Effects » Daniel Bernal
Learn about Side Effects, Middleware functions and async tasks using the Redux architecture in iOS with SwiftUI and Combine.
danielbernal.co
Redux-like architecture with SwiftUI: Error Handling » Daniel Bernal
Learn how to properly handle errors in your SwiftUI app, using the Redux architecture in this advanced tutorial.
danielbernal.co
Redux-like architecture with SwiftUI: Real World App » Daniel Bernal
Check out Bouncer!. A simple open-source Redux based application writen in Swift and Combine, to demonstrate the Redux architecture pattern.
danielbernal.co
'Swift 아키텍처' 카테고리의 다른 글
[ReactorKit] (2) Reactorkit 내부 구조를 뜯어보고 사용하자 (0) | 2024.03.03 |
---|---|
[RIBs] RIBs 분석 (0) | 2024.01.08 |
[Reactorkit] (1) Reactorkit 도입기와 여전히 남은 고민들 (0) | 2023.08.06 |
[MVC] iOS의 MVC 아키텍처에 대해서 (0) | 2022.09.06 |
[Redux] SwiftUI에서 Redux 아키텍처 적용해보기 - 첫인상 (0) | 2022.04.18 |