티스토리 뷰
프로젝트 기능을 모두 구현한 후 오늘도 어김없이 평화롭게 그동안 미뤄왔던 리팩토링을 진행했습니다.
오늘은 react-scan를 이용해 불필요하게 렌더링되는 요소를 찾고 리팩토링을 진행할 것입니다.
왜 그동안 사용했던 Profiler 대신에 react-scan을 쓰느냐...
정말로 리렌더링될 이유가 단 하나도 없는 컴포넌트가 로그인 유효시간을 나타내주는 컴포넌트가 리렌더링될 때마다 렌더링 되는 것으로 나타났기 때문입니다.

// ExpireTimer.jsx
const ExpireTimer = () => {
const { accessToken, expiresInTime, setExpiresInTime, logout, restoreSession} = useAuthStore();
useEffect(() => {
restoreSession();
}, []);
useEffect(() => {
if (!expiresInTime) return;
const interval = setInterval(() => {
if (expiresInTime === 1) {
authValidation(accessToken).catch(() => {
alert("토큰이 만료되었습니다. 다시 로그인 해주세요.");
logout();
});
}
setExpiresInTime(expiresInTime - 1);
}, 1000);
return () => clearInterval(interval);
}, [expiresInTime]);
return (
<span className="text-sm hidden md:block">{formatTime(expiresInTime)}</span>
);
};
export default ExpireTimer;
// Home.jsx
import React from "react";
import { Link } from "react-router-dom";
const Home = () => {
return (
// 상태 변화와 전혀 관련 없는 코드
);
};
export default React.memo(Home);
현재 useAuthStore로 관리하고 있는 상태가 변화하는 곳은 ExpireTimer일 뿐 Home에서는 변화하고 있지 않습니다. 하지만 Profiler로 확인할 결과 계속해서 Home도 리렌더링이 발생하는 것을 확인할 수 있습니다.
이 문제를 해결하기 위해 도움을 요청했는데, Profiler가 오랫동안 업데이트되지 않아 대신 react-scan이라는 툴을 사용해보라는 추천을 받았습니다.
react-scan 적용 방법
index.html의 <head> 내에 다음 스크립트를 추가합니다.
<script src="https://unpkg.com/react-scan/dist/auto.global.js"></script>
package.json의 scripts 항목에 아래 스크립트를 추가합니다.
"scripts": {
"scan": "vite & npx react-scan@latest localhost:5173"
}
이후 yarn scan으로 파일을 실행시켜 봅시다.

이전과는 다르게 ExpireTimer만 리렌더링 되는 모습을 확인할 수 있습니다.
Profiler와 react-scan의 차이
이전까지는 Profiler를 통해 Home이 리렌더링된다고 판단했지만, react-scan을 실행한 결과 ExpireTimer만 리렌더링되고, Home은 리렌더링되지 않는 것으로 나타났습니다.
이는 Profiler가 오래된 버전에서 업데이트되지 않고 있습니다. 실제로 Profiler는 React 17 이후로 거의 개선되지 않았으며, 최신 React 환경에서는 정확한 리렌더링 정보를 제공하지 못하는 경우가 있습니다.
결과적으로 react-scan을 활용한 것이 더 정확한 분석을 가능하게 했으며, Profiler보다 최신 도구를 사용하는 것이 중요하다는 점을 확인할 수 있었습니다.
선택적 구독을 통한 리렌더링 최적화
이제 어떤 컴포넌트가 리렌더링 되는지 정확히 파악 가능하니 어떠한 부분에서 최적화를 시킬 수 있는지 알아봅시다.

현재 Test.jsx에서는 모든 state를 구독하고 있습니다.
const { user } = useAuthStore((state) => state);
이렇게 하면 user의 상태 변화가 없더라도, useAuthStore로 관리하는 다른 상태가 변경될 때마다 컴포넌트가 리렌더링됩니다.
아래와 같이 user만을 구독하도록 변경해봅시다.
const user = useAuthStore((state) => state.user);
이제 user 이외의 상태가 변경되더라도, user가 변경되지 않는 한 불필요한 리렌더링을 방지할 수 있습니다.

앞으로 불필요하게 모든 state를 구독하지 않도록 조심해야겠습니다.