2024: Next.js 14

정은경·2024년 3월 14일
0

👸 Front-End Queen

목록 보기
260/266

Next.js 14 vs. 이전 버전

  • next.js에는 2가지 라우터가 존재
    • /pages
    • /app => next.js 14에서 등장!
  • data fetching과 routing 하는 방식이 기존과는 다르게 변경됨

NextJs14 처음부터 세팅하기

  1. npm 셋업
npm init -y
  1. 리액트, next, 리액트돔 최신버전 설치
npm install react@latest next@latest react-dom@latest

react vs. react-dom

  • React는 UI와 다른 모든 것들을 구성하는 부분
  • React-Dom은 그것을 브라우저의 Document Object Model(DOM)에 렌더하는 역할

next dev라는 명령어를 실행하면

  • NextJS는 프레임워크
  • NextJS가 작성된 코드를 찾는다

    프레임워크는 작성된 코드를 호출한다.
    하지만 라이브러리는 사용자가 라이브러리 코드를 호출한다

  • NextJS는 사용자가 프레임워크를 호출하는 것이 아닌 프레임워크가 코드를 호출하는 프레임워크이기 때문
  • NextJS는 웹사이트를 빌드하기위해서첫번째 페이지를 app 폴더 안 page라는 파일을 찾으려고 한다
  • 만약 layout 파일이 없다면 nextjs는 layout 파일을 자동생성해준다

NextJS

  • routing, navigation, layout, client/server components
  • client/server component는 NextJS에 새로 나온 아주 멋진 개념!

routing

react-router

  • react-router의 작동방식은 url을 지정하고 Home이라는 컴포넌트 render를 요청하는 것
  • 사용자가 그 url에 접근하면 home이라는 컴포넌트를 render함
  • /movies/:id와 같이 동적 라우팅도 가능함
  • NextJS에서는 파일 시스템을 통해서 url을 표현

client component vs. server component

  • NextJS가 어떤 방식으로 우리의 애플리케이션을 render하는지 이해해야함
  • 여기서 rendering이란, next.js가 우리의 react component를 가져와서 브라우저가 이해할 수 있는 html로 변환하는 작업을 의미

Client Side Rendering (CSR)

  • react가 render되는 방식은 CSR
  • 브라우저의 자바스크립트 엔진이 rendering 작업을 함
  • rendering은 react code를 브라우저가 이해할 수 있는 html로 바꾸는 것
  • Client(브라우저)는 JavaScript를 로드하고, 그 후에 JavaScript가 UI를 빌드함
  • CSR 단점
    • CSR로 된 페이지는 해당 페이지를 실행하기 위해서는 브라우저에 자바스크립트가 활성화되어있어야 함
    • SEO 검색 엔진 최적화

Serverr Side Rendering (SSR)

  • Next.js를 사용하면 자동적(default)으로 SRR이 됨
  • 브라저는 자바스크립트가 로드될 때까지 기다릴 필요가 없음. 왜냐하면 이미 화면에 표시할 html을 갖고 있기 때문
  • Next.js는 서버, 즉 백엔드에서 application을 render함. 그걸 브라우저 request에 대한 response로 줌
  • next.js에 작성된 모든 컴포넌트(클라언트/서버 컴포넌트 모두), 페이지들은 우선 SRR이 됨

hydration

  • 사용자가 최초 HTML을 본 뒤에 어떤일이 발생하는지, React가 언제 활성화되는지
  • client에서 hydrate되는 컴포넌트는 오직 "use client" 지시어를 맨위에 갖고 있는 컴포넌트들 뿐!

use client

  • client에서 hydrate되는 컴포넌트는 오직 "use client" 지시어를 맨위에 갖고 있는 컴포넌트들 뿐!
  • "use client"라는 지시어는 "이 컴포넌트는 client에서 interactive해야 해!"라고 말하는 것
  • use client를 명시함으로써 use client가 명시되어 있는 자바스크립트 코드만 선택적으로 client에서 다운받음으로써 결국에는 페이지 로딩 속도가 빨라지는 효과!
  • 서버 컴포넌트 안에 클라이언트 컴포넌트를 가질 수 있음 (반대는 안됨)

layout

  • next.js는 먼저 layout 컴포넌트를 확인하고, url을 확인한다음 해당 컴포넌트를 확인함
  • layout은 중첩이 가능함

route group

  • app 폴더 바로 아래있던 page.tsx를 괄호로 감싸 폴더((home)) 아래로 이동
  • 폴더 이름에 괄호를 감싸면 url에 영향을 미치지 않음

Metadata

  • 꼭 내보내야하는 오브젝트를 '메타데이터'라고 부름
  • 메타 데이터는 layout.tsx와는 달리, 중첩되지 않고 병합됨에 주의!
  • page.tsx나 layout.tsx만 메타데이터 export할 수 있음!!
  • 컴포넌트에서는 metadata를 내보낼 수 없음
  • metadata는 서버 컴포넌트에서만 있을 수 있음
  • metadata에 대한 템플릿을 만들 수 있음

Dynamic Routes

Data fetching

Loading 컴포넌트 (loading.tsx)

  • NextJS는 브라우저에게 웹사이트의 일부분(layout과 loading 컴포넌트)을 천천히 주면서 아직 백엔드 API 응답이 오지않았다고 계속말하고 있음
  • 백엔드 API 응답이 와서 끝나면, 이 결과값이 있는 컴포넌트가 브라우저에게 전달되고, loading 컴포넌트가 교체됨


  • HomePage 컴포넌트가 async여야 하는 이유는 NextJS가 이 컴포넌트에서 await 해야하기 때문
  • 사용자가 우리 웹사이트에 도착하는 순간 NextJS는 즉시 사용자에게 로딩 상태를 줌
  • 하지만 하면서, NextJS는 기본적으로 우리의 페이지를 작은 HTML 부분(chunk)으로 나눠서 준비된 html 부분을 브라우저에게 줄 수 있음

Pararrel Request

import { Metadata } from "next";
import { API_URL } from "../../../(home)/page";

export const metadata: Metadata = {
  title: 'movie detail',
}

type Props = {
  params: {
    id: string;
  };
};

async function getMovieDetail(id:string) {
  console.log(`fetching movies: ${Date.now()}`)
  await new Promise((resolve)=>setTimeout(resolve, 5000))
  const response = await fetch(`${API_URL}/${id }`);
  return response.json();
}

async function getVideos(id:string) {
  console.log(`fetching videos: ${Date.now()}`)
  await new Promise((resolve)=>setTimeout(resolve, 5000));
  try {
    const response = await fetch(`${API_URL}/${id}/videoss`);
    if (!response.ok) {
      throw new Error('Network response was not ok');
    }
  
    return response.json();
  } catch(e) {
    return {result: null}
  }

} 

export default async function MovieDetail({ params }: Props) {
  console.log(`fetching start: ${Date.now()}`)
  // const movie = await getMovieDetail(params.id);
  // const videos = await getVideos(params.id);
  const [movie, videos] = await Promise.all([getMovieDetail(params.id), getVideos(params.id)]); 
  console.log(`fetching end: ${Date.now()}`)

  console.log(">> videos", videos)

  return (
    <div>
      <h1> {movie.title}</h1>
      <div>{JSON.stringify(movie)}</div>
    </div>
  );
}

suspense

import { Metadata } from "next";

import MovieVideos from "../../../../components/movie-videos";
import MovieInfo from "../../../../components/movie-info";
import { Suspense } from "react";

export const metadata: Metadata = {
  title: 'movie detail',
}

type Props = {
  params: {
    id: string;
  };
};

export default async function MovieDetail({ params }: Props) {
  return (
    <div>
      <Suspense fallback={<h6>loading movie info</h6>}>
        <MovieInfo id={params.id} />
      </Suspense>
      <Suspense fallback={<h6>loading movie videos</h6>}>
        <MovieVideos id={params.id} />
      </Suspense>
    </div>
  );
}

토이프로젝트

npx create-next-app@latest

Reference

profile
#의식의흐름 #순간순간 #생각의스냅샷

0개의 댓글