누구나 로고
Online CoursesFor BusinessLibraryTrend InsightsFree coursesNoticesContactCareers
모든 강의를 무료로 볼 수 있어요. 회원가입 없이도 학습 가능합니다.
로그인무료 회원가입
강좌리액트 입문 — 컴포넌트로 만드는 웹15강

15강 / 전체 28강

useRef와 DOM 접근

6분 읽기 조회 1

반복되는 상태 로직을 커스텀 훅으로 추출하는 방법, 좋은 커스텀 훅의 설계 원칙, useToggle·useLocalStorage·useDebounce 세 가지 실전 훅을 직접 만듭니다.

🔩 커스텀 훅이란

여러 컴포넌트에서 동일한 상태 로직이 반복될 때, 그것을 커스텀 훅(Custom Hook)으로 추출합니다. 커스텀 훅은 이름이 use로 시작하는 일반 함수입니다. 다른 훅들(useState, useEffect 등)을 내부에서 사용할 수 있고, 여러 컴포넌트에서 로직을 재사용할 수 있습니다.

커스텀 훅이 컴포넌트와 다른 점: 훅은 UI를 반환하지 않습니다. 상태와 함수(그리고 필요하면 값)를 반환합니다.

1️⃣ useToggle — 불리언 토글

가장 단순하지만 실무에서 자주 쓰이는 패턴입니다. 모달 열기/닫기, 드롭다운, 체크박스 등에 활용합니다:

// hooks/useToggle.ts
import { useState, useCallback } from 'react'

export function useToggle(initialValue = false) {
  const [value, setValue] = useState(initialValue)

  const toggle = useCallback(() => setValue((prev) => !prev), [])
  const setTrue = useCallback(() => setValue(true), [])
  const setFalse = useCallback(() => setValue(false), [])

  return { value, toggle, setTrue, setFalse }
}
// 사용
function Modal() {
  const { value: isOpen, toggle, setFalse: close } = useToggle()

  return (
    <>
      <button onClick={toggle}>모달 열기</button>
      {isOpen && (
        <div className="fixed inset-0 bg-black/50 flex items-center justify-center">
          <div className="bg-white rounded-2xl p-6">
            <p>모달 내용</p>
            <button onClick={close}>닫기</button>
          </div>
        </div>
      )}
    </>
  )
}

2️⃣ useLocalStorage — 새로고침 후에도 유지되는 상태

테마, 언어 설정, 마지막 검색어 등 브라우저를 닫아도 유지돼야 하는 상태에 사용합니다:

// hooks/useLocalStorage.ts
import { useState, useEffect } from 'react'

export function useLocalStorage<T>(key: string, initialValue: T) {
  const [value, setValue] = useState<T>(() => {
    // SSR 환경(Next.js 등) 대응
    if (typeof window === 'undefined') return initialValue
    try {
      const item = window.localStorage.getItem(key)
      return item ? (JSON.parse(item) as T) : initialValue
    } catch {
      return initialValue
    }
  })

  useEffect(() => {
    try {
      window.localStorage.setItem(key, JSON.stringify(value))
    } catch {
      // 시크릿 모드 등에서 localStorage 접근 불가 시 무시
    }
  }, [key, value])

  return [value, setValue] as const
}
// 사용 — useState와 완전히 동일한 인터페이스!
function ThemeToggle() {
  const [theme, setTheme] = useLocalStorage<'light' | 'dark'>('theme', 'light')

  return (
    <button
      onClick={() => setTheme((t) => (t === 'light' ? 'dark' : 'light'))}
      className="px-4 py-2 border rounded"
    >
      현재 테마: {theme} (새로고침해도 유지됩니다)
    </button>
  )
}

3️⃣ useDebounce — 입력 지연 처리

검색창에서 타이핑할 때마다 API를 호출하면 너무 많은 요청이 발생합니다. 입력이 멈춘 뒤 일정 시간 후에만 실행되도록 디바운스(debounce)를 적용합니다:

// hooks/useDebounce.ts
import { useState, useEffect } from 'react'

export function useDebounce<T>(value: T, delay = 300): T {
  const [debouncedValue, setDebouncedValue] = useState<T>(value)

  useEffect(() => {
    const timer = setTimeout(() => {
      setDebouncedValue(value)
    }, delay)

    return () => clearTimeout(timer)  // 새 값이 오면 이전 타이머 취소
  }, [value, delay])

  return debouncedValue
}
// 사용
function SearchInput() {
  const [query, setQuery] = useState('')
  const debouncedQuery = useDebounce(query, 500)  // 0.5초 후 확정

  const { data: results } = useFetch<Result[]>(
    debouncedQuery ? `/api/search?q=${debouncedQuery}` : ''
  )

  return (
    <div>
      <input
        value={query}
        onChange={(e) => setQuery(e.target.value)}
        placeholder="검색..." 
        className="border rounded px-3 py-2"
      />
      {/* results가 있으면 표시 */}
    </div>
  )
}

타이핑 중에는 query가 계속 바뀌지만 debouncedQuery는 500ms 동안 입력이 없을 때만 업데이트됩니다. API 요청이 크게 줄어듭니다.

📐 좋은 커스텀 훅 설계 원칙

  • 한 가지 책임: 한 훅은 한 가지 관심사만 담습니다. 너무 많은 것을 넣으면 재사용성이 떨어집니다.
  • useState처럼 반환: 반환값 인터페이스를 useState처럼 직관적으로 만드세요.
  • 초기값 파라미터: 기본값을 제공해 유연성을 높입니다.
  • TypeScript 제네릭: useLocalStorage<T>처럼 제네릭으로 타입 안전성을 보장합니다.

✅ 정리

  • 커스텀 훅: use로 시작하는 함수, 상태 로직을 재사용 가능하게 추출합니다.
  • useToggle: 불리언 상태와 toggle/setTrue/setFalse 반환.
  • useLocalStorage: localStorage와 동기화된 상태, SSR 안전 처리 포함.
  • useDebounce: 값 변경을 지연시켜 API 요청 빈도를 줄입니다.

관련 주제

  • useRef 개념
  • DOM 요소 직접 접근
  • 포커스 제어
  • 렌더링과 무관한 값 저장
  • 개발·프로그래밍
  • 개발·프로그래밍 강의
  • 리액트 입문 — 컴포넌트로 만드는 웹
  • 무료강의
  • 무료 온라인 강의
  • NUGUNA
  • 누구나
이전 14강컴포넌트 합성과 재사용다음 16강 커스텀 훅 만들기

댓글

0/1000

불러오는 중...

목차

  1. 1.리액트란 무엇인가
  2. 2.개발 환경 만들기 — Vite
  3. 3.JSX 기초
  4. 4.컴포넌트 만들기
  5. 5.Props로 데이터 전달
  6. 6.State와 useState
  7. 7.이벤트 처리
  8. 8.조건부 렌더링
  9. 9.리스트 렌더링과 key
  10. 10.폼 다루기
  11. 11.useEffect 기초
  12. 12.데이터 가져오기(fetch)
  13. 13.상태 끌어올리기
  14. 14.컴포넌트 합성과 재사용
  15. 15.useRef와 DOM 접근
  16. 16.커스텀 훅 만들기
  17. 17.Context API
  18. 18.useReducer
  19. 19.리스트 앱 실전 ①
  20. 20.리스트 앱 실전 ②
  21. 21.라우팅 — React Router
  22. 22.스타일링 방법들
  23. 23.리스트·상세 페이지 연결
  24. 24.비동기 상태 관리 패턴
  25. 25.성능 기초 — memo
  26. 26.에러 처리와 경계
  27. 27.환경변수와 빌드
  28. 28.배포하기

Support

  • Notices
  • FAQ
  • Contact Us
  • Community

Help & Info

  • Terms of Service
  • Privacy Policy
  • Refund Policy

Services

  • About Us
  • Sign up
  • New courses
  • Free courses
누구나 로고
Terms of ServicePrivacy PolicyRefund Policy

상호명: NUGUNA  |  대표자: 정우진  |  사업자등록번호: 392-32-01817  |  통신판매업신고: 제 2026-서울양천-0564 호

주소: 서울특별시 양천구 목동서로 100  |  이메일: nugunapass@gmail.com  |  전화: 010-6395-3043

© 2026 NUGUNA. All rights reserved.

KB예금주인증관리자
HomeCoursesLibraryContactMY