다국어 채팅 앱을 개발하던 당시 겪었던 로딩속도 개선 프로젝트에 대해 정리한 글 입니다.
우리는 채팅에 번역시스템을 추가한 서비스를 개발하고있었다.
채팅 앱이라면 빨리 답장해야하는 만큼 로딩속도가 중요한데 기존 앱의 로딩속도는 최소 40초에서 길면 5분까지도 걸렸다.
배치 처리가 되어 있지 않아 정말 문제가 많았다.
로딩속도 개선의 목표는 3초에서 5초 이내로 잡고 로딩속도 개선 프로젝트에 참여하게 되었다.
10가지라는 숫자가 그렇게 놀랍지 않을 수도 있지만 5번과 10번의 경우 로그인 마다 요청 횟수만도 엄청 많아서 한번 로그인할 때 많으면 정말 100개도 넘게 API를 보내고 있었다. (앱을 사용하지 않고 있는 동안 번역해야하는 메시지가 온 만큼 하나씩 번역 요청을 보냈으니.. 정말 한계가 없었던 수준이다. 😂)
신입이었던 내 눈에도 이 과정은 너무나도 비효율적이었다.
😒 ... API의 수가 너무 많은 것 같은데... 이거 통합해도 무리 없을 것 같은데 통합하는건 어떨까요?
감사히 내 의견은 받아들여졌다..!
그리고 멋진 시니어분들의 의견이 쌓여 멋지게 구조를 바꿀 수 있었다.
API중 단순히 정보를 받아오는 API의 경우, 하나로 통합이 되었다.
그 결과 대부분의 API 들이 통합이 되었다.
통합 로그인 API의 전송부터 수신까지 평균 15초(15000ms) 정도 걸리게 되었다.
이전보다는 분명 개선되었다는 것이 체감이 되었다.
하지만 목표치인 3~5초에는 한참 못미쳤다.
서버측에서 시간이 가장 오래 걸리는 이유는 역시, 번역 API 때문이었다.
언어마다 사용하는 API가 달랐는데, 이 중 특정 API는 하나의 번역 송수신에 약 0.7초정도나 필요하였다.
여전히 번역해야하는 수 만큼 로딩 시간도 비례한 문제점도 있었다.
예를 들어, 로그인 시도했을 때 그동안 수신된 채팅이 30개라면 번역에만 20초 정도를 소모해야하는 것이다.
하지만 번역을 굳이 로그인할 때 모두 할 필요는 없으므로 채팅을 보낼 때 서버에서 미리 번역을 해놓아 로그인 과정에서 번역을 하지 않아도 되도록 개선하였다.
통합 로그인 API의 전송부터 수신까지 평균 100~200ms로 현저하게 줄일 수 있게 되었다!
이것만 봐서는 그렇게 큰 문제는 없지 않을까? 라고 생각할 수 있다. 하지만문제는 따로 있었다.
대부분의 데이터를 저 세곳 모두 똑같이 저장을 한다는 점이다. 예를 들어,
친구 목록에 관한 데이터를 저장한다고 하면 Redux에 저장하고 SQLite에도 저장을 하고 전역 변수에도 저장을 한다는 뜻이다.
로그인 과정에서 발생하는 모든 데이터의 업데이트도 저렇게 세곳 모두 업데이트를 계속 한다.
하지만 저렇게 쓴 이유는 레거시 코드이기 때문에 명확히 알 수는 없지만 추측을 하자면
1. 상태관리를 하기 위해 Redux를 사용했다.
2. 앱을 종료했다가 다시 켜도 유지되어야하는 정보를 저장하기 위해 SQLite를 사용했다. (일반적인 Redux는 유지되지 않는다.)
3. 모든 정보를 Redux와 SQLite에 넣을 수 없으니 전역 변수에도 저장하여 개발 용이성을 높인다.
Redux-Persist를 사용하여 Redux의 정보가 사라지지 않도록 하면 SQLite를 사용하지 않아도 된다!
또한 상태관리까지 한번에 할 수 있다.
Redux-Persist를 사용한다면 SQLite를 굳이 사용할 이유가 없기 때문에 프로젝트에서 삭제하기로 하였다.
실제로 React Native에서는 redux-persist를 SQLite보다 월등하게 많이 사용하고 있다.
일부 데이터는 AsyncStorage를 사용하고 있었는데... (몇몇 설정에 관한 정보)
굳이 AsnycStorage를 따로 써야하는 이유를 찾지 못했다.
결론 : SQLite와 AsyncStorage를 사용하는 코드는 삭제하였다.
기존에 앱을 재시작해도 유지가 되어야 하는 데이터의 경우에는 SQLite에 넣었다. 그리고 그런 데이터의 경우에는 대부분 상태관리도 필요하기 때문에 Redux에도 넣었다.
Redux Persist를 사용하면 SQLite에 넣는 과정없이 상태관리와 데이터 저장을 한번에 할 수 있어서 효율적이다.
Redux를 Redux Persist로 바꾸는 과정은 그리 어렵지 않았다.
궁금하신 분들은 링크 에서 확인해보시면 될것 같습니다! 😊
그 결과, 평균 로딩시간이 기존 40초~5분에서 현재 3~5초로 90%이상 단축했다. :)
비록 딱 같은 유저가 기존 버전에서는 ##초가 걸렸는데 지금은 #초가 걸린다!
같은 자료는 없지만 정말 개편 이전에는 하루 웬종일 로딩창을 봐야 끝났는데 이제는 잠깐만 기다려도 로딩이 끝나는 점이 너무 좋다. 실제로, 로딩이 너무 길어서 로딩이 오류가 난게 아닌가? 하고 개발자들 마저 오해하는 상황도 많을 정도로 로딩이 정~말 오래걸렸었다. 몇초가 아니라 분단위로 로딩이 필요했다... 하지만 이제는 길어도 10초안에는 로딩이 되어서 체감이 커서 좋다. 단순히 UI를 더 그리고 바꾸고 기능을 하나 더 구현하는 것보다 성능 튜닝을 위해 고민하고 개발하고 테스트할 때가 훨씬 재미있었던 것 같다. 유익한 시간이라고 생각한다 👍
API하는데만 몇초정도 걸리고, 클라이언트 단에서는 어디서 얼마나 걸리는지, 기존 시간이 정확히 평균 시간이 어느 상황에서 어느 정도나 걸리는지 등을 분석하지 않고 바로 개편부터 한 점이 아쉽다.
API만 수정했을때 얼마나 단축되고, 내부 데이터 저장 구조만 개편 했을때 얼마나 단축되는지 미리 분석하지 않은 점이 너무 아쉽다.
지금 이 글을 작성하면서 성능 개선에 관한 글에는 이런 정확한 수치가 상당히 중요하다는 점을 알아 슬플 뿐이다.
처음에는
아니 그냥 ~ SQLite, AsyncStorage쓰던거 redux-persist로 바꾸면 되는거니까
오래 걸려도 2주면 되겠지 😗~
라고 생각했다.
하지만 현실은 많이 달랐다.
구조를 바꿈으로서 사소한 모든 기능을 다시 개발해야 했다.
앱을 새로 만들듯 다시 해야 했다.
QA도 생각보다 엄청 많이 나오고... 2주면 될 줄 알았는데 기존 기능을 다 구현하고 크리티컬 버그까지 전부 수정하는 데에는 2달이 넘는 시간이 필요했다.
데이터 구조를 바꾼다는 것은 정말 신중하게 고려하고 해야하는 점 같다. 그냥 무턱대고 성능에 나쁘다고 막 바꾸는 행동은 위험하다.
참고자료 😎