next.js

fe_sw·2022년 8월 26일
1

Nextjs

목록 보기
1/2
post-thumbnail

React에서 서버 사이드 렌더링을 지원해주는 프레임워크이다.

CSR 문제점

  • ttv 문제: 클라이언트 렌더링의 경우 모든 js 파일을 로드하고 사용자는 웹을 보게된다. 이때까지 사용자는 많은 시간을 대기해야 한다다.

  • seo 문제: 클라이언트 사이드의 경우 첫 랜더링시 빈html파일을 받아오므로 검색엔진이 데이터가 없는줄알고 나가버린다.

SSR 문제점

  • 서버부하: 페이지 이동시마다 서버에 데이터를 요청하므로 이는 곧 서버부하로 이어진다

  • ttI 문제: 서버에서 랜더링해서 브라우저로 보여지므로 TTV가 빠른반면 ,브라우저에서 js를 로드하는 시간이 있으므로 TTI가 느림

  • 사용자 경험: 페이지 이동마다 서버에 페이지를 받아오므로 새로고침이 발생하므로 인터렉션이 느리다

Next.js 작동 원리: SSR + CSR

csr,ssr 각각의 단점의 해결책으로 나온게 next.js이다

  • 서버에 페이지 접속을 요청하면 SSR 방식으로 HTML을 렌더링 해 온다(SSR).

  • 그리고 브라우저에서 js를 다운로드하고 리액트를 실행한다. (→ 그래서 초기 로딩 성능도 개선하고, 이미 콘텐츠가 다 포함된 상태라 SEO도 가능함)

  • 그리고 나서 사용자가 상호작용하면서 다른 페이지로 이동할 때는, SSR이 아닌 CSR 방식으로 브라우저에서 처리하는 것이다. (→ 그래서 CSR의 장점도 유지함)

설치,실행

// 설치
npx create-next-app
// 실행 
npm run dev

hot reloading

개발 중 저장되는 코드는 자동으로 새로고침된다.

File Based Routing

React에서는 라우터가 없어서 보통 react-router-dom을 사용해서 렌더링한다
하지만 Nest.js의 경우 별 다른 설정없이 Routing을 할 수 있다.
폴더 내부의 파일위치나 파일명에 따라서 routing을 할 수 있다.

global style

다른 컴포넌트에 정의한 경우 다른 클래스와 겹쳐 오류를 발생할 수 있음으로 _app에서만 허용된다.

// _app.tsx
import "./globals.css";

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

export default MyApp;

automatic,dynamic routing

pages 폴더에 있는 파일은 해당 파일 이름으로 라우팅된다. (pages/page1.tsx -> localhost:3000/page1)

public 폴더도 pages의 폴더와 동일하게 라우팅 할수있다. 그러나 모든 사람이 페이지에 접근할 수 있으므로 지양하는게 좋다.

import Link from "next/link"; //client side navigation
import { useRouter } from "next/router";
import styles from '../styles/Navbar.module.css' //css module

function Navba() {
  const router = useRouter(); //현재 위치한 location에 대한정보가 있음
  console.log(router)

  return <nav>
    <Link href="/"><a className={router.pathname==="/" ? styles.active:""}>home</a></Link>
    <Link href="about"><a className={router.pathname==="/about" ? styles.active:""}>about</a></Link>
  </nav>;
}

export default Navba;
import { useEffect } from "react";
import { useRouter } from "next/router";
import posts from "../posts.json";

export default () => {
  const router = useRouter();

  const post = posts[router.query.id as string];
  if (!post) return <p>noting</p>;

  useEffect(() => {
    router.prefetch("/test");
  }, []);

  return (
    <>
      <h1>{post.title}</h1>
      <h1>{post.content}</h1>
      <button onClick={() => router.push("test")}>go to Test</button>
    </>
  );
};
// 동적url
import { useRouter } from "next/router";

export default () => {
  const router = useRouter();

  return (
    <>
      <h1>post</h1>
      <p>postid: {router.query.id}</p>
    </>
  );
};
import Router from 'next/router';
const greeting = 'hello' 

// 해당 파일 경로와 보내고 싶은 props를 query형태로 보내준다.
const SendQuery = () => {
    Router.push({
      pathname: '/register',
      query: { greetingK: '안녕', greetingE: greeting },
    });
  };
  
  
<button onClick={SendQuery}>클릭</button>
  
  
import { useRouter } from 'next/router';
  
// router를 이용해 data를 query를 통해 받아온다
const router = useRouter();
console.log('K : ', router.query.greetingK);
console.log('E : ', router.query.greetingE);

server rendering

클라이언트 렌더링과 다르게 서버에서 html을 rendering한다.

typescript

타입스크립트 활용을 위해 웹팩을 만지거나 바벨을 만질 필요 없다. 타입스크립트를 설치하고
(npm i typescript @types/node @types/react), (npm run dev)만 하면 자동으로 tsconfig, next-end.d.ts가 생성되어 타입스크립트로 코딩이 가능해진다.

보통 페이지간 이동은 a 태그를 사용하나 next에서는 사용하지 않는다.

a 태그를 사용하면 처음 페이지에 진입시 번들 파일을 받고, a 태그에 의해 라우팅 되면 다시 번들 파일을 받기 때문이다. 또한 redux을 쓰시는 경우 라면 store의 state 값이 증발되는 현상도 일어난다. 그렇기 때문에 a 태그는 전혀 다른 사이트로 페이지를 이동시켜 다시 돌아오지 않는 경우만 사용하고, 그 이외에는 모두 Link 태그를 사용한다다.

import Link from "next/link";

const Index = () => (
  <div>
    <Link href="/blog">
      <a>Blog</a>
    </Link>
    // 동적 link시 [] 사용
    <Link href="/blog/[address]">
      <a>Blog</a>
    </Link>
  </div>
);

dynamic component import

dynamic component import는 react에서 lazy하게 component를 import 해오는 방식과 유사하다.

dynamic하게 처음 보여줘도 안되는 컴포넌트는 import 하지 않게 됨으로, 초기 화면 렌더링 속도를 상승시키기 위해 사용한다.(맨처음 보이는 컴포넌트라면 dynamic 메소드를 사용할 이유가 없음)

 import React, { useState } from "react";
import dynamic from "next/dynamic";

const DynamicComponent = dynamic<{ nowTab: number }>(() =>
  import("./DynamicComponent")
);

const Index = () => {
  const [nowTab, setNowTab] = useState(0);

  return (
    <>
      {nowTab === 0 && <div>0 tab</div>}
      {nowTab === 1 && <DynamicComponent nowTab={nowTab} />}
    </>
  );
};

single file components (css module)

port Link from "next/link"; //client side navigation
import { useRouter } from "next/router";
import styles from '../styles/Navbar.module.css' //css module

function Navba() {
  const router = useRouter(); //현재 위치한 location에 대한정보가 있음
  console.log(router)

  return <nav>
    <Link href="/"><a className={`${styles.link} ${router.pathname==="/" ? styles.active:""}`}>home</a></Link>
    <Link href="about"><a className={ `${styles.link}  ${router.pathname==="/about" ? styles.active:""}`}>about</a></Link>
  </nav>;
}

export default Navba;

// css module -> 브라우저에서 코드를열면  <nav className={styles.nav}> 
// 이부분 className은 무작위로 선정됨 따라서 다른 컴포넌트에서 똑같은 클래스명을 지어도 충돌할 걱정이없음
// 클래스를 여러개쓸거면 ``사용
// 1개는 : className={router.pathname==="/" ? styles.active:""}
  
.active{
    color: tomato;
}
.link{
    text-decoration: none;
}

prefetching

백그라운드에서 페이지를 미리 가져온다. 기본값은 true. 뷰포트에있는 모든 항목 (초기 또는 스크롤을 통해)이 미리 로드된다. 정적 생성 을 사용하는 JSON페이지는 더 빠른 페이지 전환을 위해 데이터가 포함 된 파일을 미리로드 한다.

이건 Link 컴포넌트를 사용해서 이뤄지는데, 링크 컴포넌트를 렌더링할때 형식으로 prefetch 값을 전달해주면 데이터를 먼저 불러온다음에 라우팅을 시작한다. 프로덕션 레벨에서만 이루어진다.

custom 태그로 head 태그 옮기기

우리는 페이지의 header로 다음과 같은 정보를 추가할 수 있습니다.

  • 페이지 제목을 커스텀하고 싶을때
  • meta 태그를 변경하고 싶을때
import Head from "next/head";

export default () => (
  <div>
    <Head>
      <title>새로 만들어진 타이틀 입니다</title>
    </Head>
    <div>...</div>
  </div>
);

next/head로 부터 Head 컴포넌트를 받아 모든 컴포넌트에서 사용할 수 있다.
next.js가 해당 컴포넌트가 mount 할때, Head내 태그들을 페이지의 HTML의 Head에 포함 시킨다. 마찬가지로 unMount 할때, 해당 태그를 제거한다.

_app.js

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

export default MyApp;
  • 이곳에서 렌더링 하는 값은 무조건 모든 페이지에 영향을 준다.
  • 최초로 실행되는 것은 _app.js
  • 내부에 컴포넌트가 있다면 전부 실행하고 html의 body로 구성됨
  • 여기서 props로 받은 Component는 요청한 페이지이다. Component 에는 /pages/index.js 파일이 props로 내려오게 된다.
  • pageProps는 페이지 getInitialProps를 통해 내려 받은 props들을 말한다(미리가져온 초기객체)
  • 그 다음 _document.js가 실행됨
  • _app.js에서 consle.log 실행시 client, server둘다 콘솔 찍힘
  • global css를 여기서 선언한다.
  • componentDIdCatch 이용해 커스텀 에러 핸들링이 가능하다.
  • 페이지 전환시 상태값을 유지할 수 있다.

_document.js

meta 태그를 정의하거나, 전체 페이지에 관려하는 컴포넌트이다.

// pages/_document.tsx
import Document, { Html, Head, Main, NextScript } from "next/document";
export default class CustomDocument extends Document {
  render() {
    return (
      <Html>
        <Head>
          // 모든페이지에 아래 메타테크가 head에 들어감, 루트파일이기에 가능한 적은 코드만 넣어야함 
          <meta property="custom" content="123123" />
        </Head>
        <body>
          <Main />
        </body>
        <NextScript />
      </Html>
    );
  }
}
  • 이곳에서 console은 서버에서만 보이고 클라이언트에서는 안보인다.
  • render 요소는 방영하지만 페이지 구성요소만 반영되고 js는 반영안한다 즉, componentDidMount 같은 훅도 실행이 안된다다. 정말 static한 상황만 부여된다.
  • Next.js 페이지들은 마크업 정의를 건너띄기 떄문에 html,head,body태그를 사용할때는 이페이지를 필수적으로 만들어야 된다.

_error.jsx

라우팅 경로를 설정하지 않더라도 에러가 발생한다면 에러 페이지로 넘어가게 된다.
추가적으로 에러 상황에 따라서 500, 404 등도 추가할 수 있다

  /pages/404.js

import Header from 'header.js';
import Footer from 'footer.js';

export default function Custom404() {
  return (
    <>
      <Header />
      <h1>Custom 404 Error Page</h1>
      <Footer />
    </>
  )
}

server side 생명주기

  1. nextJs 서버가 GET 요청을 받는다.
  2. GET 요청에 맞는 pages/Component를 찾는다.
  3. _app.tsx의 getInitialProps가 있다면 실행한다.
  4. route에 맞는 페이지의 Component의 getInitialProps가 있다면 실행한다. pageProps들을 받아온다.
  5. _document.tsx의 getInitialProps가 있다면 실행한다. pageProps들을 받아온다.
  6. 모든 props들을 구성하고, _app.tsx -> page Component 순서로 렌더링
  7. 모든 Content를 구성하고 _document.tsx를 실행하여 html 형태로 출력한다.

context objects

  • pathname - 현재 pathname (/user?type=normal-> /user)
  • query - 현재 query를 객체로 (http://localhost:3000/blog/test -> {id: 'test'}, /post?type=secret -> {type: 'secret'})
  • asPath - 전체 path (http://localhost:3000/blog/test -> /blog/[id], /blog/test)
  • req - HTTP request object (server only)
  • res - HTTP response object (server only)
  • err - Error object if any error is encountered during the rendering


참고

0개의 댓글