React # 11 _ React에서 Custom Hook을 활용한 데이터 Fetching과 최적화
React에서 Custom Hook을 활용한 데이터 Fetching과 최적화
React 프로젝트를 진행하다 보면 데이터 Fetching과 관련된 코드를 여러 컴포넌트에서 반복적으로 작성하게 된다. 이런 반복을 줄이고 코드의 재사용성을 높이기 위해 Custom Hook을 활용할 수 있다. 이번 글에서는 데이터를 Fetching하기 위한 Custom Hook을 만들고 사용하는 과정을 정리해 보았다.
1. 기존 데이터 Fetching 코드
기존의 Fetching 로직은 아래와 같은 형태로 작성된다.
const [availablePlaces, setAvailablePlaces] = useState([]); // 데이터 상태
const [isLoading, setIsLoading] = useState(false); // 로딩 상태
const [error, setError] = useState(null); // 에러 상태
useEffect(() => {
async function fetchPlaces() {
setIsLoading(true);
try {
const response = await fetch('http://localhost:3000/places'); // API 호출
const resData = await response.json();
if (!response.ok) {
throw new Error(resData.message || 'Could not fetch places.');
}
setAvailablePlaces(resData.places); // 상태 업데이트
} catch (error) {
setError({ message: error.message || 'Failed to fetch places' }); // 에러 처리
}
setIsLoading(false); // 로딩 종료
}
fetchPlaces();
}, []);
이 코드는 데이터를 가져오는 데 필요한 상태 관리(useState), 로딩 처리, 에러 핸들링 등 필수 요소를 포함하고 있지만, 여러 컴포넌트에서 반복적으로 작성해야 한다는 단점이 있다.
2. Custom Hook 작성
반복적인 Fetching 로직을 Custom Hook으로 추출하여 코드 재사용성을 높일 수 있다.
import { useEffect, useState } from 'react';
export function useFetch(fetchFn, initialValue) {
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
const [fetchedData, setFetchedData] = useState(initialValue);
useEffect(() => {
async function fetchData() {
setIsLoading(true);
try {
const data = await fetchFn(); // fetchFn 실행
setFetchedData(data); // 데이터 상태 업데이트
} catch (error) {
setError({ message: error.message || 'Failed to fetch data' }); // 에러 처리
}
setIsLoading(false); // 로딩 종료
}
fetchData();
}, [fetchFn]);
return {
isLoading,
error,
fetchedData,
};
}
3. Custom Hook의 주요 포인트
- Hook 이름 규칙:
React에서 Custom Hook은 이름이 반드시 use로 시작해야 한다. 예를 들어, useFetch라는 이름은 React가 이 함수를 Hook으로 인식하게 한다. 단, React의 기본 Hook 이름(useState, useEffect)과 겹치지 않도록 주의해야 한다. - 매개변수 활용:
- fetchFn: 데이터를 가져오는 비동기 함수로, API 호출 로직을 포함한다.
- initialValue: 초기 상태 값을 설정한다.
- 상태 관리:
- isLoading: 로딩 상태를 나타낸다.
- error: 발생한 에러를 저장한다.
- fetchedData: Fetching된 데이터를 저장한다.
- 결과 반환:
return을 통해 필요한 상태와 데이터를 반환하면, Hook을 사용하는 컴포넌트에서 쉽게 활용할 수 있다.
4. Custom Hook 사용 예시
Custom Hook을 사용하여 데이터를 Fetching하는 컴포넌트를 작성해 보자.
const {
isLoading,
error,
fetchedData: availablePlaces,
} = useFetch(fetchSortedPlaces, []);
- Object Destructuring: fetchedData를 availablePlaces라는 이름으로 변경.
- Hook에서 반환된 상태를 활용하여 로딩 중, 에러 발생, 데이터 출력 상태를 간단히 구현.
5. 정렬된 데이터 Fetching 함수
Fetching된 데이터를 정렬하기 위해 추가 로직을 포함한 비동기 함수(fetchSortedPlaces)를 작성할 수 있다.
async function fetchSortedPlaces() {
const places = await fetchAvailablePlaces();
return new Promise((resolve) => {
navigator.geolocation.getCurrentPosition((position) => {
const sortedPlaces = sortPlacesByDistance(
places,
position.coords.latitude,
position.coords.longitude
);
resolve(sortedPlaces);
});
});
}
Promise를 사용한 이유
- navigator.geolocation.getCurrentPosition은 비동기 작업을 콜백으로 처리한다.
- async/await는 Promise 기반으로 동작하기 때문에, 콜백을 Promise로 감싸야 await로 사용할 수 있다.
async/await를 사용한 이유
- 코드의 가독성을 높이고, 콜백 지옥(Callback Hell)을 피하기 위해 사용.
- 비동기 흐름을 동기 코드처럼 작성할 수 있어 직관적이다.
6. 최종 형태
이제 fetchSortedPlaces를 useFetch의 fetchFn으로 전달하여 정렬된 데이터를 Fetching할 수 있다.
const {
isLoading,
error,
fetchedData: sortedPlaces,
} = useFetch(fetchSortedPlaces, []);
isLoading과 error를 활용하여 로딩 화면과 에러 메시지를 표시하고, sortedPlaces로 데이터를 화면에 출력하면 된다.
7. 마무리
Custom Hook을 사용하면 반복적인 Fetching 로직을 줄이고 코드의 가독성과 재사용성을 높일 수 있다. 또한, async/await와 Promise를 함께 사용하여 비동기 작업을 더 효율적으로 처리할 수 있다. React 개발에서 Custom Hook은 코드 구조를 간결하게 만드는 중요한 도구이므로 적극적으로 활용해 보자! 😊