리액트는 useState
, useEffect
, 그리고 useRef
같이 몇몇 내재하고 있는 Hook이 존재합니다. 기본적으로 제공되는 Hook들 이외에 추가적으로 필요한 기능이 있다면 직접 훅을 만들어서 사용할 수 있습니다. 이것을 커스텀 훅(Custom Hook)이라고 부릅니다. 커스텀 훅을 만드는 이유는 여러 컴포넌트에서 반복적으로 사용되는 로직을 훅으로 만들어 재사용하기 위함입니다.
이 예시에서는 API 호출을 통해 개와 고양이 이미지를 가져오는 커스텀 훅을 만들어서, 이를 활용한 이미지 갤러리 웹 페이지를 구현해 보겠습니다.
커스텀 훅의 이름은
use
로 시작하여 훅이라는 것을 알게 하고use
뒤에는 대문자로 시작합니다.
리액트의 useState
와 useEffect
를 이용하여 데이터를 가져오는 비동기 로직을 처리하는 커스텀 훅 useFetch
를 작성할 수 있습니다. 이 훅은 데이터를 요청하는 로직을 추상화해 재사용할 수 있도록 만들고, API에서 데이터를 가져오는 동안 상태를 관리합니다.
// useFetch.jsx
import { useEffect, useState } from 'react';
const useFetch = (url) => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const fetchData = () => {
setLoading(true);
fetch(url)
.then((res) => res.json())
.then((data) => {
setData(data);
setLoading(false);
})
.catch((err) => {
setError(err);
setLoading(false);
});
};
useEffect(() => {
fetchData();
}, [url]);
return { data, loading, error, fetchData };
};
export default useFetch;
data
: 데이터를 저장하는 상태입니다. 초기값은 null
로 설정하고, fetch를 통해 데이터를 받아오면 업데이트됩니다.loading
: 데이터를 가져오는 동안 로딩 상태를 관리합니다.error
: 에러가 발생했을 때 에러 메시지를 지정합니다.fetchData
함수는 주어진 url에서 데이터를 받아오는 비동기 로직을 수행합니다. 데이터를 가져오는 동안 loading
상태를 true
로 설정한 후, 성공적으로 데이터를 받아오면 data
상태를 업데이트하고, 실패하면 error
상태를 설정합니다. 데이터를 받아오거나 에러 상태를 업데이트하면 로딩 상태를 false
로 돌려놓습니다.useEffect
는 컴포넌트가 처음 렌더링될 때 fetchData
를 호출해 데이터를 받아오도록 설정합니다. 또한, url
이 변경될 때마다 데이터를 다시 요청하도록 의존성 배열에 url을 넣었습니다.data
, loading
, error
그리고 fetchData
함수를 리턴하여 컴포넌트에서 사용할 수 있습니다.작성한 커스텀 훅 useFetch
를 활용하여 개와 고양이 이미지를 가져오는 갤러리를 만들어보겠습니다. 버튼 클릭 시마다 새로운 이미지를 요청하고, 로딩 상태나 에러 상태를 관리할 수 있습니다.
// App.jsx
import './App.css';
import useFetch from './useFetch';
const dogUrl = 'https://dog.ceo/api/breeds/image/random';
const catUrl = 'https://api.thecatapi.com/v1/images/search';
function App() {
const {
data: dogData,
loading: dogLoading,
fetchData: dogFetchData,
} = useFetch(dogUrl);
const {
data: catData,
loading: catLoading,
fetchData: catFetchData,
} = useFetch(catUrl);
return (
<>
<h1> Animal gallery</h1>
<p>
애니멀 갤러리에 오신 여러분 환영합니다. <br /> 각 버튼을 클릭하여 사진을
업데이트 해보세요.
</p>
<div className="container">
<h2> 🐶 Dog Zone</h2>
<button onClick={() => dogFetchData(dogUrl)}>new Dog</button>
{dogLoading ? (
<p className="loading">Loading . . .</p>
) : (
dogData && <img src={dogData.message} alt="dog" />
)}
</div>
<div className="container">
<h2>🐱Cat Zone</h2>
<button onClick={() => catFetchData(catUrl)}>new Cat</button>
{catLoading ? (
<p className="loading">Loading . . .</p>
) : (
catData && <img src={catData[0].url} alt="cat" />
)}
</div>
</>
);
}
export default App;
data
, loading
상태를 별도로 관리하며, 버튼 클릭 시 새로운 이미지를 요청할 수 있도록 fetchData
함수를 실행합니다.결론
커스텀 훅을 사용하면 이렇게 API 데이터를 여러 컴포넌트에서 손쉽게 불러오고, 공통 로직을 추상화하여 재사용성을 높일 수 있습니다. 이번 예시에서는 단순한 이미지 갤러리였지만, 다양한 비동기 로직을 처리할 때 매우 유용하게 활용할 수 있습니다.
✅ 참고