React 성능 최적화
Lazy Loading, Suspense, 메모이제이션으로 성능 향상
학습 항목
이미지 로딩 중...
Performance 최적화 실전 프로젝트 가이드
웹 애플리케이션의 성능을 극대적으로 향상시키는 실전 최적화 기법을 다룹니다. 렌더링 최적화부터 네트워크 성능, 번들 사이즈 최적화까지 실무에서 바로 적용 가능한 패턴들을 배웁니다.
들어가며
이 글에서는 Performance 최적화 실전 프로젝트 가이드에 대해 상세히 알아보겠습니다. 총 12가지 주요 개념을 다루며, 각각의 개념에 대한 설명과 실제 코드 예제를 함께 제공합니다.
목차
- React.memo로_불필요한_리렌더링_방지
- useMemo로_비용이_큰_연산_캐싱
- useCallback으로_함수_재생성_방지
- Code_Splitting으로_초기_로딩_속도_개선
- Virtual_List로_대용량_데이터_렌더링
- Image_Lazy_Loading으로_네트워크_절약
- Debounce로_API_호출_최적화
- Web_Worker로_무거운_작업_분리
- CSS_in_JS_최적화_with_Styled_Components
- React_DevTools_Profiler로_성능_측정
- Bundle_Analyzer로_번들_크기_최적화
- Intersection_Observer로_스크롤_이벤트_최적화
1. React.memo로_불필요한_리렌더링_방지
개요
컴포넌트의 props가 변경되지 않으면 리렌더링을 건너뛰어 성능을 최적화합니다.
코드 예제
const UserCard = React.memo(({ name, email }) => {
console.log('UserCard rendered');
return (
<div>
<h3>{name}</h3>
<p>{email}</p>
</div>
);
});
설명
props가 동일하면 이전 렌더링 결과를 재사용하여 불필요한 렌더링을 방지합니다.
2. useMemo로_비용이_큰_연산_캐싱
개요
복잡한 계산 결과를 메모이제이션하여 매 렌더링마다 재계산하지 않도록 합니다.
코드 예제
const FilteredList = ({ items, query }) => {
const filtered = useMemo(() => {
return items.filter(item =>
item.name.toLowerCase().includes(query.toLowerCase())
);
}, [items, query]);
return <ul>{filtered.map(item => <li key={item.id}>{item.name}</li>)}</ul>;
};
설명
items나 query가 변경될 때만 필터링을 다시 수행하여 성능을 개선합니다.
3. useCallback으로_함수_재생성_방지
개요
콜백 함수를 메모이제이션하여 자식 컴포넌트의 불필요한 리렌더링을 방지합니다.
코드 예제
const TodoList = () => {
const [todos, setTodos] = useState([]);
const addTodo = useCallback((text) => {
setTodos(prev => [...prev, { id: Date.now(), text }]);
}, []);
return <TodoForm onAdd={addTodo} />;
};
설명
addTodo 함수가 매번 새로 생성되지 않아 TodoForm의 리렌더링을 최소화합니다.
4. Code_Splitting으로_초기_로딩_속도_개선
개요
React.lazy와 Suspense를 사용하여 필요한 시점에만 컴포넌트를 로드합니다.
코드 예제
const Dashboard = lazy(() => import('./Dashboard'));
const Settings = lazy(() => import('./Settings'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</Suspense>
);
}
설명
각 페이지를 별도 번들로 분리하여 초기 로딩 시간을 단축시킵니다.
5. Virtual_List로_대용량_데이터_렌더링
개요
화면에 보이는 항목만 렌더링하여 수천 개의 데이터도 빠르게 처리합니다.
코드 예제
import { FixedSizeList } from 'react-window';
const VirtualList = ({ items }) => (
<FixedSizeList
height={400}
itemCount={items.length}
itemSize={50}
width="100%"
>
{({ index, style }) => (
<div style={style}>{items[index].name}</div>
)}
</FixedSizeList>
);
설명
뷰포트에 보이는 항목만 DOM에 렌더링하여 메모리와 성능을 최적화합니다.
6. Image_Lazy_Loading으로_네트워크_절약
개요
이미지를 필요한 시점에만 로드하여 초기 페이지 로딩 속도를 향상시킵니다.
코드 예제
const OptimizedImage = ({ src, alt }) => (
<img
src={src}
alt={alt}
loading="lazy"
decoding="async"
style={{ width: '100%', height: 'auto' }}
/>
);
설명
loading="lazy" 속성으로 이미지가 뷰포트에 가까워질 때만 로드됩니다.
7. Debounce로_API_호출_최적화
개요
연속된 이벤트를 그룹화하여 불필요한 API 호출을 줄입니다.
코드 예제
const SearchBox = () => {
const [query, setQuery] = useState('');
const debouncedSearch = useMemo(
() => debounce((value) => {
fetch(`/api/search?q=${value}`).then(/* ... */);
}, 500),
[]
);
return <input onChange={(e) => debouncedSearch(e.target.value)} />;
};
설명
사용자가 타이핑을 멈춘 후 500ms 후에만 검색 API를 호출합니다.
8. Web_Worker로_무거운_작업_분리
개요
메인 스레드를 블로킹하지 않고 백그라운드에서 복잡한 연산을 처리합니다.
코드 예제
const worker = new Worker('calculator.worker.js');
const HeavyComputation = () => {
const calculate = (data) => {
worker.postMessage(data);
worker.onmessage = (e) => {
console.log('Result:', e.data);
};
};
return <button onClick={() => calculate([1,2,3])}>Calculate</button>;
};
설명
복잡한 계산을 별도 스레드에서 실행하여 UI가 멈추지 않습니다.
9. CSS_in_JS_최적화_with_Styled_Components
개요
동적 스타일을 효율적으로 처리하여 불필요한 CSS 재생성을 방지합니다.
코드 예제
const Button = styled.button`
background: ${props => props.primary ? '#007bff' : '#6c757d'};
padding: 10px 20px;
border: none;
border-radius: 4px;
`;
const OptimizedButton = memo(Button);
설명
스타일 컴포넌트를 메모이제이션하여 props 변경 시에만 재생성합니다.
10. React_DevTools_Profiler로_성능_측정
개요
컴포넌트별 렌더링 시간을 측정하여 병목 지점을 찾아냅니다.
코드 예제
import { Profiler } from 'react';
<Profiler id="Dashboard" onRender={(id, phase, actualDuration) => {
console.log(`${id} (${phase}) took ${actualDuration}ms`);
}}>
<Dashboard />
</Profiler>
설명
각 컴포넌트의 렌더링 성능을 측정하여 최적화가 필요한 부분을 파악합니다.
11. Bundle_Analyzer로_번들_크기_최적화
개요
번들에 포함된 모듈의 크기를 시각화하여 불필요한 의존성을 제거합니다.
코드 예제
// package.json
{
"scripts": {
"analyze": "source-map-explorer 'build/static/js/*.js'"
}
}
// 터미널에서 실행
// npm run build && npm run analyze
설명
빌드된 번들을 분석하여 큰 라이브러리를 찾아 tree-shaking이나 대체를 검토합니다.
12. Intersection_Observer로_스크롤_이벤트_최적화
개요
스크롤 이벤트 대신 Intersection Observer를 사용하여 성능을 향상시킵니다.
코드 예제
const LazyComponent = () => {
const [isVisible, setIsVisible] = useState(false);
const ref = useRef();
useEffect(() => {
const observer = new IntersectionObserver(([entry]) => {
if (entry.isIntersecting) setIsVisible(true);
});
observer.observe(ref.current);
}, []);
return <div ref={ref}>{isVisible && <HeavyContent />}</div>;
};
설명
요소가 뷰포트에 들어올 때만 무거운 컴포넌트를 렌더링하여 성능을 개선합니다.
마치며
이번 글에서는 Performance 최적화 실전 프로젝트 가이드에 대해 알아보았습니다. 총 12가지 개념을 다루었으며, 각각의 사용법과 예제를 살펴보았습니다.
관련 태그
#React #Performance #Optimization #Memoization #LazyLoading