React에서 Custom Hooks 설계 원칙 및 재사용성 향상
Custom hook 설계 방법과 React 훅 재사용성 향상을 위한 핵심 원칙, 패턴, 테스트와 리팩토링 전략을 사례 중심으로 정리한 기술 자료
목차
개요
Custom hook은 React 코드에서 재사용 가능한 상태 로직을 추출하는 핵심 도구다. 잘 설계된 훅은 코드 중복을 줄이고 유지보수를 쉽게 만든다. 이 글은 custom hook 설계 방법과 react 훅 재사용성 관점에서 핵심 원칙과 패턴을 설명한다. 처음 접하는 개발자도 따라올 수 있도록 기본 개념부터 실제 코드 예제까지 단계별로 정리한다.
핵심 설계 원칙
훅을 설계할 때 지켜야 할 기본 원칙은 단순성, 명확한 책임 분리, 최소한의 의존성, 테스트 가능성이다. 각 항목을 구체적으로 설명한다.
1. 단일 책임
하나의 훅은 하나의 목적을 가져야 한다. 상태 관리, 데이터 페칭, 폼 처리 등 책임을 분리하면 재사용성이 높아진다.
2. 명확한 입력과 출력
훅은 가능한 한 명확한 인터페이스를 제공해야 한다. 반환값은 배열이나 객체 형태로 정리하고, 어떤 값이 변경되는지 문서화한다.
3. 부작용과 상태 분리
부작용(side effect)은 useEffect 내부에서 관리하고, 상태 업데이트 로직은 훅의 반환값으로 분리한다. 이렇게 하면 재사용성과 예측 가능성이 높아진다.
4. 최소한의 의존성
외부에 강하게 결합된 훅은 재사용이 어렵다. API 엔드포인트나 글로벌 상태에 직접 의존하기보다 인자를 통해 주입하거나 콜백을 받는 패턴이 좋다.
설계 패턴과 베스트프랙티스
구체적 패턴을 통해 hook 패턴 베스트프랙티스를 소개한다. 재사용성 관점에서 자주 쓰이는 접근법들을 나열한다.
- Facade 훅: 복합 기능을 묶어 단일 API로 제공
- Adapter 훅: 외부 라이브러리 인터페이스를 감싸 표준화
- Strategy 훅: 동작 방식을 인자로 바꿀 수 있도록 설계
- Composability: 작은 훅들을 합성하여 큰 기능을 구현
예: 데이터 페칭 추상화
데이터 페칭은 재사용성이 높은 영역이다. 기본적으로 네트워크 요청, 로딩 상태, 에러 처리를 분리한다.
function useFetch(url, options) {
const [data, setData] = React.useState(null);
const [loading, setLoading] = React.useState(false);
const [error, setError] = React.useState(null);
React.useEffect(() => {
if (!url) return;
let mounted = true;
setLoading(true);
fetch(url, options)
.then(res => res.json())
.then(json => { if (mounted) setData(json); })
.catch(err => { if (mounted) setError(err); })
.finally(() => { if (mounted) setLoading(false); });
return () => { mounted = false; };
}, [url, JSON.stringify(options)]);
return { data, loading, error };
}
위 패턴은 간단하지만 재사용성이 높다. 단, options를 의존성에 넣을 때는 깊은 비교나 문자열화가 필요하다. 그렇지 않으면 불필요한 리렌더를 유발한다.
구성 가능한 훅 작성
작은 훅을 만들고 이를 조합하면 복잡한 기능도 깔끔하게 구현된다. 예를 들어 상태 관리 훅과 페칭 훅을 조합할 수 있다.
function usePaginatedFetch(url, pageSize) {
const [page, setPage] = React.useState(1);
const { data, loading, error } = useFetch(`${url}?_page=${page}&_limit=${pageSize}`);
return { data, loading, error, page, setPage };
}
이처럼 작은 훅을 조합하면 의도한 기능만 노출하는 깔끔한 API를 만들 수 있다.
테스트와 문서화
react 훅 재사용성은 테스트로 보장된다. 훅을 설계할 때는 단위 테스트를 염두에 둬야 한다. 다음 원칙들이 도움된다.
- 부작용은 모킹 가능하도록 분리
- 네트워크 요청은 fetch나 axios 추상화 레이어로 분리
- 테스트 가능한 반환값과 상태 인디케이터 제공
간단한 테스트 예시는 React Testing Library의 renderHook을 사용한다. 테스트는 훅의 상태 전환을 검증하는 데 집중한다.
리팩토링 전략
기존 컴포넌트에서 훅으로 추출할 때는 다음 절차를 따르는 것이 안전하다.
- 1) 단일 책임으로 분리할 로직 식별
- 2) 외부 의존성을 인자로 바꾸거나 콜백으로 추상화
- 3) 내부 상태와 부작용을 분리하여 useEffect로 이동
- 4) 기존 컴포넌트에 동일한 동작을 유지하는지 검증
리팩토링 예시: 폼 입력 처리 로직을 useForm 훅으로 추출하면 검증 로직과 입력 상태를 재사용할 수 있다.
function useForm(initialValues) {
const [values, setValues] = React.useState(initialValues);
const handleChange = (name) => (e) => {
const value = e.target.value;
setValues(prev => ({ ...prev, [name]: value }));
};
const reset = () => setValues(initialValues);
return { values, handleChange, reset };
}
// 사용 예시
// <input value={values.email} onChange={handleChange('email')} />
성능 고려사항
재사용성과 함께 성능도 고려해야 한다. 불필요한 연산이나 렌더를 막기 위해 다음을 기억한다.
- 메모이제이션: useMemo, useCallback으로 리렌더 방지
- 의존성 정확히 명시: 잘못된 의존성은 버그를 만든다
- 이펙트 정리: 구독과 타이머는 정리함수로 해제
결론
custom hook 설계 방법은 단순하지만 주의가 필요하다. 단일 책임, 명확한 인터페이스, 최소 의존성, 테스트 가능성이라는 원칙을 지키면 react 훅 재사용성은 자연스럽게 높아진다. 또한 작은 훅을 조합하는 패턴과 테스트, 문서화는 유지보수성과 확장성을 보장한다. hook 패턴 베스트프랙티스를 실무에 적용하면 팀 전체의 개발 생산성이 개선된다.