반응형
    
    
    
  소개
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 |