[Next.js] Next.js 시작하기 - (2)

zzzzsb·2023년 1월 13일
0

2.0 Patterns

Layout 패턴

_app.tsx 파일이 지나치게 커지는 것을 막기 위함.
Layout 컴포넌트를 만들어주고, App 컴포넌트에서 이를 사용한다.

/components/Layout.tsx

import NavBar from "./NavBar";

interface LayoutProps {
  children: React.ReactNode;
}
export default function Layout({ children }: LayoutProps) {
  return (
    <>
      <NavBar />
      <div>{children}</div>
    </>
  );
}

/pages/_app.tsx

...
export default function App({ Component, pageProps }: AppProps) {
  return (
    <Layout>
      <Component {...pageProps} />
    </Layout>
  );
}

Head 컴포넌트

  • 리액트에서 react-helmet으로 구현했던 것들을 Next.js에서는 간단히 Head 컴포넌트로 구현 가능.

/components/Seo.tsx

import Head from "next/head";

interface SeoProps {
  title: string;
}
export default function Seo({ title }: SeoProps) {
  return (
    <Head>
      <title>{title} | Next Movies</title>
    </Head>
  );
}

/pages/index.tsx

import Seo from "../components/Seo";

export default function Home() {
  return (
    <div>
      <Seo title="Home" />
      <h1 className="active">Hello</h1>
    </div>
  );
}

Head 컴포넌트 사용하는 Seo 컴포넌트를 만들어주고, 각 컴포넌트에서 Seo 컴포넌트를 추가해준다.


2.1 Fetching Data

public 폴더에 있는 파일들은 / 경로로 쉽게 import 가능하다.

Image

next.js에서는 이미지 최적화를 위해 img 태그 대신 Image 태그를 사용한다.

import Image from "next/image";
...
<Image src="/vercel.svg" alt="" width={100} height={30} />
...

src, width, height는 필수 속성이다.

The Movie Database(TMDB
API 키 발급

pages/index.tsx

interface MovieProps {
  id: number;
  original_title: string;
}

export default function Home() {
  const [movies, setMovies] = useState([]);
  useEffect(() => {
    (async () => {
      const { results } = await (
        await fetch(
          `https://api.themoviedb.org/3/movie/popular?api_key=${API_KEY}`
        )
      ).json();
      console.log(results);
      setMovies(results);
    })();
  }, []);
  return (
    <div>
      <Seo title="Home" />
      {!movies && <h4>Loading...</h4>}
      {movies.map((movie: MovieProps) => (
        <div key={movie.id}>
          <h4>{movie.original_title}</h4>
        </div>
      ))}
    </div>
  );
}

발급받은 API 키와 API DOCS를 참고하여 데이터를 fetch하는 코드를 작성해줬다.


2.2 Redirect and Rewrite

  • 위에서 API 요청하는 코드를 작성했는데, Network 탭에서 확인해보면 내 API 키가 쉽게 노출되고 있음을 확인할 수 있다. (popular?api_key=~ )

Redirect

next.config.js

const nextConfig = {
  reactStrictMode: true,
  async redirects() {
    return [
      {
        source: "/contact",
        destination: "/form",
        permanent: false,
      },
    ];
  },
};

module.exports = nextConfig;
  • 유저가 source의 url로 방문하면, destination으로 리다이렉트 해준다.
  • 사용자가 ~~~url/contact에 접속하면, ~~~url/form으로 리다이렉트 됨.
  • 유저는 url이 바뀌었다는 사실을 알게된다.

Rewrite

  • 유저를 리다이렉트 시키지만, url은 변하지 않는다.
  • Rewrite를 이용해 API 키를 숨길수 있다.

next.config.js

...
async rewrites() {
    return [
      {
        source: "/api/movies",
        destination: `https://api.themoviedb.org/3/movie/popular?api_key=${API_KEY}`,
      },
    ];
  },
};
...

index.tsx

...
const { results } = await (await fetch(`/api/movies`)).json();

index.tsx에서 API 요청하는 코드 주소도 변경해준다.

  • url 프록시 역할, destination 경로를 masking 해줌.

.env

만약 내 API_KEY를 config 파일에 작성하고 싶지 않다면, .env 파일에 API_KEY를 저장해두고 process.env.API_KEY 로 접근해 사용해도 된다.
(.gitignore에 .env 추가)


2.3 Server Side Rendering

이제까지 만든 페이지는 Loading이라는 화면이 실제로 보이지는 않지만, 페이지 소스를 확인해보면 현재 영화데이터가 들어와있음에도 불구하고 영화데이터는 존재하지 않고, 데이터가 들어와있지 않은 초기 상태인 Loading 코드가 들어가있다.

하지만 어떤 경우에는 이런 Loading을 보여주고 싶지 않을 수 있다.

  • 데이터 fetch, server 작업을 모두 마친 후 페이지 rendering이 되기를 원하는 경우 (SSR)

next.js에서는 위와같은 작업을 getServerSideProps 함수를 통해 구현할 수 있다.

getServerSideProps

export default function Home({ results }) {
  return (
    <div className="container">
      <Seo title="Home" />
      {results?.map((movie: MovieProps) => (
        <div className="movie" key={movie.id}>
          <img src={`https://image.tmdb.org/t/p/w500/${movie.poster_path}`} />
          <h4>{movie.original_title}</h4>
        </div>
      ))}
      ...
    </div>
  );
}

export async function getServerSideProps() {
  const { results } = await await (
    await fetch(`http://localhost:3000/api/movies`)
  ).json();
  return {
    props: {
      results,
    },
  };
}
  • 함수명 바꾸면 안됨.
  • 위 코드는 server에서만 작동하기 때문에, API_KEY를 여기에 작성해주면 절대로 client에게 보이지 않는다.
  • _app.tsx에서 pageProps가 위에서 return된 results 이다.

웹 페이지에서 페이지소스를 보면 이제 영화데이터의 정보도 함께 볼 수 있다.(데이터 페칭된 이후이기 때문에)

하지만 위 함수를 사용해주면 데이터가 페치되기 전까지는 아무것도 보이지 않을것이다.

개발자는 로딩화면이 보이고 데이터를 나중에 띄울 것인지,
SSR을 사용해 데이터를 모두 받은 후 띄울 것인지 선택해서 사용하면 된다!

  • SSR을 사용한 경우에는 사용자가 자바스크립트를 비활성화했더라도 작동할 것(서버에서 HTML 만들어줘서 보내니까)
profile
성장하는 developer

0개의 댓글