회사 프로젝트의 react-native 버전을 0.73.9에서 0.77.2로 업그레이드 하기로 결정했다. 내부적으로 New Architecture에 대한 관심은 꾸준히 있어왔고, 우선순위에 밀려 계속 딜레이되고 있었는데, 퇴사 전 마지막 프로젝트로 RN 버전업과 함께 NA 활성화 작업을 담당해보는게 어떻겠냐는 팀 리드님의 권유가 있었기 때문이다.
결론부터 말하자면 내부 일정 문제로 NA 활성화는 마무리 짓지 못했다. MS Codepush 서비스 종료에 따라 일찍이 StandAlone Codepush server를 구축해 놓았는데, New Architecture를 활성화하려면 NA를 공식지원하지 않는 Codepush를 들어내고 서드파티 OTA를 도입해야했다. 또한 NA를 활성화하는 순간 단순 UI 깨짐 현상 뿐 아니라 기능적으로도 어디서 문제가 생길지 예측할 수 없기 때문에 앱 전체적으로 강도 높은 QA가 필수적이었다. 당시 회사 사정상 매출을 위한 스프린트가 빼곡하게 진행되고 있었고, 배포 일정도 빠듯하게 잡힌 상황에서 상대적으로 긴급도가 떨어지는 내부 개선 작업에 QA 리소스를 추가 할당해달라고 요청하기 어려웠다. 이런 이유로 결국 NA는 비활성화한 채로 react-native 버전만 업그레이드하기로 결정했다.
위 의사결정에 따라 Old Architecture를 유지한 채로 버전 마이그레이션을 완료했고, 최종적으로 자체 QA 진행 중에 이상한 문제를 발견했다. Android에서 Codepush 업데이트 완료 이후 재시작하는 과정에서 앱이 멈추거나 충돌이 발생했다.
bundle 다운로드 이후 즉시 재시작하여 bundle을 반영할 때만 나타나는 문제였으며, 다음 앱 시작 시 bundle 적용 옵션을 선택하면 문제는 발생하지 않았다. react-native 버전업 이외에 Codepush 관련 코드는 수정한 이력이 없기 때문에 막연히 Codepush와 RN 0.77에서 호환성 이슈가 있는 것은 아닐까 판단했고, Codepush android 네이티브 모듈을 살펴보면서 디버깅을 시도했다.
최종적으로 검토를 마친 결과 Codepush 네이티브 코드는 모두 정상적으로 실행되었는데, 앱을 재시작하며 다운로드한 bundle을 로드하는 과정에서 멈춤 이슈가 반복되고 있었다. 이제 남은 유일한 단서는 멈춤이 발생할 때 Android Studio LogCat에 반복적으로 찍히는 아래 Error Log 뿐이었다.
com.facebook.react.bridge.ReactNoCrashSoftExCeption: Cannot get UIManager because the context doesn't contain an active CatalystInstance.
관련 키워드로 열심히 서치한 결과 동일한 문제가 발생한다는 react native repo issue를 발견했다. 아예 동일한 트리거는 아니었지만 RN 0.77 버전대 Old Architecture Android 앱에서 재시작 시 발생하는 이슈라는 점에서 관련이 있을게 분명했다. 이 내용을 기반으로 Codepush android 코드에서 앱 재시작을 트리거하는 loadBundle 메서드를 확인해 보니 크게 두 가지 방법으로 앱을 재시작해서 다운로드한 bundle을 적용하고 있음을 확인했다.
Soft Restart
native app의 instance는 그대로 유지한 채로 그 위에 화면으로 띄우는 react context만 새롭게 구성해 갈아끼우는 방식으로 빠르게 RN앱을 재시작한다.
Hard Restart
android native 앱 자체를 재시작한다. Codepush 라이브러리에는 loadBundleLegacy라는 이름으로 메서드가 구현되어 있다.
Codepush에서 즉시 설치 옵션으로 bundle을 다운로드하면 loadBundle 메서드가 실행되며, 기본적으로 Soft Restart가 호출된다. 여기서 error가 발생하면 catch문으로 Hard Restart가 호출되는 구조로 구성되어 있다. 그런데 여기서 모종의 이유로 Soft Restart에서 문제가 발생했고, 앱 자체가 멈춰버리며 exception 조차 실행되지 않게 되는 것으로 추정했다.
가설을 검증하기 위해 항상 loadBunldeLegacy를 실행하여 앱 재시작을 트리거하니 체감되는 성능 저하 없이 정상적으로 동작하는 것을 확인할 수 있었다.
회사 일정으로 인해 마무리 짓지 못하고 PR만 올려놓은 채 퇴사하게 되어 조금 아쉬운 마음이 있다. 또한 위에 링크한 이슈에서 볼 수 있듯이 RN 0.77에서 발생하는 고질적인 문제인 것으로 보이기 때문에 일시적인 반창고 수준이라고 생각한다. 이에 RN 자체가 내포한 불안정성으로 인해 QA를 엄격하게 진행해야 할 것 같다는 의견과 장기적으로는 Hot Updater나 Revopush와 같이 요즘 라이징하는 서드파티 OTA 라이브러리로 전환하는 건에 대해서 적극적으로 고려해보는건 어떻냐는 의견을 남기고 나올 수밖에 없었다. 그래도 덕분에 최신 react-native 버전의 breaking points와 Codepush 네이티브 코드가 어떻게 구성되어 있는지 자세하게 살펴볼 수 있는 기회가 되었던 것 같아 개인적으로는 유익한 시간이었다고 회상한다.(MS가 코드는 진짜 깔끔하게 잘 짜는구나 싶었다.)