반응형
소개
React의 memo
함수는 컴포넌트의 불필요한 리렌더링을 방지하여 성능을 최적화하는 데 사용됩니다. 이번 글에서는 memo
의 올바른 사용법과 실제 사례를 살펴보겠습니다.
기본 사용법
import { memo } from 'react';
interface UserProfileProps {
name: string;
email: string;
}
const UserProfile = memo(function UserProfile({ name, email }: UserProfileProps) {
return (
<div className="user-profile">
<h2>{name}</h2>
<p>{email}</p>
</div>
);
});
export default UserProfile;
memo가 필요한 경우와 필요하지 않은 경우
memo가 유용한 경우
// 1. 큰 리스트의 항목 컴포넌트
const TodoItem = memo(function TodoItem({ todo, onToggle }: TodoItemProps) {
return (
<li>
<input
type="checkbox"
checked={todo.completed}
onChange={() => onToggle(todo.id)}
/>
{todo.text}
</li>
);
});
// 2. 자주 변경되지 않는 데이터를 표시하는 컴포넌트
const StaticHeader = memo(function StaticHeader({ title }: HeaderProps) {
return (
<header>
<h1>{title}</h1>
</header>
);
});
memo가 불필요한 경우
// 1. 자주 변경되는 데이터를 가진 컴포넌트
const Timer = () => {
const [time, setTime] = useState(0);
useEffect(() => {
const interval = setInterval(() => setTime(t => t + 1), 1000);
return () => clearInterval(interval);
}, []);
return <div>{time} seconds</div>;
};
// 2. 단순한 컴포넌트
const Button = ({ onClick, children }) => (
<button onClick={onClick}>
{children}
</button>
);
커스텀 비교 함수 사용
const ExpensiveComponent = memo(
function ExpensiveComponent({ data, onUpdate }: Props) {
return (
<div>
{/* 복잡한 렌더링 로직 */}
</div>
);
},
(prevProps, nextProps) => {
// 깊은 비교 수행
return JSON.stringify(prevProps.data) === JSON.stringify(nextProps.data);
}
);
성능 최적화 예제
리스트 최적화
interface ListProps {
items: Item[];
onItemClick: (id: string) => void;
}
const OptimizedList = memo(function OptimizedList({
items,
onItemClick
}: ListProps) {
return (
<ul>
{items.map(item => (
<ListItem
key={item.id}
item={item}
onClick={onItemClick}
/>
))}
</ul>
);
});
const ListItem = memo(function ListItem({
item,
onClick
}: ListItemProps) {
return (
<li onClick={() => onClick(item.id)}>
{item.name}
</li>
);
});
폼 컴포넌트 최적화
const FormField = memo(function FormField({
label,
value,
onChange,
error
}: FormFieldProps) {
return (
<div className="form-field">
<label>{label}</label>
<input value={value} onChange={onChange} />
{error && <span className="error">{error}</span>}
</div>
);
});
// 사용 예시
const Form = () => {
const [formData, setFormData] = useState({ name: '', email: '' });
const handleNameChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
setFormData(prev => ({ ...prev, name: e.target.value }));
}, []);
return (
<form>
<FormField
label="Name"
value={formData.name}
onChange={handleNameChange}
/>
{/* 다른 필드들 */}
</form>
);
};
memo와 다른 Hooks의 조합
useMemo와 함께 사용
const ComplexComponent = memo(function ComplexComponent({
data,
onProcess
}: ComplexProps) {
const processedData = useMemo(() => {
return expensiveOperation(data);
}, [data]);
return (
<div>
{processedData.map(item => (
<div key={item.id}>{item.value}</div>
))}
</div>
);
});
useCallback과 함께 사용
const ParentComponent = () => {
const [items, setItems] = useState<string[]>([]);
const handleAdd = useCallback((newItem: string) => {
setItems(prev => [...prev, newItem]);
}, []);
return (
<MemoizedChildComponent
items={items}
onAdd={handleAdd}
/>
);
};
const MemoizedChildComponent = memo(function ChildComponent({
items,
onAdd
}: ChildProps) {
return (
<div>
<button onClick={() => onAdd('new item')}>Add Item</button>
<ul>
{items.map(item => (
<li key={item}>{item}</li>
))}
</ul>
</div>
);
});
성능 측정
const withPerformanceLogging = <P extends object>(
WrappedComponent: React.ComponentType<P>,
componentName: string
) => {
return memo(function LoggedComponent(props: P) {
console.time(`${componentName} render`);
const result = <WrappedComponent {...props} />;
console.timeEnd(`${componentName} render`);
return result;
});
};
const OptimizedComponent = withPerformanceLogging(MyComponent, 'MyComponent');
주의사항
- 과도한 사용 피하기
- 올바른 의존성 관리
- 깊은 비교의 성능 영향 고려
- 컴포넌트의 책임 범위 명확히 하기
마치며
memo
는 React 애플리케이션의 성능을 최적화하는 강력한 도구입니다. 하지만 모든 컴포넌트에 무분별하게 적용하는 것은 오히려 성능을 저하시킬 수 있으므로, 실제 성능 측정을 통해 필요한 곳에만 적용하는 것이 중요합니다.
'TypeScript' 카테고리의 다른 글
next.config.js 커스터마이징 (0) | 2025.01.08 |
---|---|
[React] React.lazy와 Suspense (2) | 2024.12.26 |
[React Query] useMutation과 useQuery (0) | 2024.12.24 |
[React] Zustand로 배우는 간단하고 강력한 상태 관리 (0) | 2024.12.20 |
[React Query] QueryClient : 효율적인 상태관리와 데이터 캐싱 (0) | 2024.12.19 |