Promise.all
// 예시 fetch
const getPosts = async () => {
await new Promise((reslove) => setTimeout(reslove, 2000));
const response = await fetch('https://jsonplaceholder.typicode.com/posts');
const data = response.json();
return data;
};
const getComments = async () => {
await new Promise((reslove) => setTimeout(reslove, 5000));
const response = await fetch('https://jsonplaceholder.typicode.com/posts');
const data = response.json();
return data;
};
< 1. 순차적 실행 >
const posts = await getPosts();
const comments = await getComments();
< 2. 병렬 실행 >
const [posts, comments] = await Promise.all([getPosts(), getComments()]);
첫번째 처럼 각각 개별로 실행하면 먼저 getPosts 함수가 완료될때까지 기다린후 getComments가 실행되며 순차적으로 작업이 실행된다. 순차적으로 실행되기 때문에 시간이 오래 걸릴수 있다.
두번째 promist.all은 getPosts와 getComments 함수가 동시에 실행되어 두 작업이 모두 완료 될때까지 기다렸다가 결과를 반환한다. 병렬로 작업을 수행하기 때문에 시간을 단축할 수 있다.
Api Route
// page.tsx
const getMovie = async (type: string) => {
const url = `${process.env.TMDB_URL}/${type}?language=en-US&page=1`;
const options = {
method: 'GET',
headers: {
accept: 'application/json',
Authorization: `Bearer ${process.env.TMDB_TOKEN}`,
},
};
const response = await fetch(url, options);
const data = await response.json();
return data;
};
----------------------------------------------------------------
// 호출
const getMovie = async (type: string) => {
const url = `${process.env.TMDB_URL}/${type}?language=en-US&page=1`;
const options = {
method: 'GET',
headers: {
accept: 'application/json',
Authorization: `Bearer ${process.env.TMDB_TOKEN}`,
},
};
const response = await fetch(url, options);
const data = await response.json();
return data
};
client Api 호출 방식은 페이지가 로드된 후에 데이터 요청을 보내므로 초기 로딩에는 빠르지만 데이터가 사용자에게 보여지려면 약간의 시간이 필요하며 SEO에 불리하다.
// api/route.ts
export const GET = async (request: Request) => {
const { searchParams } = new URL(request.url);
const type = searchParams.get('type') || 'popular';
const lang = searchParams.get('lang') || 'en-US';
const page = searchParams.get('page') || 1;
const url = `${process.env.TMDB_URL}/${type}?language=${lang}&page=${page}`;
const options = {
method: 'GET',
headers: {
accept: 'application/json',
Authorization: `Bearer ${process.env.TMDB_TOKEN}`,
},
};
const response = await fetch(url, options);
const data = await response.json();
return Response.json(data);
};
------------------------------------------------------------------------
// 호출
const getMovie = async (type: string) => {
const response = await fetch(`${process.env.BASE_URL}/api?type=` + type);
const data = await response.json();
return data;
};
server api 호출 방식은 서버 측에서 데이터를 미리 가져와 페이지를 로드 하기때문에 초기 로딩시간이 길어질 수 있다. 하지만 페이지 로드 시점에 데이터가 포함되었기 때문에 사용자는 바로 준비된 페이지를 볼수있고 SEO에 유리하다.
무한 스크롤
npm i react-intersection-observer
// page.tsx
export const getMovie = async (type: string, page = 1) => {
const response = await fetch(`http://localhost:3000/api?type=` + type + '&page=' + page);
const data = await response.json();
return data;
};
// Loader.tsx
// ref : 지정할 요소
// inview : 지정한 요소가 나타났을때 체크 = boolean
const { ref, inView } = useInView();
const [movies, setMovies] = useState<IMovieType[]>([]);
const [loading, setLoading] = useState(false);
const pageRef = useRef(2);
useEffect(() => {
if (inView && !loading) {
setLoading(true);
getMovie('popular', pageRef.current++).then((res) => {
setMovies((prev) => [...prev, ...res.results]);
});
setTimeout(() => {
setLoading(false);
}, 300);
}
console.log(pageRef.current + '페이지');
}, [inView, movies]);
// ref 지정할 요소
<div ref={ref} className="flex justify-center">
<FaSpinner className="text-5xl text-white animate-spin" />
</div>
첫 페이지에 먼저 1번째 page data를 보여주고 지정한 ref의 요소가 나타나면 true로 변경되고 2페이지의 data로 movies state 배열 추가가된다.
이렇게 계속 지정한 ref의 요소가 true가 되면 다음 페이지의 data가 movies 배열로 추가되면서 useEffect가 의존성 배열에 의해 실행되어 보여지게 되므로 무한스크롤을 구현할 수 있다.