Learning
레슨 5 / 9·20분

Hooks 활용하기

useRef

useRef는 렌더링 사이에 값을 유지하되, 값이 변경되어도 리렌더링을 일으키지 않는 Hook입니다. DOM 요소에 접근하거나 이전 값을 저장할 때 사용합니다.

tsx
import { useRef, useEffect } from 'react';

function AutoFocusInput() {
  const inputRef = useRef<HTMLInputElement>(null);

  // 마운트 시 자동 포커스
  useEffect(() => {
    inputRef.current?.focus();
  }, []);

  return <input ref={inputRef} placeholder="자동 포커스" />;
}

// 렌더링 횟수 추적
function RenderCounter() {
  const count = useRef(0);

  useEffect(() => {
    count.current += 1;
  });

  return <p>렌더링 횟수: {count.current}</p>;
}

useMemo와 useCallback

useMemo는 비용이 큰 계산 결과를 메모이제이션하고, useCallback은 함수 참조를 메모이제이션합니다. 불필요한 재계산이나 자식 컴포넌트의 불필요한 리렌더링을 방지합니다.

tsx
import { useState, useMemo, useCallback } from 'react';

function ExpensiveList({ items, query }: {
  items: string[];
  query: string;
}) {
  // 비용이 큰 필터링을 메모이제이션
  const filtered = useMemo(() => {
    console.log("필터링 실행");
    return items.filter(item =>
      item.toLowerCase().includes(query.toLowerCase())
    );
  }, [items, query]); // items나 query 변경 시만 재계산

  // 콜백 함수 메모이제이션
  const handleClick = useCallback((item: string) => {
    console.log("선택:", item);
  }, []); // 의존성 없음 — 함수가 변경되지 않음

  return (
    <ul>
      {filtered.map(item => (
        <li key={item} onClick={() => handleClick(item)}>
          {item}
        </li>
      ))}
    </ul>
  );
}

커스텀 Hook

커스텀 Hook은 use로 시작하는 함수로, 여러 컴포넌트에서 재사용할 로직을 추출합니다. 내부에서 다른 Hook을 자유롭게 사용할 수 있습니다.

tsx
// 커스텀 Hook: localStorage와 동기화
function useLocalStorage<T>(key: string, initial: T) {
  const [value, setValue] = useState<T>(() => {
    const saved = localStorage.getItem(key);
    return saved ? JSON.parse(saved) : initial;
  });

  useEffect(() => {
    localStorage.setItem(key, JSON.stringify(value));
  }, [key, value]);

  return [value, setValue] as const;
}

// 사용
function Settings() {
  const [theme, setTheme] = useLocalStorage("theme", "light");
  const [lang, setLang] = useLocalStorage("lang", "ko");

  return (
    <div>
      <button onClick={() => setTheme(
        theme === "light" ? "dark" : "light"
      )}>
        테마: {theme}
      </button>
      <select value={lang} onChange={e => setLang(e.target.value)}>
        <option value="ko">한국어</option>
        <option value="en">English</option>
      </select>
    </div>
  );
}
💡

useMemouseCallback은 성능 최적화 도구입니다. 모든 곳에 사용하면 오히려 코드 복잡도만 올라가므로, 실제 성능 문제가 있을 때만 적용하세요.