The Net Ninja <Next.js Tutorial> 강의 노트

9rganizedChaos·2021년 11월 22일
0
post-thumbnail

(해당 포스팅은 <The Net Ninja> 채널의 Next.js Tutorial의 강의노트 입니다. 링크를 통해 강의를 보실 수 있습니다.)

NextJS는 Server Side Rendering과 Static Site Generation을 지원하는 프레임워크이다. 다시 말해 Pre-render되는 리액트 웹사이트를 만들기 위한 프레임워크라고 할 수 있다.

Client Side Rendering 방식을 따르는 리액트의 경우, 기본 html 파일만을 서버로 부터 내려받고, js 스크립트를 통해 DOM을 구성해 다시 html에 그려준다. 브라우저에서 렌더링을 실행한다는 것이다! 그러나 React를 사용하다보면 때때로 우리는 이런 생각을 하게 된다. "나는 굳이 사용자와의 실시간성을 보장할 필요가 없는데도 CSR을 사용해야 하는 걸까?", "많은 양의 비디오나 이미지들은 컴퓨팅파워가 브라우저에 비해 뛰어난 서버에서 미리 처리해 html 파일을 내려주면 좋을텐데!" 하는 고민들 말이다.

NextJS와 함께 라면 html이 미리 렌더링된 상태로 브라우저로 내려오게 된다.
이러한 Pre-rendering은 퍼포먼스를 개선과 SEO 최적화에 유리하다.

01 NextJS 셋업!

npx create-next-app [프로젝트 이름]

create-react-app과 유사하게 위와 같은 명령어로 프로젝트를 시작해준다.

위와 같은 파일 구조를 확인할 수 있다!
(디테일한 부분에는 차이가 있을 수 있습니다.)

NextJS에서는 페이지 폴더에 생성되는 파일이 각각 하나의 페이지를 구성하게 된다.
이 때,_app.js 는 루트 컴포넌트와 같다고 할 수 있을 것이다.

02 페이지와 라우트

the file name and location of each component is tied to the route for that particular page

예를 들어, pages 폴더 내에 About.js라는 파일을 생성하면, /about으로 해당 페이지가 라우팅된다.

만일 /ninja/test와 같이 라우팅을 하고 싶다면, pages/ninja/test.js와 같이 폴더 및 파일 구조를 구성해주면 된다. 더 나아가 /ninja/test와 동시에 /ninja를 사용하고 싶다면 해당 ninja 폴더 내부에 index.js 파일을 생성해주고 내용을 작성해주면 된다!

모쪼록 pages 폴더에 들어있는 파일 및 폴더 구조에 따라서 라우팅이 자동으로 생성된다는 점을 기억하면 되겠다!

03 컴포넌트 추가하기

여러 페이지에 재사용되는 컴포넌트를 구성해주고 싶다면, 리액트에서와 동일하게 사용해주면 된다!
루트 디렉토리에 components와 같은 이름의 폴더를 만들어주고 해당 폴더 내부에 필요한 컴포넌트들을 작성해준다.

예를 들어 각 페이지에 삽입되는 Nav바와 Footer를 생성해줄 수 있을 것이다.

const Footer = () => {
  return <footer>Copyright 2021 Ninja List</footer>;
};

export default Footer;

04 페이지들을 연결하기

우리는 기존에 react-router<Link>를 활용했던 것처럼, NextJS가 제공해주는 Link를 활용해, 페이지 간의 이동을 시도할 것이다.

import Link from "next/link";

const Navbar = () => {
  return (
    <nav>
      <Link href="/">
        <a>Home</a>
      </Link>
      <Link href="/about">
        <a>About</a>
      </Link>
      <Link href="/ninjas">
        <a>Ninja Listing</a>
      </Link>
    </nav>
  );
};

export default Navbar;

그런데 조금 이상하다고 생각되는 점은!

분명 NextJS는 SSR 방식이라고 했던 것 같은데 왜 페이지 이동 시에 네트워크 탭에 새로운 html 파일을 받아오는 내역이 보이지 않는가...!?

05 레이아웃 컴포넌트 만들기

위에서 NavbarFooter를 생성해주었다.
우리는 이 두 컴포넌트를 일일이 각 페이지에 삽입해주는 것보다 더 효율적인 방안을 갖고 있다.
바로 Layout 컴포넌트를 만들어주고 이것을 _app.js에 추가해주는 것이다!

방식은 다음과 같다.

// Layout.js
import Footer from "./Footer";
import Navbar from "./Navbar";

const Layout = ({ children }) => {
  return (
    <div className="content">
      <Navbar />
      {children}
      <Footer />
    </div>
  );
};

export default Layout;
import Layout from "../components/Layout";
import "../styles/globals.css";

function MyApp({ Component, pageProps }) {
  return (
    <Layout>
      <Component {...pageProps} />
    </Layout>
  );
}

export default MyApp;

Custom App과 관련한 NextJS 공식문서를 살펴보면 조금 더 디테일하게 이 원리를 이해해볼 수 있다.

먼저 MyApp이 프롭스로 넘겨받고 있는 Component는 활성화된 페이지를 의미한다. 만일 현재 유저가 머무른 페이지가 Home이라면 위 코드에 작성된 Component 역시 Homepage가 되는 것이다. 이때 해당 컴포넌트로 내려준 Props는 페이지에서 전부 내려받을 수 있다.

pageProps의 경우 공식문서는 이를 "데이터 페칭 메소드에 의해 페이지에 미리 로드된 초기 props"라고 소개한다. 만일 초기에 설정해준 프롭이 없다면 빈 객체를 내려받게 된다.

06 스타일링하기

만일 직접 NextJS로 프로젝트를 진행해야 한다면, 스타일링 라이브러리를 쓰겠지만!
우선 기본적인 CSS를 어떻게 NextJS에서 다루는지 알아보도록 하자.

우선 위와 같이 styles 폴더가 구성된다.
각 파일 내부에는 기존 바닐라 자바스크립트, 혹은 리액트를 다룰 때와 동일하게 적용해줄 스타일에 해당되는 CSS 내용을 작성해준다.
이를 사용하는데 있어 약간의 차이가 있는데!

위와 같이 각 컴포넌트, 혹은 페이지에서 임포트해서 클래스 네임을 지정해준다!
각 컴포넌트/페이지 별 스타일 시트를 만들어준다고 생각하면 좋을 것 같다.
게다가 전체 페이지 기준으로 보았을 때 클래스명이 중복되는 것을 방지하기 위해서 실제 브라우저 콘솔에서 엘리먼트의 클래스네임을 확인하면, 스타일드컴포넌트를 사용했을 때와 같이 임의의 코드가 클래스네임 뒤에 더 붙어있는 것을 확인할 수 있다.

모쪼록 style을 적용해줄 때는 컴포넌트.module.css라는 이름으로 파일을 생성해주고 내부에 CSS파일을 작서앟ㄴ 후에 해당 파일을 컴포넌트에서 임포트해서 클래스네임을 변수로 지정해준다는 것만 기억하면 될 듯 싶다.

07~08 404페이지 커스텀하기 & 리다이렉트 시키기

pages폴더 내부에 404.js 파일을 만들어준다.
이때, 만일 자동으로 리다이렉트를 해주고 싶다면, useRouter를 활용하자!

import Link from "next/link";
import { useEffect } from "react";
import { useRouter } from "next/router";

const NotFount = () => {
  const router = useRouter();

  useEffect(() => {
    setTimeout(() => {
      // router.go(1);
      router.push("/");
    }, 3000);
  }, []);

  return (
    <div className="not-found">
      <h1>Ooooops...</h1>
      <h2>That page cannot be found.</h2>
      <p>
        Go back to the{" "}
        <Link href="/">
          <a>Homepage</a>
        </Link>
      </p>
    </div>
  );
};

export default NotFount;

routergo 메서드를 활용하면 넘겨준 인자(1/-1)에 따라, 앞으로 가기와 뒤로 가기가 구현되고, push 메서드를 활용하면 특정 페이지로 이동시킬 수 있다.

09 Image와 메타데이터!

정적데이터들을 활용할 때, 예를 들어 이미지 파일을 사용할 때는 NextJS가 제공하는 Image 태그를 활용하면 좋다. 자동으로 레이지 로딩을 적용해준다.

import Image from "next/image";

<Image src="/logo.png" width={128} height={77} />

위와 같이 적용해주면 된다!

또한, Meta 태그를 활용하면 메타데이터를 쉽게 다룰 수 있고 탭바에 출력되는 내용을 수정해줄 때도 유용하다.

import Head from "next/head";

      <Head>
        <title>Ninja List | About</title>
        <meta name="keywords" content="ninjas" />
      </Head>

위와 같이 활용할 수 있다! 페이지 이동시마다 타이틀이 변경된다.

10 데이터 페칭

NextJS에서는 useEffect와 같은 훅을 사용해 데이터를 페칭하지 않는다.
NextJS는 프리렌더링을 하며, 그렇기 때문에 데이터 페칭을 위해서 NextJS가 제공하는 특별한 함수를 사용하도록 한다.

getStaticProps를 사용할 것이다.
getStaticProps는 정적생성 방식으로 빌드 시에 데이터를 가져와 한 번 빌드되고 나면 정적으로 움직이지 않는다. 기본적으로 빌드시에 처음 데이터를 가져오는 것이라고 생각하면 좋다.
(getServerSideProps를 활용하면 페이지가 요청될 때마다 데이터가 재요청된다. 페이지를 이동할 때마다 새로 데이터를 불러오기 때문에 속도는 느려지지만 동적인 구성이 가능해진다.)

import styles from "../../styles/Ninjas.module.css";
import Link from "next/link";

export const getStaticProps = async () => {
  const res = await fetch("https://jsonplaceholder.typicode.com/users");
  const data = await res.json();

  return {
    props: { ninjas: data },
  };
};

const Ninjas = ({ ninjas }) => {
  return (
    <div>
      <h1>All Ninjas</h1>
      {ninjas.map((ninja) => (
        <Link href={"/ninjas/" + ninja.id} key={ninja.id}>
          <a className={styles.single}>
            <h3>{ninja.name}</h3>
          </a>
        </Link>
      ))}
    </div>
  );
};

export default Ninjas;

데이터 페칭과 관련한 더 자세한 부분은 공식문서를 참고하자!

11~13 동적 라우팅하기

페이지가 동적 경로를 갖고 있다면 우리는 아래와 같은 방법을 사용한다.
[id]와 같이 대괄호를 사용해 페이지를 구성해주도록 한다.
그리고 파일 내부에서는 getStaticPaths를 사용한다.

만일 우리가 동적 경로를 사용하는 페이지에서 getStaticPaths를 사용하면 명시해놓은 모든 경로를 NextJS가 정적으로 사전 렌더한다.

export const getStaticPaths = async () => {
  const res = await fetch("https://jsonplaceholder.typicode.com/users");
  const data = await res.json();

  const paths = data.map((ninja) => {
    return {
      params: { id: ninja.id.toString() },
    };
  });

  return {
    paths,
    fallback: false,
  };
};

export const getStaticProps = async (context) => {
  const id = context.params.id;
  const res = await fetch("https://jsonplaceholder.typicode.com/users/" + id);
  const data = await res.json();

  return {
    props: { ninja: data },
  };
};

const Details = ({ ninja }) => {
  return (
    <div>
      <h1>{ninja.name}</h1>
      <p>{ninja.email}</p>
      <p>{ninja.website}</p>
      <p>{ninja.address.city}</p>
    </div>
  );
};

export default Details;

실제로 이렇게 작성한 후에 npm run build를 통해 빌드해주고 나서,
.next 내부에서 빌드된 결과를 살펴보면!

이와 같이 확인할 수 있다!

궁금한 건...

이럴 경우 만약 미리 예측하지 못하는 검색어 같은 것들은 어떻게 프리렌더링을 하지?
라는 고민이 들긴하지만, 아마 그런 부분은 CSR로 구현하겠거니...
그리고 이런식으로 프리렌더링 해줘야 하는 html이 엄~~청 많은 경우도...

profile
부정확한 정보나 잘못된 정보는 댓글로 알려주시면 빠르게 수정토록 하겠습니다, 감사합니다!

0개의 댓글