들어가기
안녕하세요.
React 애플리케이션의 성능을 개선하는 방법을 쉽게 이해할 수 있는 예제를 통해 알아보겠습니다.
특히 react-scan이라는 도구를 사용해서 어떤 부분이 성능을 저하시키는지 쉽게 찾아볼 수 있습니다.
react-scan 소개
- 불필요한 렌더링 발생 지점 식별
- 컴포넌트 렌더링 횟수 모니터링
- 성능 병목 현상 시각화
- 최적화 포인트 발견
등을 할 수 있습니다.
예제 소개
사용자 목록을 보여주는 간단한 애플리케이션을 만들어볼 건데요, 조금 억지스럽지만..
같은 기능을 구현하는 두 가지 다른 방법을 비교해 볼 거예요
- 일부러 성능이 안 좋게 만든 버전 (나쁜 예시)
- 성능을 개선한 버전 (좋은 예시)
성능이 안 좋은 버전은 어떻게 만들었나요?
먼저 성능이 안 좋은 버전을 보여드릴게요. 이 버전에서는 일부러 이런 문제들을 만들었습니다
// 비효율적인 구현 예시
// 각 문자를 개별 컴포넌트로 만들어서 렌더링하는 안 좋은 방식
const SlowCharacter: React.FC<{ char: string }> = ({ char }) => {
useEffect(() => {
// 일부러 성능 저하를 시뮬레이션하기 위한 인위적인 지연
// 실제 프로젝트에서는 절대 이렇게 하면 안 됩니다!
const start = performance.now();
while (performance.now() - start < 1) {}
}, [char]);
return <span>{char}</span>;
};
// 이름을 한 글자씩 쪼개서 렌더링하는 비효율적인 컴포넌트
// 예: "John Doe" => <span>J</span><span>o</span><span>h</span><span>n</span>...
const FullName: React.FC<{ firstName: string; lastName: string }> = ({
firstName,
lastName,
}) => {
const fullName = `${firstName} ${lastName}`;
return (
<div className="full-name">
{/*
문자열을 배열로 쪼개고 각 문자마다 새로운 컴포넌트를 생성
이는 매우 비효율적인 방식입니다:
1. 불필요한 컴포넌트 생성
2. 과도한 메모리 사용
3. 성능 저하
*/}
{fullName.split("").map((char, index) => (
<SlowCharacter key={index} char={char} />
))}
</div>
);
};
이게 왜 안 좋을까요?
- 이름의 각 글자마다 새로운 컴포넌트를 만듭니다 (너무 과하죠?)
- 각 글자마다 일부러 지연 시간을 넣었어요
- 불필요하게 여러 번 다시 그려집니다
개선된 버전은 어떻게 다른가요?
이제 같은 기능을 하지만, 훨씬 더 효율적으로 만든 버전을 보여드릴게요:
// React.memo를 사용하여 불필요한 리렌더링 방지
// props가 변경되지 않으면 다시 렌더링하지 않음
const FullName = memo(
({ firstName, lastName }: { firstName: string; lastName: string }) => (
<div className="full-name">
{/* 이름을 쪼개지 않고 한 번에 렌더링 */}
{firstName} {lastName}
</div>
)
);
// 사용자 카드 컴포넌트도 memo로 최적화
// 각 사용자의 정보가 변경될 때만 리렌더링
const UserCard = memo(
({
firstName,
lastName,
email,
}: {
firstName: string;
lastName: string;
email: string;
}) => (
<div className="user-card">
{/* 컴포넌트를 논리적인 단위로 분리 */}
<FullName firstName={firstName} lastName={lastName} />
<div className="email">{email}</div>
</div>
)
);
이 버전이 더 좋은 이유는
- 이름을 쪼개지 않고 한 번에 보여줍니다
- React.memo를 사용해서 필요할 때만 다시 그립니다
- 불필요한 작업이 없어서 훨씬 빠릅니다
react-scan으로 차이 확인하기
이제 react-scan을 사용해서 두 버전의 차이를 실제로 확인해 볼 수 있습니다
import { scan } from "react-scan";
function App() {
scan(); // 함수로 호출
return (
<div className="app">
<h1>React Scan 성능 테스트</h1>
<div className="comparison">
<section className="inefficient">
<h2>비효율적인 구현</h2>
<InefficientList />
</section>
<section className="efficient">
<h2>최적화된 구현</h2>
<OptimizedUserList />
</section>
</div>
</div>
);
}
react-scan을 사용하면:
- 어떤 컴포넌트가 자주 다시 그려지는지
- 어떤 부분이 느린지
- 어떻게 하면 더 빠르게 만들 수 있는지
쉽게 확인할 수 있습니다.
에서 확인하실 수 있습니다.
마치며
성능 최적화는 단순히 기술적인 과제가 아닌, 사용자 경험을 향상시키는 중요한 요소입니다. 이러한 목표를 달성하는데 react-scan과 같은 도구들이 유용하게 사용될 수 있는 것 같습니다.