⏱️타이머에서 생긴 성능 이슈와 해결 방법

김규리·2024년 12월 13일
1

타이머

목록 보기
3/3
post-thumbnail

타이머를 제작하면서 겪은 성능 이슈와 그 해결 과정이다..! 😥

사실 블로그 쓰려고 간단하게 만든 타이머였는데 자꾸 욕심이 생겨서 기능을 덧붙이다 보니..




1. requestAnimationFrame 성능 저하

1-1. 문제 상황

타이머가 제대로 실행되고 있는지, 오차는 없는지 타이머가 업데이트 될 때마다 화면에 [delay]값을 표기하는 기능이 있다.
requestAnimationFrame에서도 마찬가지로 타이머가 업데이트 될 때마다 화면에 delay값을 표기했는데..!

초기에는 정상적으로 16ms-17ms로 딜레이 값이 표기됐다.

그런데 점점 타이머의 시간이 흐를수록?

왜.. delay값이 300까지 늘어나는거지?
성능이 현저히 저하돼고 있었다.
분명 문제가 있다!



1-2. 원인 분석

개발자 도구의 성능탭에서 확인해보니 난리도 아니었다.

좀 더 확인해보니, 딜레이를 표기하기 위해서 화면에 표시하는 부분이 문제였다.

읽어보면 addDelayToList 부분에서 문제가 생기고 있다.

이 함수는 어떤 함수냐면, 딜레이를 표기하기 위해 계속 DOM에 <li> 태그를 추가하는 함수다.
이 함수 때문에

  1. 16-17ms 마다 DOM 조작으로 인해 강제 레이아웃이 발생하게 되고,
  2. DOM 트리의 크기가 지속적으로 증가하게 된다. (캡쳐된 화면을 보면 레이아웃이 필요한 노드가 12961개다...)
  3. 결과적으로 레이아웃 계산에 소요되는 시간이 점진적으로 증가한다.



1-3. 해결 방안

DOM 요소의 무한 증가를 방지하기 위해 <li> 태그가 무한정 늘어나지 않도록 수정했다.

최대 표시할 수 있는 <li> 태그의 개수를 제한하고 새로운 데이터가 추가될 때 가장 오래된 데이터를 제거하는 방식을 통해 requestAnimationFrame의 성능이 안정화 되고, delay값을 유지할 수 있게 됐다!




2. Web Worker의 부적절한 생명주기 관리

2-1. 문제 상황

처음에는 Worker를 이렇게 useEffect 밖에서 생성하여 타이머를 관리했다.

그런데, 성능 탭에서 확인해보니 매 초 타이머가 리렌더링 될 때마다 새로운 Worker가 생성되고 있었다. OMG😱



2-2. 원인 분석

컴포넌트 리렌더링마다 새로운 Worker가 생성되는 게 문제인데...
그렇다면 Worker 인스턴스를 어디서 생성해야할까?



2-3. 해결 방안

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를 생성하여 메모리를 낭비하지 않는다.





출처

profile
먹바눕

0개의 댓글