최근 이직을 하게 되었다. 새로운 회사에서 코드를 살펴보니 최적화가 전혀 되어있지 않았다. 다른 문제들도 많이 보였는데 최적화는 앱이 전체적으로 너무 느리고 버벅였던 원인 중 하나였다. 하지만 이런 상황에서 기회로 삼아 배운 것들을 적용하여 성능을 개선하는 것 또한 재미도 있어 보이고 나의 커리어에 큰 도움이 될거 라고 생각이 들어 해보자고 결정하게 되었다. (사실 코드 퀄리티가 생각보다 심각하긴 했다...)
앱 상태는 생각보다 심각했다. 지속적으로 앱을 사용했을 때 이런 수치가 나왔다.
엄청난 메모리 누수와 CPU를 오버하게 사용하고 있었다. 개인적으로 CPU가 저렇게 사용이 가능한지 몰랐다. 이렇다 보니 기기에서 발열이 생기고 버벅이고 전체적으로 속도가 느렸다.
헤더 컴포넌트와 바디 컴포넌트의 데이터가 상관관계가 없음에도 불구하고, 부모 컴포넌트에서 데이터를 패칭하고 자식 컴포넌트로 전달하는 방식으로 구현되어 있어, 데이터 변경 시 불필요한 리렌더링이 발생했다.
useMemo, useCallback, Memo를 적절히 활용하지 않아 값이 비싼 계산이 반복되었다. 그 예시로 FlatList의 renderItem 함수와 같이 UI에 영향을 많이 주는 함수에도 Memoization이 부족했다.
이벤트 리스너를 등록했지만 unMount 시에 제거하지 않아 메모리 누수가 발생했다. 이런 리스너들이 한 두개가 아닌 여러개가 발견 되었었다.
렌더링 함수 내에서 컴포넌트를 생성하는 방식이나, 필요없는 주석 처리된 코드 등의 가독성을 저해하는 불필요한 코드들이 많이 있었다.
부모와 자식 컴포넌트를 옳바르게 분리하고 각 컴포넌트에서 필요한 데이터를 해당 컴포넌트에서 패칭하도록 변경하여, 불필요한 데이터 전달과 리렌더링을 줄였다.
useMemo, useCallback, Memo를 적절히 사용하여 값이 비싼 계산을 최소화하고, 불필요한 리렌더링을 방지하였다.
모든 이벤트 리스너를 unMount 시에 제거하여 메모리 누수를 막았다.
불필요한 코드를 수정 및 삭제하고, 주석된 코드들도 모두 삭제했다. 그리고 Switch와 if-else문을 활용하여 가독성을 높였다.
개선 방법
문제점을 파악했으니 그대로 작업 하면 된다.
1. 부모와 자식 컴포넌트들을 제대로 나누고 각 컴포넌트에서 필요한 데이터를 패칭한다.
2. useMemo, useCallback, Memo를 한다.
useMemo
useCallback
Memo
3. 이벤트 리스너 제거
CPU는 100% 위로 여전히 높지만 50%로 줄였고 Memory 85% 정도로 확연하게 줄어들었다. 속도도 전에 비해 굉장히 빨라졌는데 회사 직원분중에 한분이 안드로이드 사용자인데 새로운 앱이 된 것 같다는 말씀과 감사하다는 말씀을 해주셔서 너무 뿌듯했다. 그리고 CPU가 여전히 높은 이유는 다른 이유가 있었다. 이 문제는 다음 글에 문제와 해결 방법을 공유하려고 한다.
성능 최적화는 앱의 사용성을 향상시키고 사용자 만족도를 높이는 데 중요한 요소이다. 문제를 파악하고 개선하는 과정은 어렵고 복잡하지만, 지금까지의 경험으로 보다 쉽게 문제를 파악하고 개선 할 수 있었다. 이번 기회를 통해 내가 직접 앱을 최적화 하고 그 결과를 눈으로 볼 수 있는 기회가 될 수 있었다. 앞으로도 지속적인 코드 리팩토링과 성능 최적화를 통해 우리의 앱을 더욱 효율적으로 만들어 나가 도록 노력해야겠다.