[TIL] 내일배움캠프 React 과정 2024.07.03

김형빈·2024년 7월 3일
0

내일배움캠프

목록 보기
53/81
post-custom-banner

오늘의 한 일

  • 포켓몬 도감 (개인 프로젝트)
    • 무한 스크롤 (구현 중...)

Next.js와 useInfiniteQuery를 활용하여 무한 스크롤 구현하기

무한 스크롤을 생각한 이유

  • 페이지에 보여줘야 하는 포켓몬의 종류가 많고, 데이터에 이미지가 포함된 만큼 무겁기 때문에 사용자의 편의성페이지 초기 로딩 속도를 동시에 잡을 수 있는 방법이라고 생각하였다.

왜 useInfiniteQuery인가?

  • 통신을 통해 가져온 데이터를 캐싱하기에 편리하다.

  • query 내에 자체적으로 다음 페이지를 가져오는 속성을 지니고 있다.

  • isFetching, hasNextPage 등 무한 스크롤을 구현하는데 필요한 정보도 속성으로 가지고 있다.

    예시 코드(useInfiniteQuery)

    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;
      },
    });

    예시 코드(pokemonAPI)

    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를 사용하면서 데이터 통신을 다룰 때 고려해야하는 새로운 문제로 이제는 서버에서 어떻게 통신을 해야하는지 설정해야 한다.

    기존의 route.ts(서버) 코드

    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를 호출하여 한국 이름까지 속성으로 넣어주는 작업을 한다는 것까지 이해를 하였다.

  • 이를 기준으로 무한 스크롤을 위한 로직으로 수정을 해보자!

    수정된 route.ts(서버) 코드

     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개의 포켓몬 데이터를 호출한다.

결과

  • 아직 스크롤에 관련한 로직을 구현하지는 못하였지만 임시로 헤더를 누르면 다음 페이지의 포켓몬 데이터를 불러오는 데 까지는 성공하였다!
profile
The secret of getting ahead is getting started.
post-custom-banner

0개의 댓글