DEVLOG

Next.js ํ”„๋กœ์ ํŠธ :: Open API (TMDB), React-Query ๋ณธ๋ฌธ

frontend/next.js

Next.js ํ”„๋กœ์ ํŠธ :: Open API (TMDB), React-Query

meroriiDev 2023. 6. 22. 15:13
728x90
๋ฐ˜์‘ํ˜•
๐Ÿ“–  ๋ชฉ์ฐจ

     

    ๐ŸŽˆ TMDB Open API

    ์ด๋ฒˆ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” TMDB API์—์„œ ์ œ๊ณตํ•˜๋Š” Open API๋ฅผ ์‚ฌ์šฉํ•ด ์˜ํ™” ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ฌ ์˜ˆ์ •์ด๋‹ค.

     

    API key ๋ฐœ๊ธ‰

    ๋ฐ์ดํ„ฐ๋ฅผ ์ด์šฉํ•˜๋ ค๋ฉด TMDB ์‚ฌ์ดํŠธ - ์„ค์ • - api์—์„œ api key๋ฅผ ๋ฐœ๊ธ‰๋ฐ›์•„์•ผํ•œ๋‹ค.

    https://api.themoviedb.org/3/movie/{movie_id}?api_key=<<api_key>>&language=en-US

     

    API docs

    TMDB API docs์— ์ค€๋น„๋˜์–ด ์žˆ๋Š” api๋“ค์„ ์ด๊ฒƒ์ €๊ฒƒ ์กฐํšŒํ•ด๋ณด๋ฉฐ ์‚ฌ์šฉํ•  api๋“ค์„ ์ฐพ์•„๋ƒˆ๋‹ค!์ƒ๊ฐ๋ณด๋‹ค ๋‹ค์–‘ํ•œ api๋“ค์ด ์žˆ์–ด์„œ ์—ฌ๋Ÿฌ ๊ธฐ๋Šฅ์„ ๊ฐ€์ง„ ํŽ˜์ด์ง€๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™๋‹ค.

     

    ๐ŸŽˆ React-Query

    ๋ฐ์ดํ„ฐ fetching ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ์ด๋ฒˆ์—” ํ•œ๋ฒˆ์ฏค ์‚ฌ์šฉํ•ด๋ณด๊ณ  ์‹ถ์—ˆ๋˜ react-query๋ฅผ ์‚ฌ์šฉํ•ด๋ณด๊ธฐ๋กœ๋กœ ํ–ˆ๋‹ค. ์—ฌ๋Ÿฌ๊ฐ€์ง€ ์žฅ์ ์ด ์žˆ์ง€๋งŒ ์ฃผ๋กœ ์ด๋Ÿฌ์ด๋Ÿฌํ•œ ์žฅ์  ๋•Œ๋ฌธ์— ์ตœ๊ทผ์— ๋งŽ์ด ๋– ์˜ค๋ฅด๊ณ  ์žˆ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ผ๊ณ  ํ•œ๋‹ค.

    ์™œ react-query์ธ๊ฐ€?

    • ์บ์‹ฑ
    • get์„ ํ•œ ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•ด update๋ฅผ ํ•˜๋ฉด ์ž๋™์œผ๋กœ get์„ ๋‹ค์‹œ ์ˆ˜ํ–‰ํ•œ๋‹ค. (์˜ˆ๋ฅผ ๋“ค๋ฉด ๊ฒŒ์‹œํŒ์˜ ๊ธ€์„ ๊ฐ€์ ธ์™”์„ ๋•Œ ๊ฒŒ์‹œํŒ์˜ ๊ธ€์„ ์ƒ์„ฑํ•˜๋ฉด ๊ฒŒ์‹œํŒ ๊ธ€์„ getํ•˜๋Š” api๋ฅผ ์ž๋™์œผ๋กœ ์‹คํ–‰)
    • ๋ฐ์ดํ„ฐ๊ฐ€ ์˜ค๋ž˜ ๋˜์—ˆ๋‹ค๊ณ  ํŒ๋‹จ๋˜๋ฉด ๋‹ค์‹œ get (invalidateQueries)
    • ๋™์ผ ๋ฐ์ดํ„ฐ ์—ฌ๋Ÿฌ๋ฒˆ ์š”์ฒญํ•˜๋ฉด ํ•œ๋ฒˆ๋งŒ ์š”์ฒญํ•œ๋‹ค. (์˜ต์…˜์— ๋”ฐ๋ผ ์ค‘๋ณต ํ˜ธ์ถœ ํ—ˆ์šฉ ์‹œ๊ฐ„ ์กฐ์ ˆ ๊ฐ€๋Šฅ)
    • ๋ฌดํ•œ ์Šคํฌ๋กค (Infinite Queries (opens new window))
    • ๋น„๋™๊ธฐ ๊ณผ์ •์„ ์„ ์–ธ์ ์œผ๋กœ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.
    • react hook๊ณผ ์‚ฌ์šฉํ•˜๋Š” ๊ตฌ์กฐ๊ฐ€ ๋น„์Šทํ•˜๋‹ค.

    ์ด๋Ÿฌํ•œ ์žฅ์  ์ค‘ ๋ฌดํ•œ ์Šคํฌ๋กค์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด ํฐ ์žฅ์ ์ด ๋Š๊ปด์งˆ ๊ฒƒ ๊ฐ™์•„ react-query๋ฅผ ์„ ํƒํ–ˆ๋‹ค.

    ์„ค์น˜

    npm i react-query

     

    _app.tsx ์„ธํŒ…

    _app.tsx์— ๋จผ์ € QueryClientProvider๋ฅผ ์„ธํŒ…ํ•ด์•ผํ•œ๋‹ค.

    //base
    import type { AppProps } from "next/app";
    import Head from "next/head";
    
    //libraries
    import { QueryClientProvider, QueryClient } from "react-query";
    
    //style
    import "../styles/globals.css";
    
    function MyApp({ Component, pageProps }: AppProps) {
      const queryClient = new QueryClient();
    
      return (
        <QueryClientProvider client={queryClient}>
          <Head>
            <meta name="viewport" content="width=device-width, initial-scale=1" />
            <title>MERORIIVING</title>
            <link rel="icon" href="/favicon.ico" />
          </Head>
          <Component {...pageProps} />
        </QueryClientProvider>
      );
    }
    
    export default MyApp;

     

    useQuery

    ์กฐํšŒ๋Š” useQuery๋ฅผ ์ด์šฉํ–ˆ๋‹ค.

    export const getMovieList = async (list: string, page = 1) => {
      const { data } = await axios.get(
        `https://api.themoviedb.org/3/movie/${list}?api_key=${process.env.NEXT_PUBLIC_API_KEY}&language=ko-KR&page=${page}`
      );
      return data;
    };
    
    export const getMovieInfo = async (id: string) => {
      const { data } = await axios.get(
        `https://api.themoviedb.org/3/movie/${id}?api_key=${process.env.NEXT_PUBLIC_API_KEY}&language=ko-KR`
      );
      return data;
    };
    
    ...
    
    const { isLoading:listLoading, data:listData } = useQuery("movieData", () => getMovieList('popular'));
    
    const { isLoading:movieLoading, data:movieData } = useQuery("movie", () => {
        return getMovieInfo(params.id);
      });
    

     

    โ— ์ƒ์„ธํŽ˜์ด์ง€ ์„ธํŒ… ์‹œ ๊ฒช์€ ์ด์Šˆ

    โ”‚  โ””โ”€โ”€ pages
    โ”‚      โ”œโ”€โ”€ detail
    โ”‚      โ”‚   โ””โ”€โ”€ [id].tsx
    โ”‚      โ”œโ”€โ”€ _app.tsx
    โ”‚      โ”œโ”€โ”€ _document.tsx
    โ”‚      โ””โ”€โ”€ index.tsx

    detail์˜ subpage๋ฅผ ๊ฐ–๋Š” ๊ตฌ์กฐ๋กœ ์ž‘์—…ํ•˜์—ฌ ๋ฉ”์ธ์—์„œ ์นด๋“œ ํด๋ฆญ์‹œ detail/[id]๋กœ Link๋ฅผ ์—ฐ๊ฒฐํ•ด์ฃผ์—ˆ๋‹ค.

    <Link href={`/detail/${id}`}>
        <Poster posterUrl={imageUrl(poster_path)} />
        <div>{title}</div>
    </Link>

     

    ๐Ÿ’ฌ ๊ฒช์€ ์ด์Šˆ Next.js ๋ผ์šฐํŠธ์ฟผ๋ฆฌ undefined ์˜ค๋ฅ˜

     

    Next.js ๋ผ์šฐํŠธ์ฟผ๋ฆฌ undefined ์˜ค๋ฅ˜

    subpage์˜ query๋ฅผ ์ด์šฉํ•ด์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์™€์•ผํ•˜๋Š”๋ฐ useRouter๋กœ query๋ฅผ ์ฝ๋Š” ์‹œ๊ธฐ๋ณด๋‹ค useQuery๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถ€๋ฅด๋Š” ์‹œ๊ธฐ๊ฐ€ ๋” ๋นจ๋ผ undefined๋กœ ๊ฒ€์ƒ‰์ด ๋˜๋Š” ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค.During prerendering, the router's q

    velog.io

     

    ๐ŸŽˆ ๊ฒฐ๊ณผ

    ๋ฉ”์ธํŽ˜์ด์ง€

    ์ƒ์„ธํŽ˜์ด์ง€

    ๐Ÿ‘‰ Next?

    ๊ทธ๋Ÿฐ๋ฐ,, ์•„๋ฌด๋ž˜๋„ ๋‚ด๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” api์˜ ๊ฒฝ์šฐ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ณ€ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋„ ์•„๋‹ˆ๊ณ , SEO์ธก๋ฉด์—์„œ ์œ ๋ฆฌํ•œ ๊ฒƒ์ด ์ข‹์€ ํŽ˜์ด์ง€๋กœ ์„ธํŒ…ํ•˜๋Š”๊ฒŒ ์ข‹์ง€ ์•Š์„๊นŒ? ํ•˜๋Š” ์ƒ๊ฐ๊ณผ ํ•จ๊ป˜ Next.js๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋œ ์ด์œ  ์ค‘ ํ•˜๋‚˜์ธ SSR๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์„ธํŒ…ํ•˜๊ณ ์‹ถ์–ด์กŒ๋‹ค.
    ๊ทธ๋ž˜์„œ ๋ฐ์ดํ„ฐ๋ฅผ SSR๋กœ ์„ธํŒ…ํ•˜๊ธฐ์œ„ํ•ด getServerSideProps๋ฅผ ๋„์ž…ํ•  ์˜ˆ์ •์ด๋‹ค.

     

     

    728x90
    ๋ฐ˜์‘ํ˜•
    Comments