내용 정리
프로젝트를 진행하던 중 UI 버그가 일어나서 해결하다가 PrepareForReuse에 대해 다시 공부해보다...
프로젝트를 진행하던 중 UI에 버그가 발견되었다.

셀의 하단에 요일이 중복되어 표시되는 버그가 발생한 것이다...
이 버그는 처음부터 발생하지는 않고, 셀을 스크롤해서 재사용 되거나 새로운 셀을 추가하면 발생하는 버그였다.
왜 이런 버그가 발생하는지 원인을 찾아 브레이크 포인트를 여기저기 걸고 다녔는데,
발견한 원인은 PrepareForReuse에 있었다.
// 셀 재사용 옵션
override func prepareForReuse() {
super.prepareForReuse()
setupUI()
}
기존에 내가 작성한 코드는 위와 같은 형식이었는데, 저 setupUI() 메소드가 문제였다.
setupUI() 메소드는 말 그대로 셀의 UI를 모두 설정하는 메소드인데, 저 메소드 안에서는 addSubView(_:) 메소드와 오토레이아웃을 설정하는 메소드도 포함이 되어있다.
때문에, 셀이 재사용 될 때마다 새로운 뷰를 추가하고 오토레이아웃을 설정하고... 이런 과정이 반복되는 탓에 위의 사진처럼 UI가 중복되어 표시되거나 중첩되어 보이는 버그가 발생한 것이다.
원인을 파악한 후에는 아래와 같이 코드를 바꾸어 주었다.
// 셀 재사용 옵션
override func prepareForReuse() {
super.prepareForReuse()
prepareForReuseData()
}
func prepareForReuseData() {
self.weekdaysStatus.accept([])
self.timeLabel.text = ""
self.note.text = ""
}
PrepareForReuse만을 위한 메소드를 하나 만들고, 메소드에서는 셀의 UI에 필요한 데이터를 초기화 시키도록 구현했다.
이렇게 재사용 옵션을 수정하니 아래와 같이 UI 버그가 사라졌다.

그래서 내가 지금까지 PrepareForReuse에 대해 오해를 하고 있었던 것 같아 이번 기회에 다시 공부를 해보았다.
PrepareForReuse는 UIKit의 UITableViewCell과 UICollectionViewCell에서 사용되는 메소드로, 재사용을 위해 셀이 초기화되는 시점에 호출된다.
재사용 큐를 사용하는 테이블뷰와 컬렉션뷰의 성능을 최적화하는 데 필수적인 역할이다.
테이블뷰나 컬렉션뷰의 셀은 스크롤하면서 계속 생성되는 것이 아니라 재사용(reuse)되기 때문에, 이전 데이터나 상태를 초기화하지 않으면 셀이 이상하게 보이거나 잘못된 데이터를 보여주는 등 버그가 발생할 수 있다.
또, 이미지 다운로드 같은 비동기 작업이 완료되기 전에 셀이 재사용되면, 잘못된 이미지를 표시할 가능성이 있다.
때문에 PrepareForReuse를 통해 이전 셀의 데이터나 상태를 초기화하여 셀의 UI 구성 요소를 새 데이터로 업데이트하기 전에 리셋하는 것이다.
PrepareForReuse를 사용하여 반드시 초기화를 해주는 것이 좋은 것들은 아래 3가지가 있다.
override func prepareForReuse() {
super.prepareForReuse()
myLabel.text = nil
myImageView.image = nil
}
override func prepareForReuse() {
super.prepareForReuse()
mySwitch.isOn = false
myCheckbox.isChecked = false
}
override func prepareForReuse() {
super.prepareForReuse()
imageDownloadTask?.cancel()
imageDownloadTask = nil
}
위의 3가지 작업은 PrepareForReuse를 사용하여 반드시 초기화를 해주는 것이 좋다. 그렇지 않으면 위에서 언급한 버그들이 발생할 수 있기 때문이다.
RxSwift에서 DisposeBag을 초기화해서 불필요한 바인딩을 방지할 수 있다.
override func prepareForReuse() {
super.prepareForReuse()
disposeBag = DisposeBag() // Re-initialize
}
셀 재사용시 disposeBag을 다시 초기화 시켜주는 것인데, 이는 셀의 재사용 시 불필요한 Rx 바인딩을 해제하기 위해 꼭 필요한 패턴이다.
RxSwift를 사용하면 컬렉션뷰나 테이블뷰의 데이터소스를 바인딩할 수도 있고, 델리게이트를 사용하지 않고도 셀을 선택하거나 특정 동작에 대한 것들을 바인딩할 수 있다.
그런데, 셀은 재사용되기 때문에 이전의 Rx 구독이 남아 있다면 새 데이터와 엉킨 상태로 잘못된 동작을 할 가능성이 높다.
때문에 셀을 재사용할 때 disposeBag을 새로 초기화하여 기존 구독을 모두 해제한 후 새로운 구독만 관리할 수 있도록 하는 것이다.
이를 통해 메모리 누수와 중복 구독을 방지할 수 있다.
이런 간단한 이유 때문에 UI가 버그가 발생하다니... 정말 가볍게 공부할 내용은 하나도 없는 것 같다.
그래도 이런 버그 덕분에 또 공부를 하고, 알아가는 것이 생기니까 좋다고 생각한다.
그치만...
아직도 처리해야할 UI 버그는 많다...

이런 UI 겹침 문제라던지...
남은 프로젝트도 파이팅...ㅎ
RxSwift가 문제야