발단...
틈틈이 주말마다 제가 출시한 앱들을 업데이트하고 있는데요. 어느 때와 다름없이 주말에 업데이트를 하고 편하게 쉬고 있었습니다. 그런데 다음날 이런 메일이 왔습니다.
그리고 앱에 들어가서 확인해 보니 정말로 그랬습니다! 🥲 💀
프로그래밍적 원인
원인은 의외로 간단했습니다. 제가 새로 추가한 기능이 이미 추가된 구독을 UI에 표시해 주는 기능이었는데요.
그런데 추가됨을 판단하는 로직이 생각보다 복잡해서 (미리 이 기능을 고려해서 데이터에 추가하지 않았기 때문에) 이걸 Main Thread에서 돌리니 앱의 반응성이 너무 느려진다는 것이었습니다. 속도를 개선하기 위해서 여러 가지를 시도해 봤습니다.
- Lazy 하게 하기도 하고
- UserDefaults에 저장해서 Cache를 쓰기도 하고
- 계산 결과를 UI 진입 전에 무조건 미리 계산해 보기도 하고
근데 결국은 Main Thread에서 돌리는 거 자체가 UI의 반응성에 영향을 준다고 결론을 내려서 아무 생각 없이 다른 Thread에서 돌리게 했습니다.
DispatchQueue.global(qos: .userInitiated).async {
let computed = expenses.computeMatches(for: subscriptionVM.subscriptionByType.flatMap(\.value))
DispatchQueue.main.async {
if self.expenses == expenses && self.subscriptionVM.subscriptionByType == subscriptionVM.subscriptionByType {
self.precomputedMatches = computed
}
}
}
여기서 문제는... 저 expenses가 Swift Data에서 가져온 것이라는 겁니다... 일단 Swift Data는 Core Data의 Wrapper의 역할을 하는 건데 Core Data 전체가 Thread Safe 하지는 않습니다...
Using Core Data in the background | Apple Developer Documentation
Use Core Data in both a single-threaded and multithreaded app.
developer.apple.com
여기서 저는 사실 디버깅을 할 때 다른 Thread에서 접근만 하고 수정/삭제/추가는 안 하는데 문제가 되나? 했지만 Thread Safe하지 않으면 접근 하는 거 자체가 문제가 되는 모양입니다.
근본적인 원인
테스트 부족
원인은 변명할 여지도 없이 테스트 부족입니다. 여태까지 제 앱들의 테스트 방식은 2가지 였습니다.
- 중요한 로직은 테스트를 짜서 돌립니다.
- 나머지 중요하지 않은 부수적인 로직이나 UI 관련은 직접 테스트합니다. 근데 보통 그 버전에서 추가/수정한 부분만 테스트 합니다.
여기서는 2번이 문제였습니다. 점점 앱이 복잡해져 가면서 영향이 있다고 생각하지 못했던 부분에서 영향이 생겼습니다. 이번에는 기술적으로 어렵다고 할 수 있는 Thread에서 문제가 발생했지만 이거보다 사소한 문제로, 이거보다 큰 영향도의 일이 (기존 데이터에 영향이 간다던가) 생기는 일은 시간문제였다고 생각합니다.
재발 방지책
일단 테스트를 잘하는 게 첫걸음이라 생각해 여러 테스트 케이스를 적고 있습니다. 앞으로는 최종 버전에서 이 모든 테스트를 끝내지 못하면 업데이트를 출시하지 않을 생각입니다.
iOS 앱은 한 번 나가면 업데이트를 하지 않는 이상 바꿀 수가 없습니다. 웹 개발에 익숙해져 있는 사고방식을 버리고 1번 1번의 Release에 책임감을 가져야 할 거 같습니다. 저도 믿기진 않지만 제 앱을 다운로드한 분들이 벌써 100분에 가까워지고 있기 때문에 돈을 받은 거에 대한 책임감은 져야 하지 않나 싶습니다.
'👨💻 프로그래밍 > 🍎 iOS 개발' 카테고리의 다른 글
🍎 SwiftUI에서 Local Notification URL 처리하기 (0) | 2024.12.07 |
---|---|
🍎 Tipkit을 사용해서 앱의 숨겨진 기능을 유저에게 넛지 주기 (0) | 2024.09.23 |
🍎 Stanford iOS 강의 (CS193P) 수강 후기 (0) | 2024.09.14 |
🍎 Swift의 mutating 알아보기 (feat. Cannot assign to property: 'self' is immutable) (0) | 2024.09.01 |
🍎 Swift Testing: 보기 싫은 XCTest는 이제 안녕!! (0) | 2024.08.15 |