최적화(Optimization)
React는 상태(state)나 props가 변경될 때마다 컴포넌트를 다시 렌더링 합니다.
이 과정이 반복되면 성능이 저하될 수 있기 때문에 최적화(Optimization)가 필요합니다.
React에서 사용할 수 있는 최적화 기법에는 크게 3가지가 있습니다.
- 불필요한 연산 방지 → useMemo
- 불필요한 렌더링 방지 → React.memo
- 불필요한 함수 재생성 방지 → useCallback
이제 각각의 개념을 하나씩 살펴보겠습니다.
useMemo - 불필요한 연산 방지
useMemo는 메모이제이션(Memoization) 기법을 활용하여 불필요한 연산을 방지하는 React 훅입니다.
✅ 메모이제이션(Memoization)이란?
이전에 계산한 값을 저장해 두고, 필요할 때 다시 꺼내 쓰는 것을 의미합니다.
같은 연산을 반복할 필요가 없기 때문에 성능을 향상시킬 수 있습니다.
예를 들어, 리스트에서 숫자를 정렬하는 연산이 있다면,
이미 정렬한 데이터를 저장해 두고 필요할 때만 다시 계산하면 불필요한 연산을 줄일 수 있습니다.
📝 코드 예제:
import { useMemo, useState } from "react";
function HeavyComponent({ count }) {
// count가 변경될 때만 다시 계산하고, 그렇지 않으면 기존 값을 사용합니다.
const expensiveResult = useMemo(() => {
console.log("무거운 연산 실행...");
return count ** 2;
}, [count]); // count가 바뀔 때만 연산 실행
return <div>결과: {expensiveResult}</div>;
}
이렇게 하면 count 값이 변경되지 않는 한 이전 결과를 재사용하여 불필요한 연산을 줄일 수 있습니다.
React.memo - 불필요한 리렌더링 방지
React에서는 부모 컴포넌트가 리렌더링 되면 자식 컴포넌트도 함께 리렌더링 됩니다.
하지만 자식 컴포넌트가 받는 props가 변하지 않았다면, 다시 렌더링 할 필요가 없습니다.
이럴 때 React.memo를 사용하면 props가 변하지 않으면 기존 렌더링 결과를 재사용하여 불필요한 리렌더링을 방지할 수 있습니다.
📝 코드 예제:
import { memo } from "react";
function ContactItem({ name, contact }) {
console.log("렌더링 발생");
return (
<div>
<div>{name}</div>
<div>{contact}</div>
</div>
);
}
export default memo(ContactItem);
이렇게 하면 name과 contact 값이 그대로라면 ContactItem이 다시 렌더링 되지 않습니다.
memo 메서드와 객체 props의 문제
React.memo는 props를 얕은 비교(Shallow Comparison)로 판단합니다.
따라서 객체 형태의 props를 전달하면 값이 같아도 React가 "새로운 값"으로 인식할 수 있습니다.
이 문제를 해결하려면 비교 함수를 사용할 수 있습니다.
📝 코드 예제:
export default memo(ContactItem, (prevProps, nextProps) => {
return (
prevProps.name === nextProps.name &&
prevProps.contact === nextProps.contact
);
});
이렇게 두 번째 인수( 과거 props, 현재 props )로 비교 함수를 전달하면 직접 변경 여부를 확인할 수 있습니다.
useCallback - 불필요한 함수 재생성 방지
컴포넌트가 렌더링 될 때마다 함수도 새로 생성됩니다.
이렇게 되면 자식 컴포넌트에서 props로 받은 함수를 매번 새로운 값으로 인식하여 불필요한 렌더링이 발생할 수 있습니다.
useCallback을 사용하면 동일한 함수를 반복해서 생성하는 것을 방지할 수 있습니다.
📝 코드 예제:
import { useCallback, useState } from "react";
function App() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
console.log("버튼 클릭");
setCount((prev) => prev + 1);
}, []);
return <button onClick={handleClick}>클릭: {count}</button>;
}
이렇게 하면 handleClick 함수가 렌더링될 때마다 새로 생성되지 않고, 같은 함수가 유지됩니다.
마무리
위에서 설명한 최적화 기법들은 애플리케이션의 성능을 크게 향상시킬 수 있습니다.
각 기법은 특정 상황에서 유용하게 사용될 수 있으므로, 프로젝트의 요구사항에 맞게 적절히 적용하는 것이 중요합니다.
최적화 기법 | 주요 효과 | 적용 시기 |
useMemo | 불필요한 연산 방지, 성능 향상 | 계산 비용이 큰 연산이 있을 때 유용 |
React.memo | 불필요한 리렌더링 방지, 성능 향상 | 자주 변경되지 않는 props를 받을 때 유용 |
useCallback | 불필요한 함수 재생성 방지, 성능 향상 | 자식 컴포넌트에 함수를 전달할 때 유용 |
✅ 최적화 적용 시 주의할 점
- 프로젝트를 거의 완성한 후에 최적화를 진행하는 것이 좋습니다.
: 처음부터 모든 코드에 최적화를 적용하려 하면 오히려 코드가 복잡해질 수 있습니다. - 모든 곳에 최적화를 적용할 필요는 없습니다.
: 최적화는 필요한 부분에만 적용해야 합니다.
예를 들어, 자주 바뀌는 작은 UI 요소에는 굳이 React.memo를 사용할 필요가 없습니다. - 컴포넌트의 구조를 먼저 잘 설계하는 것이 최적화보다 중요합니다.
: React의 기본 동작을 이해하고, 올바르게 상태를 관리하는 것이 더 중요합니다.
[React] 최적화 전략 2탄 (lazy, Suspense와 코드 스플리팅, Debounce, Throttle)
01lazy와 Suspense1. lazy lazy는 컴포넌트를 필요할 때만 로딩하는 기능으로, 초기 로딩을 최적화할 수 있습니다.이는 대개 큰 애플리케이션에서 유용하게 쓰입니다. ✅ 사용법import React, { Suspense, lazy
dev-watnu.tistory.com
'DEVELOPMENT > React.js' 카테고리의 다른 글
[React] 페이지 라우팅 (Routing) (1) | 2025.02.13 |
---|---|
[React] 컨텍스트 (Context) (0) | 2025.02.10 |
[React] 이벤트 핸들링 (Event Handling) (3) | 2025.02.03 |
[React] export 모듈 불러오기 에러 (2) | 2024.06.15 |
[React] Vite-App 생성하는 방법 (2) | 2024.06.14 |