안녕하세요, React 개발자 여러분! React를 사용하다 보면 컴포넌트 간에 반복되는 로직 때문에 골치 아픈 적이 많으시죠? 예를 들어, API 호출이나 폼 상태 관리 같은 부분이 여러 곳에서 비슷하게 구현되다 보면 코드가 복잡해지고 유지보수가 어려워집니다. 이런 문제를 해결하는 강력한 무기가 바로 커스텀 훅(Custom Hook) 입니다.
이 글에서는 커스텀 훅이 무엇인지부터, 왜 써야 하는지, 실제로 만드는 방법과 실전 예시까지 자세히 알아보겠습니다. 초보자도 쉽게 따라할 수 있도록 단계별로 설명하겠어요. React의 내장 훅을 활용해 코드를 더 깔끔하고 효율적으로 만드는 팁을 공유할 테니, 끝까지 읽어보세요!
커스텀 훅이란 무엇인가요?
커스텀 훅은 React의 상태(state)나 라이프사이클(lifecycle) 관련 로직을 재사용 가능한 함수로 추출한 것입니다. 기본 훅(useState, useEffect 등)처럼 'use'로 시작 해야 하며, 컴포넌트의 복잡한 비즈니스 로직을 분리해 UI 렌더링에만 집중할 수 있게 해줍니다.
React 공식 문서에서도 강조하듯, 커스텀 훅은 "로직을 공유하는 함수"로, 컴포넌트의 재사용성을 높여줍니다. 예를 들어, 클래스 컴포넌트 시대의 고차 컴포넌트(HOC)나 렌더 프롭(render props) 패턴을 대체할 수 있는 현대적인 접근법이죠. React 16.8부터 도입된 훅 시스템의 확장판이라고 생각하시면 쉽습니다.
왜 커스텀 훅을 사용해야 할까요?
커스텀 훅은 단순한 트릭이 아니라, React 프로젝트의 품질을 높이는 핵심 도구입니다. 아래에서 주요 장점을 세 가지로 정리해 보았어요.
1. 코드 재사용성 향상
여러 컴포넌트에서 동일한 로직(예: 데이터 페칭이나 입력 검증)을 반복하지 않아도 됩니다. 한 번 만들면 어디서든 불러 쓸 수 있어 개발 속도가 빨라지고, 코드 일관성이 유지됩니다. 결과적으로 버그 발생 가능성도 줄어들죠!
2. 관심사 분리(Seperation of Concerns)
UI와 비즈니스 로직을 명확히 분리합니다. 컴포넌트는 "무엇을 보여줄까?"에만 집중하고, 훅은 "어떻게 데이터를 처리할까?"를 담당하니 코드가 더 읽기 쉽고, 수정 시 파급 효과가 적습니다. 대규모 프로젝트에서 특히 빛을 발합니다.
3. 테스트 용이성
커스텀 훅은 독립적인 함수이므로, Jest나 React Testing Library로 쉽게 단위 테스트할 수 있습니다. 컴포넌트 전체를 렌더링하지 않고 로직만 검증 가능해 디버깅이 수월해집니다. TDD(Test-Driven Development)를 실천하는 개발자에게 딱 맞아요.
이 외에도 타입스크립트와 결합하면 타입 안전성까지 보장할 수 있어, 프로덕션 레벨 앱에 필수입니다.
커스텀 훅 생성하기: 단계별 가이드
커스텀 훅 만들기는 간단합니다. React 훅을 활용한 JavaScript(또는 TypeScript) 함수로 정의하면 끝! 아래는 폼 입력 상태를 관리하는 useForm 훅 예시입니다. 이 훅으로 여러 폼 컴포넌트를 쉽게 만들어보세요.
1단계: 훅 함수 정의하기
별도의 파일(예: useForm.js)에 훅을 작성합니다.
import { useState } from 'react';
function useForm(initialValues) {
const [values, setValues] = useState(initialValues);
const handleChange = (event) => {
const { name, value } = event.target;
setValues({
...values,
[name]: value,
});
};
return [values, handleInputChange];
}
initialValues: 초기 폼 데이터(객체 형태).handleChange: 입력 이벤트 핸들러로, 상태를 업데이트합니다.- 반환값:
[현재 값 객체, 변경 핸들러 함수]배열.
2단계: 컴포넌트에서 사용하기
이제 컴포넌트에서 불러와 보세요. 중복 코드 없이 폼을 관리할 수 있습니다.
import React from 'react';
import useForm from './useForm'; // 훅 파일 경로
function MyForm() {
const [formData, handleInputChange] = useForm({ username: '', email: '' });
const handleSubmit = (event) => {
event.preventDefault();
console.log('제출된 데이터:', formData);
// 실제로는 API 호출 등 처리
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
name="username"
value={formData.username}
onChange={handleInputChange}
placeholder="사용자 이름"
/>
<input
type="email"
name="email"
value={formData.email}
onChange={handleInputChange}
placeholder="이메일"
/>
<button type="submit">제출</button>
</form>
);
}
export default MyForm;
이 예시처럼 useForm을 사용하면 폼 로직이 컴포넌트에서 사라져 깔끔해집니다. 입력 유효성 검사나 에러 핸들링을 추가로 구현할 수도 있어요.
커스텀 훅의 실제 적용 시나리오
이론만 말고 실전에서 어떻게 쓰는지 보죠. 아래 두 가지 예시는 프로젝트에서 바로 적용할 수 있는 코드입니다.
1. API 호출 관리: useFetch
데이터 페칭의 로딩, 에러 상태를 한 번에 처리합니다. fetch API를 사용했지만, Axios나 SWR 같은 라이브러리와도 쉽게 결합 가능해요.
import { useEffect, useState } from 'react';
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
setLoading(true);
const response = await fetch(url);
if (!response.ok) throw new Error('네트워크 오류');
const result = await response.json();
setData(result);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchData();
}, [url]); // URL 변경 시 재실행
return { data, loading, error };
}
사용 예시:
function UserList() {
const { data, loading, error } = useFetch('/api/users');
if (loading) return <div>로딩 중...</div>;
if (error) return <div>오류: {error}</div>;
return (
<ul>
{data.map(user => <li key={user.id}>{user.name}</li>)}
</ul>
);
}
2. 로컬 스토리지 관리: useLocalStorage
브라우저의 localStorage를 상태처럼 다루세요. 테마 설정이나 사용자 선호도 저장에 유용합니다.
import { useEffect, useState } 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 [theme, setTheme] = useLocalStorage('theme', 'light');
const toggleTheme = () => setTheme(theme === 'light' ? 'dark' : 'light');
return (
<button onClick={toggleTheme}>
{theme === 'light' ? '어두운 모드' : '밝은 모드'}
</button>
);
}
이 외에도 useDebounce(검색 입력 지연), useWindowSize(창 크기 추적) 같은 훅을 만들어 보세요. GitHub에 공유된 오픈소스 훅 라이브러리(예: React Hook Form)도 참고하면 좋습니다.
결론: 커스텀 훅으로 React 코딩을 업그레이드하세요!
커스텀 훅은 React의 철학 – "컴포넌트는 작고, 로직은 재사용 가능하게" – 을 실현하는 마법 같은 도구입니다. HOC나 render props처럼 복잡한 패턴 없이 로직을 공유할 수 있어, 중소 규모부터 엔터프라이즈 앱까지 모두에 적합하죠. 오늘 예시를 직접 따라 해보고, 여러분의 프로젝트에 적용해 보세요. 코드가 더 짧아지고, 팀 협업도 수월해질 거예요!
'프로그래밍 > ReactJS' 카테고리의 다른 글
| 모바일 앱 개발의 혁신: React Native, 웹 개발자의 새로운 기회 (0) | 2025.10.19 |
|---|---|
| React 커스텀 훅: 로직 재사용의 마법을 경험하다! (0) | 2025.10.19 |
| React Router: 리다이렉트와 인증으로 사용자 경험과 보안을 강화하는 방법 (0) | 2025.10.19 |
| React Router 중첩 라우트: 복잡한 애플리케이션 내비게이션을 위한 핵심 전략 (0) | 2025.10.19 |
| React Router 동적 라우팅 마스터하기: 사용자 경험을 혁신하는 방법 (0) | 2025.10.19 |