[REFACTOR] CurriculumView Diffable 및 MVVM(Combine)-C로 리팩터링 (#179) #187
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
🌱 작업한 내용
🌱 PR Point
❗️cell내에 Button action이 여러번 불리는 문제 발생
Step 1.
기존에 정한 원칙(#165) 을 기반으로 delegate pattern대신 publisher를 활용해 cell에서의 버튼 이벤트를 상위 객체(ViewController)에게 넘겨줍니다.
기존의 방법대로 했을 때 cell내에 있는 버튼을 누를 때 버튼 이벤트가 동시에 여러번 발생하는 문제가 있었습니다.
근본적인 원인 파악: cancelBag에 stream이 누적되고 있음.
CollectionView는 여러개의 데이터를 효율적으로 보여주기 위해 미리 생성해놓은 Cell을 reuse하는 방식을 채택하고 있습니다.
기존의 Cell을 reuse하는 과정에서 새로운 데이터를 cell에 덧씌울 때 해당
setDataSource()
메서드가 불리게 되고, 이 때마다 새로운 stream이 생성되어 ViewController의 cancelBag에 계속해서 담기게 되어 cancelBag에 stream들이 누적해서 쌓이게 됩니다. (아래 사진 참고)이 때 cell내에 있는 cancelBag은 cell이 처음 init될 때, 생성된 1개의 stream을 각각 가지고 있고, 추가적으로 cancelBag의 요소가 늘어나지 않습니다.
즉, cell내에서의 버튼 이벤트는 한번만 발생하고 있지만, cell이 reuse되면서 setDataSource메서드내에서 새로 생성된 subscription들이 ViewController의 cancelBag에 저장되면서 여전히 stream이 살아있기 때문에 버튼 이벤트가 다수 발생하게 됩니다.
Step 2.
ViewController의
setDataSource
내에서 생성되는subscription
을 cell내부의cancelBag
에 보낸다고 하더라도, 처음 생성된 N개의 cell에 있는 각각의cancelBag
에 있는 stream들에게 버튼 이벤트가 전달됩니다. 따라서 생성된 cell이 3개고 각각의 cancelBag에 3, 4, 5개의 stream이 있다면 첫번째 cell을 눌렀을 때는 아래 print문이 3번, 두번째 cell은 4번, 세번째 cell은 5번 각각 또 다른 결과를 보입니다.결국 이 또한 stream을 붙들고 있는 위치만 바꿀뿐, 모든 stream이 살아있기 때문에 버튼 액션의 동시다발적인 전달을 막지는 못합니다.
Step 3.
따라서 지금 현재 사용되는 stream을 제외하고 나머지 stream들을 끊어줘야 합니다.
cell의
prepareForReuse()
생명주기를 이용해서setDataSource
로 cell을 reuse하기 이전에cancelBag
을removeAll
시킴으로써 나머지 stream들을 끊어주었습니다.위 사진을 보면 ViewController에서 store된 AnyCancellable과 cell 내부에서의 버튼 이벤트를 Sink하면서 생긴 AnyCancellable, 이렇게 2개로 cancelBag의 count가 2로 유지되는 것을 볼 수 있습니다. 재사용하기 위해 만들어진 기본 cell들입니다.
이 후
prepareForReuse
가 불리면서 cancelBag을 날리면서 0이 된 후에 1이 되는 것을 볼 수 있는데, 여기서 1은 ViewController에서 store된 AnyCancellable이고, Cell내에 버튼 이벤트를 Sink하고 있던 stream이 사라져prepareForReuse
가 불린 이후에는 어떤 cell의 버튼 이벤트를 감지할 수가 없게 됩니다.Step 4.
이를 해결하기 위해서는 cell내부에서 별도의 stream을 만들지 않아야 합니다. 별도의 stream을 만들지 않는 아래의 두가지 선택지가 있었습니다.
이 중 button의 접근제한자를 풀고 직접적으로 접근하는 것이 아닌 후자의 방식을 통해서 문제를 해결했습니다.
📮 관련 이슈