본문 바로가기
👨‍💻 프로그래밍/🍎 iOS 개발

사이드 프로젝트 장애 회고

by 개발자 진개미 2024. 12. 30.
반응형


발단...

틈틈이 주말마다 제가 출시한 앱들을 업데이트하고 있는데요. 어느 때와 다름없이 주말에 업데이트를 하고 편하게 쉬고 있었습니다. 그런데 다음날 이런 메일이 왔습니다.

그리고 앱에 들어가서 확인해 보니 정말로 그랬습니다! 🥲 💀


프로그래밍적 원인

원인은 의외로 간단했습니다. 제가 새로 추가한 기능이 이미 추가된 구독을 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가지 였습니다.

  1. 중요한 로직은 테스트를 짜서 돌립니다.
  2. 나머지 중요하지 않은 부수적인 로직이나 UI 관련은 직접 테스트합니다. 근데 보통 그 버전에서 추가/수정한 부분만 테스트 합니다.

여기서는 2번이 문제였습니다. 점점 앱이 복잡해져 가면서 영향이 있다고 생각하지 못했던 부분에서 영향이 생겼습니다. 이번에는 기술적으로 어렵다고 할 수 있는 Thread에서 문제가 발생했지만 이거보다 사소한 문제로, 이거보다 큰 영향도의 일이 (기존 데이터에 영향이 간다던가) 생기는 일은 시간문제였다고 생각합니다.


재발 방지책

일단 테스트를 잘하는 게 첫걸음이라 생각해 여러 테스트 케이스를 적고 있습니다. 앞으로는 최종 버전에서 이 모든 테스트를 끝내지 못하면 업데이트를 출시하지 않을 생각입니다.

iOS 앱은 한 번 나가면 업데이트를 하지 않는 이상 바꿀 수가 없습니다. 웹 개발에 익숙해져 있는 사고방식을 버리고 1번 1번의 Release에 책임감을 가져야 할 거 같습니다. 저도 믿기진 않지만 제 앱을 다운로드한 분들이 벌써 100분에 가까워지고 있기 때문에 돈을 받은 거에 대한 책임감은 져야 하지 않나 싶습니다.


 

반응형