무한 스크롤을 생각한 이유
사용자의 편의성
과 페이지 초기 로딩 속도
를 동시에 잡을 수 있는 방법이라고 생각하였다.왜 useInfiniteQuery인가?
통신을 통해 가져온 데이터를 캐싱하기에 편리하다.
query 내에 자체적으로 다음 페이지를 가져오는 속성을 지니고 있다.
isFetching, hasNextPage 등 무한 스크롤을 구현하는데 필요한 정보도 속성으로 가지고 있다.
const {
data: pokemons,
error: isError,
fetchNextPage,
hasNextPage,
isFetching: isLoading,
isFetchingNextPage,
status,
} = useInfiniteQuery({
queryKey: ["projects"],
queryFn: ({ pageParam }) => api.pokemon.getPokemonList({ pageParam }),
initialPageParam: 6,
getNextPageParam: (lastPage, allPages, lastPageParam) => {
if (lastPage.length === 0) {
return undefined;
}
return lastPageParam + 6;
},
});
class PokemonAPI {
private axios: AxiosInstance;
constructor(axios: AxiosInstance) {
this.axios = axios;
}
async getPokemonList({ pageParam = 6 }) {
const path = `/api?cursor=${pageParam}`;
const response = await this.axios.get<Promise<TPokemon[]>>(path);
const pokemonsData = await response.data;
return pokemonsData;
}
}
Next.js의 서버에서는 어떤 처리를 해주어야 할까?
Next.js를 사용하면서 데이터 통신을 다룰 때 고려해야하는 새로운 문제로 이제는 서버에서 어떻게 통신을 해야하는지 설정해야 한다.
import { NextResponse } from "next/server";
import axios from "axios";
const TOTAL_POKEMON = 151;
export const GET = async (request: Request) => {
try {
const allPokemonPromises = Array.from({ length: TOTAL_POKEMON }, (_, index) => {
const id = index + 1;
return Promise.all([
axios.get(`https://pokeapi.co/api/v2/pokemon/${id}`),
axios.get(`https://pokeapi.co/api/v2/pokemon-species/${id}`)
]);
});
const allPokemonResponses = await Promise.all(allPokemonPromises);
const allPokemonData = allPokemonResponses.map(([response, speciesResponse], index) => {
const koreanName = speciesResponse.data.names.find(
(name: any) => name.language.name === "ko"
);
return { ...response.data, korean_name: koreanName?.name || null };
});
return NextResponse.json(allPokemonData);
} catch (error) {
return NextResponse.json({ error: "Failed to fetch data" });
}
};
이번 과제를 위해 제공된 서버 코드
아직 서버의 로직을 잘 아는 것은 아니지만 ts 코드는 읽을 수 있으니까 총 151개의 Array를 생성해서 각 index를 기준으로 포켓몬 api를 호출하여 한국 이름까지 속성으로 넣어주는 작업을 한다는 것까지 이해를 하였다.
이를 기준으로 무한 스크롤을 위한 로직으로 수정을 해보자!
import axios from "axios";
import { NextRequest, NextResponse } from "next/server";
export const GET = async (request: NextRequest) => {
const DATA_NUM_PER_PAGE = 6;
const TOTAL_POKEMON = Number(request.nextUrl.searchParams.get("cursor"));
try {
const allPokemonPromises = Array.from(
{ length: DATA_NUM_PER_PAGE },
(_, index) => {
const id = TOTAL_POKEMON - DATA_NUM_PER_PAGE + index + 1;
return Promise.all([
axios.get(`https://pokeapi.co/api/v2/pokemon/${id}`),
axios.get(`https://pokeapi.co/api/v2/pokemon-species/${id}`),
]);
}
);
const allPokemonResponses = await Promise.all(allPokemonPromises);
const allPokemonData = allPokemonResponses.map(
([response, speciesResponse], index) => {
const koreanName = speciesResponse.data.names.find(
(name: any) => name.language.name === "ko"
);
return { ...response.data, korean_name: koreanName?.name || null };
}
);
return NextResponse.json(allPokemonData);
} catch (error) {
return NextResponse.json({ error: "Failed to fetch data" });
}
};
페이지가 증가할 때마다 6씩 증가하게 클라이언트 사이드에서 로직을 작성했으므로 서버에도 DATA_NUM_PER_PAGE
라는 변수를 6으로 선언하여 관리한다.
url에서 query parameter에서 cursor의 값을 받아 몇 번째까지 포켓몬 데이터를 불러올지 설정하였다.
Array는 DATA_NUM_PER_PAGE
만큼의 크기로 생성하고 마지막으로 호출할 포켓몬 id를 기준으로 이전 6개의 포켓몬 데이터를 호출한다.
결과