최근에 서버에서 데이터를 받아올 때, 여러개의 endpoints
에서 데이터를 한번에 가져와야하는 경우가 있었다. 한번에 10개의 endpoints
에서 데이터를 받아와서 5초의 주기로 갱신을 해야했다.
내가 처음에 시도했던 방법은 Promise.all()
을 사용해서 한번에 데이터를 비동기적으로 불러오는 방법이였는데, too many request(429 error) 에러를 마주하게 되었다. 왜냐하면 Promise.all()
은 여전히 10번의 http request를 서버로 보내기 때문이다.
이러한 현상을 해결하기 위해서, 먼저 각각의 fetch
에 delay를 준 다음, await
을 사용하여 endpoints를 돌면서(loop
) 순서대로 데이터를 fetch
할 수 있다. 즉 동기적으로 데이터를 series로 불러오는 것이다.
const delay = (ms = 1000) => new Promise(r => setTimeout(r, ms));
const getDataSeries = async items => {
let results = [];
for (let index = 0; index < items.length; index++) {
await delay();
const res = await fetch(items[index]);
results.push({ name: res.name, data: res.data });
}
return results;
};
일정한 주기로 데이터를 갱신해야 하는 경우,
setInterval
와setTimeout
중 어떤 것이 더 적합할까?
그리고 일정한 주기로 데이터를 갱신해야 하는 경우, setInterval
보다 setTimeout
을 사용하도록 권장된다. setInterval
을 사용하게 되면, 서버에서 데이터를 받아올 때 응답이 늦어지는 경우에도 일정하게 계속 다음 데이터를 fetch
하기 때문에, 에러를 핸들링할 수가 없게된다. 하지만 setTimeout
을 사용하면, 이전 데이터를 잘 받은 뒤에, recursive pattern을 사용해서 일정주기로 데이터를 fetch
할 수 있다.
아래와 같이 catch
에서 setTimeout
을 호출하게 되면, 데이터를 불러오는 과정에서 문제가 생겨서 다음 데이터를 불러올 수 있다. 이렇게 하지 않을 경우에는, 데이터를 불러오는 과정에서 문제가 생기면, 다음 데이터의 fetch
가 중단되어진다.
// useFetch custom hook
import { useEffect, useState, useCallback } from 'react';
import api from 'api/api';
import { Data, GroupData } from 'types/types';
type DataStatus = [GroupData, boolean, string];
const useFetch = (endpoints: string[]): DataStatus => {
const [data, setData] = useState<GroupData>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState('');
const fetchData = useCallback(
async (endpoints: string[]): Promise<any> => {
const temp: GroupData = [];
try {
await setLoading(false);
for (let i of endpoints) {
const res: Data = await api.spot(i);
await temp.push(res);
}
await setData(temp);
setTimeout(() => fetchData(endpoints), 5000); // (*)
} catch (err) {
await setError(error);
setTimeout(() => fetchData(endpoints), 5000); // (*)
}
},
[error]
);
useEffect(() => {
fetchData(endpoints);
}, [fetchData, endpoints]);
return [data, loading, error];
};
export default useFetch;
잘 보고 갑니당!