React 애플리케이션을 만들다 보면, 여러 컴포넌트에서 비슷한 로직이 반복되는 경우가 많다.
예를 들어 입력 값 제어, 타이머 관리, 로컬 스토리지 접근, API 상태 관리 등은 다양한 컴포넌트에서 공유되는 공통된 패턴이다.
React는 이러한 로직을 커스텀 Hook(Custom Hook)으로 추상화할 수 있도록 지원한다.
이번 글에서는 커스텀 Hook의 개념부터 작성법, 실전 예제까지 알아본다.
커스텀 Hook은 React의 기본 Hook(useState
, useEffect
, 등)을 사용해 만든, 재사용 가능한 함수다.
이 함수는 반드시 use
로 시작하는 이름을 가지며, 내부에서 하나 이상의 Hook을 사용할 수 있다.
function useCustomFeature() {
const [value, setValue] = useState(0);
// 기타 로직 ...
return [value, setValue];
}
커스텀 Hook은 UI가 아닌 로직만을 담당하며, 함수형 컴포넌트처럼 동작하지만 JSX를 반환하지 않는다.
여러 입력 필드에서 useState
를 반복하는 대신, 커스텀 훅으로 추상화할 수 있다.
// useInput.js
import { useState } from 'react';
function useInput(initialValue = "") {
const [value, setValue] = useState(initialValue);
const onChange = (e) => setValue(e.target.value);
const reset = () => setValue("");
return { value, onChange, reset };
}
export default useInput;
사용 예시:
import useInput from './useInput';
function LoginForm() {
const username = useInput();
const password = useInput();
const handleSubmit = (e) => {
e.preventDefault();
console.log("입력값:", username.value, password.value);
username.reset();
password.reset();
};
return (
<form onSubmit={handleSubmit}>
<input {...username} placeholder="아이디" />
<input {...password} type="password" placeholder="비밀번호" />
<button>로그인</button>
</form>
);
}
use
로 시작해야 한다→ React는 이 네이밍 규칙을 기반으로 Hook을 식별하고 동작 순서를 관리한다.
→ 조건문, 루프, 중첩 함수 내에서 호출하면 안 된다.
→ 커스텀 Hook은 로직만 담당하는 함수이며, UI는 일반 컴포넌트에서 처리한다.
import { useEffect, useState } from 'react';
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
let ignore = false;
setLoading(true);
fetch(url)
.then((res) => res.json())
.then((result) => {
if (!ignore) {
setData(result);
setLoading(false);
}
});
return () => {
ignore = true;
};
}, [url]);
return { data, loading };
}
사용 예시:
function PostList() {
const { data: posts, loading } = useFetch("https://jsonplaceholder.typicode.com/posts");
if (loading) return <p>로딩 중...</p>;
return (
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
}
import { useState, useEffect } from 'react';
function useLocalStorage(key, initialValue) {
const [value, setValue] = useState(() => {
const stored = localStorage.getItem(key);
return stored ? JSON.parse(stored) : initialValue;
});
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
return [value, setValue];
}
사용 예시:
function ThemeToggle() {
const [darkMode, setDarkMode] = useLocalStorage("darkMode", false);
return (
<button onClick={() => setDarkMode(!darkMode)}>
{darkMode ? "라이트 모드" : "다크 모드"}
</button>
);
}
use
로 시작해야 하며, JSX를 반환하지 않는다.React에서 커스텀 Hook은 로직을 모듈화하고 팀 개발 효율을 높이기 위한 가장 강력한 도구 중 하나다. 복잡한 컴포넌트일수록 로직을 커스텀 Hook으로 분리하면 코드가 훨씬 읽기 쉬워지고, 유지보수성이 향상된다.
앞으로는 하나의 기능을 구현할 때마다 "이걸 커스텀 Hook으로 만들 수 있을까?"를 고민해보자. 작은 반복도 훅으로 추상화하는 습관이 점점 더 탄탄한 리액트 실력을 만들어줄 것이다.