1초마다 화면에 데이터 400개 업데이트하기

yeshyungseok·2024년 4월 23일
post-thumbnail

가상화폐 실시간 시세 차액 테이블

실시간으로 거래소마다 가상화폐 시세 차액을 보여주는 사이트를 구현하려고 한다.

목표는 4개의 거래소(업비트, 빗썸, 바이낸스, 바이비트)에서 시세를 받아와 각 거래소마다 시세 차액을 보여주고, 가장 큰 시세 차액이 나는 거래소를 보여주는 것이다.

이를 달성하기 위해서는 실시간으로 시세 변동을 알아야하고 데이터를 받아오기 위해 각 거래소마다 웹소켓을 연결해야 한다.

즉, 초당 평균적으로 100여 개의 데이터를 전송하는 웹소켓 4개를 동시에 연결 및 관리해야 하며, 중요한 것은 어떻게 이 데이터들을 효율적으로 화면에 보여줄 것이냐이다.

상태 관리

리액트에서 배열로 된 가상화폐 목록을 시각화하려면 보통 map 고차함수를 많이 사용한다. 그리고 반환된 각 컴포넌트(가상화폐)에 대한 거래액을 상태로 관리하는 것이 일반적이다.

하지만 실시간으로 수 백 번의 상태 변경이 이루어진다는 것은 그만큼 리렌더링을 유발하기 때문에 성능적으로 보았을 때 적절해보이지는 않는다.

물론 상태 변경 횟수 == 리렌더링 횟수를 의미하지는 않는다.

리액트 내부적으로 가상 DOM을 diffing할 때는, batch update라는 과정을 통해 상태를 한번에 업데이트 한 이후 DOM을 업데이트하기 때문에 여러 번의 상태 업데이트가 발생한다면 이를 몰아서 처리하게 된다.

하지만 이를 감안하더라도 상태 관리로 이를 해결하는 것은 좋은 옵션으로 보이지는 않는다. 1초에 400번의 상태 업데이트가 발생하더라도, 리액트는 이를 최대한 적은 렌더링으로 처리하도록 노력한다.

하지만, 배치 업데이트가 항상 완벽하게 작동하는 것은 아니며, 변경되는 데이터 양이 많으면 오히려 더 많은 리렌더링이 발생할 수 있다.

캔버스 API

다음으로 생각해본 방법은 Canvas API를 활용하는 것이다.

Canvas API는 DOM 요소와 달리, DOM 트리와 같은 중간 표현 없이 직접적으로 픽셀 버퍼를 조작한다. 이는 렌더링 과정에서 발생하는 불필요한 오버헤드를 줄여 속도를 향상시킨다. 즉, 리렌더링을 발생시키지 않으며 이는 DOM 요소를 조작하는 것보다 훨씬 빠른 속도를 가능하게 한다.

그렇다고 해서 Canvas를 활용하는 것이 상태 관리보다 이상적인 방법이라고 할 수는 없다. Canvas를 활용하여 테이블을 모든 요소를 그려주기 위해서는 그만큼 더 복잡한 구현 난이도가 요구된다.

상태가 업데이트되면 자동으로 리렌더를 유발하는 리액트의 강력한 기능을 포기해야할 뿐더러, 리렌더링 로직을 직접 구현해야 하기에 일반 상태 관리 코드와 일관성이 떨어져 유지보수에도 좋지 않을 수 있다.

가장 치명적인 것은

캔버스로 가상화폐 정보들에 대한 텍스트까지 렌더링하게 되면 이는 HTML 태그로 인식되는 것이 아닌, 캔버스 위 뿌려진 픽셀들에 불과하게 된다. 즉, SEO에 굉장히 치명적이다. (직접 다 구현하고 테스트를 해보면서 그제야 깨달았다,,🙄)

이러한 이유로 Canvas API를 사용한다는 아이디어 또한 폐기하게 된다.

textContent

상태 관리와 Canvas의 문제점, 즉 지나친 리렌더링 유발 문제와 SEO 문제를 해결해 줄 방법이 필요했다. 그리고 생각난 것이 DOM node의 textContent를 직접 조작하는 것이었다.

DOM의 textContent 속성은 요소의 모든 하위 텍스트들을 읽을 수 있을 뿐더러 그 값을 직접적으로 변경해 줄 수 있다.

// 전 : <div id="myElement">바뀌기 전입니다</div>

const element = document.getElementById('myElement');
const textContent = element.textContent;
textContent = "바뀌었습니다";

// 후 : <div id="myElement">바뀌었습니다<div>

때문에 이로써 SEO 최적화에 대한 문제는 해결되었다.

중요한 것은 성능인데, 성능적으로도 textContent 속성을 활용하는 것은 굉장히 우월하다.

우선 일반적인 리액트의 virtual DOM 방식을 활용한 렌더링 방식과 비교해보자.

virtual DOM과 실제 DOM을 비교하고 차이를 계산하는데 많은 비용이 든다. 하지만 textContent 속성을 통해 DOM을 직접적으로 조작하면 이러한 과정을 우회할 수 있다. DOM을 새로 만드는 것이 아닌 직접 DOM node에 텍스트를 작성하기 때문에 리렌더가 발생하지 않는다.

Canvas API와 비교했을 때에도 훨씬 우수한 성능을 보여준다.

특히 performance API를 활용해 canvas의 fillText와 textContent를 비교했을 때 textContent가 최소 2배에서 최대 12배까지도 높은 성능을 보여준다.

(간단한 문자열 한줄을 렌더링 하는 과정의 성능 차이를 보여주므로, 실제 프로덕션 환경과 프로젝트 구조, 브라우저 환경에 따라 조금씩 수치가 다를 수는 있을 것이다)

textContent가 무조건 좋은가?

그렇다고 말할 수는 없다.

우선 리액트 공식 문서에서도 직접적인 DOM 조작을 지양하라고 얘기한다.

리액트의 훅과 API들을 활용하지 않고 직접적인 DOM 개입이 잦아진다면, 리액트의 장점을 활용하기도 힘들 뿐더러 유지 보수성 또한 나빠질 수 있다.

때문에 textContent 속성을 활용하는 것은 실시간으로 빠르게 데이터를 변경해야 하는 경우, 그리고 그 데이터가 단순 문자열일 경우에만 활용하는 것이 좋을 것이다.

특수한 상황에는 직접적인 DOM 조작을 고려할 수 있지만 최대한 지양하는 것이 맞는 것 같다.

profile
FE 개발자

0개의 댓글