타이머를 제작하면서 겪은 성능 이슈와 그 해결 과정이다..! 😥
사실 블로그 쓰려고 간단하게 만든 타이머였는데 자꾸 욕심이 생겨서 기능을 덧붙이다 보니..
타이머가 제대로 실행되고 있는지, 오차는 없는지 타이머가 업데이트 될 때마다 화면에 [delay]값을 표기하는 기능이 있다.
requestAnimationFrame에서도 마찬가지로 타이머가 업데이트 될 때마다 화면에 delay값을 표기했는데..!
초기에는 정상적으로 16ms-17ms로 딜레이 값이 표기됐다.
그런데 점점 타이머의 시간이 흐를수록?
왜.. delay값이 300까지 늘어나는거지?
성능이 현저히 저하돼고 있었다.
분명 문제가 있다!
개발자 도구의 성능탭에서 확인해보니 난리도 아니었다.
좀 더 확인해보니, 딜레이를 표기하기 위해서 화면에 표시하는 부분이 문제였다.
읽어보면 addDelayToList
부분에서 문제가 생기고 있다.
이 함수는 어떤 함수냐면, 딜레이를 표기하기 위해 계속 DOM에 <li>
태그를 추가하는 함수다.
이 함수 때문에
DOM 요소의 무한 증가를 방지하기 위해 <li>
태그가 무한정 늘어나지 않도록 수정했다.
최대 표시할 수 있는 <li>
태그의 개수를 제한하고 새로운 데이터가 추가될 때 가장 오래된 데이터를 제거하는 방식을 통해 requestAnimationFrame의 성능이 안정화 되고, delay값을 유지할 수 있게 됐다!
처음에는 Worker를 이렇게 useEffect 밖에서 생성하여 타이머를 관리했다.
그런데, 성능 탭에서 확인해보니 매 초 타이머가 리렌더링 될 때마다 새로운 Worker가 생성되고 있었다. OMG😱
컴포넌트 리렌더링마다 새로운 Worker가 생성되는 게 문제인데...
그렇다면 Worker 인스턴스를 어디서 생성해야할까?
2-3-1. 컴포넌트 밖에서 Worker 선언
아예 컴포넌트 밖에서 Web Worker 인스턴스가 생성되도록 수정해보았다.
초기에 딱 한 번 생성되므로 해결된 듯 하지만 문제가 있다.
당장 worker가 필요하지 않아도 Worker 인스턴스가 초기에 생성되어 계속 메모리를 차지한다.
이미지의 오른쪽 탭을 보면, 현재 setInterval 타이머를 실행중임에도 worker가 존재하는 것을 확인할 수 있다.
즉, web worker timer를 실행하지 않아도 워커가 계속 별도의 스레드로 존재하며 메모리 공간을 차지하는 문제점이 있다.
2-3-2. useEffect 안에서 worker를 선언
Web Worker를 useEffect 안으로 옮겼다.
그리고 이 컴포넌트가 언마운트될 때, Web Worker를 종료하도록 수정했다.
성능 탭에서, Worker가 하나만 존재하는 것을 확인할 수 있었다.
또한, setInterval과 같이 당장 Worker가 필요없는 경우에는 메모리에서 해제하고 Web Worker를 이용한 타이머를 동작할 때만 Worker를 생성하여 메모리를 낭비하지 않는다.
출처