React.js 커스텀 훅스(Custom Hooks) 만들기: 코드 재사용성 극대화
React.js 커스텀 훅스(Custom Hooks) 만들기: 코드 재사용성 극대화
React.js는 컴포넌트 기반의 구조 덕분에 재사용 가능한 UI 요소를 쉽게 만들 수 있는 장점이 있습니다. 하지만 컴포넌트가 복잡해질수록 상태 관리와 로직의 재사용이 어려워질 수 있습니다. 이러한 문제를 해결하기 위해, React에서는 커스텀 훅스(Custom Hooks)
라는 개념을 도입했습니다. 커스텀 훅스를 사용하면 컴포넌트 간의 로직을 간단하고 효율적으로 공유할 수 있습니다. 이번 글에서는 커스텀 훅스의 개념, 작성 방법, 그리고 실전 예제를 통해 그 유용성을 알아보겠습니다.
커스텀 훅스의 개념
기본적으로 훅은 React 함수형 컴포넌트에서 상태와 생명주기 기능을 가능하게 하는 특수한 함수입니다. useState
, useEffect
같은 내장 훅은 이미 익숙하실 텐데요, 커스텀 훅은 이러한 내장 훅들을 조합해 나만의 훅을 만드는 것입니다.
커스텀 훅은 use
로 시작하는 이름을 가지고, 다른 훅처럼 상태와 생명주기 관리를 할 수 있습니다. 이를 통해 복잡한 로직을 재사용 가능하게 만들고, 코드를 더 깨끗하고 유지보수 가능하게 합니다.
예시: 기본 커스텀 훅
예를 들어, 사용자의 현재 시간을 관리하는 커스텀 훅을 만들어보겠습니다:
jsximport { useState, useEffect } from 'react'; function useCurrentTime() { const [currentTime, setCurrentTime] = useState(new Date()); useEffect(() => { const intervalId = setInterval(() => { setCurrentTime(new Date()); }, 1000); return () => clearInterval(intervalId); }, []); return currentTime; } export default useCurrentTime;
위 예제에서는 useCurrentTime
이라는 커스텀 훅을 만들어, 현재 시간을 초 단위로 갱신하도록 했습니다. 이제 이 훅을 필요한 컴포넌트에서 가져와 사용할 수 있습니다.
커스텀 훅 작성 방법
커스텀 훅을 작성할 때는 몇 가지 기본 원칙을 준수해야 합니다:
use
로 시작하는 이름: 모든 훅은use
로 시작해야 React가 이것이 훅임을 알 수 있습니다.- 다른 훅을 호출: 커스텀 훅 안에서는 기존의 훅(
useState
,useEffect
등)을 호출할 수 있습니다. - 함수형 컴포넌트에서만 호출: 커스텀 훅은 함수형 컴포넌트 또는 다른 커스텀 훅에서만 호출될 수 있습니다.
예시: API 데이터 페칭 커스텀 훅
HTTP 요청을 통해 데이터를 가져오는 일반적인 작업을 커스텀 훅으로 만들어보겠습니다:
jsximport { useState, useEffect } from 'react'; function useFetch(url) { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { setLoading(true); fetch(url) .then(response => { if (!response.ok) { throw new Error('Network response was not ok'); } return response.json(); }) .then(data => { setData(data); setLoading(false); }) .catch(error => { setError(error); setLoading(false); }); }, [url]); return { data, loading, error }; } export default useFetch;
이제 이 useFetch
훅을 사용하면 다양한 컴포넌트에서 데이터 페칭 로직을 공유할 수 있습니다:
jsximport React from 'react'; import useFetch from './useFetch'; function DataComponent({ url }) { const { data, loading, error } = useFetch(url); if (loading) return <p>Loading...</p>; if (error) return <p>Error: {error.message}</p>; return ( <div> <h1>Fetched Data:</h1> <pre>{JSON.stringify(data, null, 2)}</pre> </div> ); } export default DataComponent;
이와 같은 방식으로, 훅을 활용하여 코드의 중복을 줄이고, 필요한 곳에서 동일한 로직을 간편하게 재사용할 수 있습니다.
실전 팁
훅의 컴포넌트화
커스텀 훅을 작성할 때 컴포넌트의 특정 로직을 가능한 한 많이 분리하여, 훅의 독립성을 유지하려고 노력해야 합니다. 이렇게 하면 훅을 다양한 컴포넌트에서 재사용하기 쉬워집니다.
테스트와 디버깅
커스텀 훅도 다른 함수처럼 테스트가 필요합니다. Jest
와 같은 테스트 프레임워크를 사용해서 훅의 동작을 검증하세요. 예를 들어, React Testing Library
를 사용해 커스텀 훅을 테스트할 수 있습니다:
jsximport { renderHook } from '@testing-library/react-hooks'; import useFetch from './useFetch'; it('should fetch data', async () => { const { result, waitForNextUpdate } = renderHook(() => useFetch('https://api.example.com/data')); await waitForNextUpdate(); expect(result.current.data).not.toBeNull(); expect(result.current.loading).toBe(false); expect(result.current.error).toBeNull(); });
로직의 일반화
커스텀 훅을 만들 때는 최대한 일반화된 로직으로 작성하여 다양한 상황에서 재사용할 수 있도록 해야 합니다. 예를 들어, useInput
훅을 만들어 다양한 형태의 입력 컨트롤을 관리할 수 있습니다:
jsximport { useState } from 'react'; function useInput(initialValue) { const [value, setValue] = useState(initialValue); function handleChange(e) { setValue(e.target.value); } return { value, onChange: handleChange, }; } export default useInput;
이제 <input>
컴포넌트와 함께 사용할 수 있습니다:
jsximport React from 'react'; import useInput from './useInput'; function InputComponent() { const nameInput = useInput(''); return ( <div> <label> Name: <input type=