뚱이는 Next.js를 만나고 마인부우가 되었다

임철종·2023년 4월 13일
1
post-thumbnail

Framework Overview

Next.js 공식 문서에서는 Next.js를 이렇게 표현하고 있다.

The React Framework for Production(생산을 위한 리액트 프레임워크)

Library vs Framework


라이브러리와 프레임워크의 주요 차이점은 ‘Inversion of control’(통제의 역전)이다.
library는 메서드를 호출하여 사용자가 제어할 수 있고
framework는 제어가 역전되어 프레임워크가 사용자를 호출한다.

library - 사용자가 파일 이름이나 구조를 정하고 결정을 내린다.
framework - 파일 이름이나 구조를 정해진 규칙에 따라 만든다.

React의 한계


  • 상품으로 배포하기에 기능적으로 완벽한 어플리케이션을 만들기가 쉽지 않다.
  • React는 유저 인터페이스를 만들기 위한 라이브러리이다.
  • 라우팅을 제공하지 않는다.
  • 데이터를 가져올 수 있는 솔루션을 지원하지 않는다.
  • Client side rendering을 하기 때문에 SEO에 좋지 않다.
  • 첫 페이지를 보는데 오래 걸린다. (Slow TTV/FCP)
    TTV = Time To View
    FCP = First Content Paint

그래서 나온 Frameworks

  1. Next.js
  2. Gatsby
  3. Remix
    ...

Next.js?


  • React를 사용하여 유저 인터페이스를 만들기 위한 패키지이다.
  • 라우팅, 스타일링, 인증, 번들 최적화 등 본격적인 상품이 될 어플리케이션을 만들기에 필요한 많은 기능들이 들어가 있다.
  • Next.js가 모든 것을 제공하므로 추가적인 패키지를 다운할 필요가 없다.
  • 기능을 구현하기 위해 따라야 할 규칙이 정해져 있다.

1. File based routing

React에서는 라우팅을 위한 패키지를 설치해야하고 라우트를 추가할 때 마다 코드를 작성해야한다.
Next.js에서는 파일명과 위치에 따라 라우팅이 된다.

Page based routing mechanism

페이지는 파일 경로와 이름에 따라 연결된다.

Nested routes

중첩된 폴더 구조의 파일은 URL에서 동일한 방식으로 라우팅된다.

Dynamic routes

페이지 이름(파일 이름)을 대괄호로 감싸주면 만들 수 있다.

Catch all routes

대괄호 안에 점 3개를 추가하여 만든다.
동일한 페이지 레이아웃에 대해 서로 다른 URL을 원하는 경우나 일부 경로 매개변수가 선택적인 페이지를 작업할 때 유용하다.

export default function detail() {
  return 'detail page';
}

예외

index.js → home page로 연결된다.
react.js를 import 할 필요가 없이 jsx를 사용할 수 있다.
잘못된 경로를 입력하면 만들어져 있는 404페이지가 나온다. (커스텀 가능)

2. Pre-rendering

Client side의 javascript에서 모든 작업을 수행하지 않고 각 페이지의 html을 미리 생성한다.

create-react-app

  • CRA는 브라우저, 즉 client side의 자바스크립트가 모든 것을 렌더링하므로 인터넷이 느린 경우나 javascript가 비활성 된 경우 흰 화면만(실제 작성된 태그인 root div) 보이는 문제가 발생한다.
  • create-react-app으로 만들어진 앱의 페이지 소스코드를 보면 <div id=root></div> 만 존재하는 것을 볼 수 있다.

create-next-app

  • Next.js에서는 실제 작성된 태그가 pre-render 되기 때문에 느린 인터넷 환경이나 js가 비활성 된 경우에도 HTML 태그가 존재하기 때문에 api로 받아오는 정보 이외의 페이지는 볼 수 있다.
  • create-next-app으로 만들어진 앱의 페이지 소스코드를 보면 화면에 존재하는 모든 태그와 내용이 존재하는 것을 볼 수 있다.

💡 create react app (CRA)과 next.js의 차이점은 렌더링 방식에 있다.

Pre-rendering은 더 나은 퍼포먼스를 보여준다.

  • 리액트 앱에서는 자바스크립트가 실행되는 것을 기다려야 한다.
  • 외부 API에서 데이터를 가져온 다음 UI를 렌더링한다.
  • 이때 유저에게 기다리는 시간이 발생한다.
  • pre-rendered 페이지에서는 HTML이 이미 생성되어 있고 더 빠르게 로딩된다.

Pre-rendering은 SEO에 유리하다.

SEO는 언제나 옳다.

  • 온라인 쇼핑몰이나 블로그를 만들 경우 SEO가 중요하다.
  • 리액트 앱에서는 검색 엔진이 div 태그밖에 볼 수없다.
  • 검색 엔진이 pre-rendered 페이지를 보게 되면, 모든 내용이 소스코드에 들어있기 때문에 페이지를 찾는데 유리하다.
  • SEO가 중요한 앱을 만든다면, pre-rendering이 필요하다.

Next.js의 Pre-rendering

Next.js 는 두 형태의 pre-rendering을 지원한다.


Static Generation

  • 빌드 시 HTML 페이지가 생성되는 위치를 미리 렌더링하는 방법
  • 웹 페이지의 내용을 구성하는 모든 데이터가 포함된 HTML은 앱을 빌드할 때 미리 생성된다.
  • 가능할 때마다 페이지를 미리 렌더링 하는 것이 권장된다.
  • 페이지를 한 번 만들고 CDN을 통해 캐시하여 고객에게 즉시 제공할 수 있다.

예) 블로그 페이지, 전자상거래 제품 페이지, 문서 및 마케팅 페이지

Next JS는 기본적으로 모든 페이지를 pre-render 한다.
페이지에 모든 정적 HTML 태그가 자동적으로 생성된다.

Static Generation with Data

  • Without Data
    외부 데이터를 가져올 필요가 없기 때문에 각 페이지들이 빌드 과정에서 HTML을 생성한다.
  • With Data
    빌드 과정에서 외부 데이터를 가져온 뒤에 HTML을 생성한다.

Hydration

Next.js Server에서는 Pre-rendering된 웹 페이지를 클라이언트에게 보내고 나서, 바로 리액트가 번들링 된 자바스크립트 코드들을 클라이언트에게 전송한다.

네트워크 탭을 보면, 맨 처음 응답받는 요소가 document Type의 파일이고, 이후에 React 코드들이 렌더링 된 JS 파일들이 Chunk 단위로 다운로드되는 것을 확인할 수 있다.

그리고 이 자바스크립트 코드들이 이전에 보내진 HTML DOM 요소 위에서 한번 더 렌더링을 하면서, 각자 자기 자리를 찾아가며 매칭이 된다.

이 과정을 Hydrate라고 부른다.

이것은 마치 자바스크립트 코드들이 DOM 요소 위에 물을 채우 듯 필요로 하던 요소들을 채운다 하여 Hydrate(수화)라는 용어를 쓴다고 한다.

next.js는 react.js를 백엔드에서 동작하여 페이지를 미리 생성하고 컴포넌트들을 렌더링한다.
렌더링이 끝나면 HTML이 되고 초기 상태를 페이지의 소스코드에 넣어준다.
그렇게 되면 유저는 js와 react.js가 로딩되지 않더라도 콘텐츠를 볼 수 있다.
react.js가 로딩되면 이미 존재하는 HTML과 연결되어 일반적인 react.js 앱이 된다.

💡 HTML이 존재하기 때문에 SEO에 도움이 된다!

Server-side Rendering

Next.js는 빌드 타임에 pre-render 하는 것 뿐만 아니라 요청 타임에도 pre-rendering이 가능하다.
HTML은 들어오는 요청을 위해 생성된다.

  • SSR은 요청 타임에 HTML을 생성하는 pre-rendering 방식
  • SSR은 요청마다 데이터를 가져올 경우나 개인화된 데이터를 SEO 친화적으로 가져올 때 필요하다.

getServerSideProps

  • getServerSideProps 는 서버 측에서만 작동한다.
  • 함수는 클라이언트 측에서 실행되지 않는다.
  • getServerSideProps 내부에 작성한 코드는 JS 번들에 포함되지 않기 때문에 브라우저로 보내지지 않는다.
  • getServerSideProps 내부에 server 측 코드를 작성할 수 있다.
  • getServerSideProps 내부에서 파일 시스템에 접근하거나 데이터베이스 쿼리를 할 수 있다.
  • getServerSideProps 내부에서 API 키를 사용해도 브라우저에 노출되지 않는다.
  • getServerSideProps 는 페이지와 일반 컴포넌트 파일에서만 사용할 수 있다.
  • pre-rendering을 위해 사용하고 클라이언트 측에서 데이터를 가져올 때는 사용하지 않는다.
  • getServerSideProps 는 props 키를 가진 객체를 반환해야 한다.
  • getServerSideProps 요청 타임에 실행된다.

3. API routes

API를 만들 수 있다.
Full stack framework로 볼 수 있다.

API Folder

  • Next.js는 Full-stack 프레임워크이다.
  • React에서 FE 코드를 작성할 수 있으며, 호출할 API를 FE 코드에서 작성할 수 있다.
  • Next.js 어플리케이션은 폴더 구조를 사용하여 RESTful한 API 엔드포인트를 생성할 수 있게 한다.
  • pages 폴더에 api 이름의 폴더를 생성해야한다.
  • api 폴더에는 어플리케이션의 api를 정의할 수 있다.
  • 커스텀 서버 코드나 API 라우트를 위한 설정 없이 비즈니스 로직을 작성할 수 있다.
  • handler 라는 이름으로 생성하여 사용한다.
  • Next.js는 React+Node 어플리케이션에 필요한 모든 것을 제공한다.
export default function handler(req, res) {
  res.status(200).json({ name: 'Home API route' });
}

Dynamic API routes


Dynamic Routes 와 동일하게 폴더 안에 대괄호([])로 파일을 만들어주면 해당 파일 이름으로 접근 할 수 있다.

import { comments } from '../../../data/comments';
export default function handler(req, res) {
  const { commentId } = req.query;
  const comment = comments.find(
    (comment) => comment.id === parseInt(commentId)
  );
  res.status(200).json(comment);
}

Catch all routes

Catch-all 또한 이전에 사용했던 폴더 구조와 파일명([...params].js)을 api 폴더에서 사용하면 된다.

export default function handler(req, res) {
  const params = req.query.params;
  console.log(params);
  res.status(200).json(params);
}

Summary

  • API 라우팅은 페이지 라우팅과 방식이 흡사하다.

  • API들은 파일 구조를 따른다.

  • 모든 API 라우트는 handler 함수로 export 된다.

  • handler 함수는 인자로 requestresponse를 받는다.

  • GET, POST 등 요청을 req.method 를 사용하여 수용한다.

  • 동적 API 라우트는 파일 구조로 생성한다. [파일명]

  • Catch All API 라우트는 파일 구조로 생성한다. […params]

4. Support for CSS modules

Tailwind CSS 같은 css framework를 사용할 수 있다.
Styled-component 같은 css in js library를 사용할 수 있다.
기본적으로 아주 좋은 CSS module을 지원한다.

css module pattern

{name}.module.css 형식으로 만든 css 파일을 자바스크립트 오브젝트로 import 하여 스타일을 적용하는 방식이다.

.nav {
		background-color: red;
}
import Link from 'next/link';
import { useRouter } from 'next/router';
import styles from './NavBar.module.css';

export default function NavBar() {
  const router = useRouter();
  return (
    <nav className={styles.nav}>
      <Link href="/">
        <a style={{ color: router.pathname === '/' ? 'red' : 'blue' }}>Home</a>
      </Link>
      <Link href="/about">
        <a style={{ color: router.pathname === '/about' ? 'red' : 'blue' }}>
          About
        </a>
      </Link>
    </nav>
  );
}

장단점


장점

  • css module 패턴으로 스타일을 적용한다면 실제 페이지에서는 무작위 텍스트로 class 명을 만들어주므로 각 컴포넌트의 클래스가 충돌하지 않게 된다.

단점

  • css 파일을 따로 가져야한다.
  • 클래스 이름을 기억해야하고 클래스명이 두개 이상 들어가는 경우 조건을 작성하는 것이 복잡하다.

Styled JSX


styled jsx 방식으로 스타일을 적용하기 위해서는 style 태그에 jsx 속성을 추가하고 그 안에 CSS를 작성하면 된다.

<style jsx>{`
        nav {
          background-color: tomato;
        }
        a {
          text-decoration: none;
        }
        .active {
          color: red;
        }
`}</style>

이렇게 작성하면 실제 클래스명은 무작위 텍스트로 입력되고 그 클래스는 해당 style 태그가 작성된 컴포넌트에만 적용된다.

같은 이름을 사용할지라도 해당 컴포넌트에만 격리되어 적용된다.

컴포넌트의 Prop 가져오기

<style jsx>{`
        nav {
          background-color: tomato;
        }
        a {
          text-decoration: none;
        }
        .active {
          color: ${props.color};
        }
`}</style>

단순히 js 문자열이므로 ${} 를 사용하여 객체를 가져올 수 있다.

Custom App


Next.js는 App 컴포넌트를 사용하여 페이지를 초기화한다.

  1. 페이지 변경간의 레이아웃 유지
  2. 페이지 탐색 시 state 유지
  3. componentDidCatch 를 사용한 Custom error 처리
  4. 페이지에 추가 데이터 삽입
  5. Global CSS 적용

_app.js 파일

pages 폴더에 _app.js 파일을 만들고 App 컴포넌트를 생성한다.

import { AppProps } from 'next/app';
export default function App({ Component, pageProps }: AppProps) {
  return (
      <Component {...pageProps} />
  );
}

Next.js는 컴포넌트(Component)와 속성(pageProps)을 가져와서 렌더링한다.

그렇기 때문에 렌더링 할 페이지 이외의 컴포넌트를 작성하면 모든 페이지에 그 컴포넌트가 렌더링된다.

import { AppProps } from 'next/app';
import NavBar from '../components/NavBar';

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

이렇게 NavBar 컴포넌트를 포함하면 모든 페이지에 같이 렌더링된다.

같은 방법으로 Global style을 적용할 수 있다.

import { AppProps } from 'next/app';
import NavBar from '../components/NavBar';
import '../styles/globals.css';

export default function App({ Component, pageProps }: AppProps) {
  return (
    <>
      <NavBar />
      <Component {...pageProps} />
      <style jsx global>
        {`
          a {
            color: green;
          }
        `}
      </style>
    </>
  );
}

5. Authentication

여러 케이스에 맞는 다중 인증을 지원한다.

6. Dev and Prod build system

Dev 와 Prod 빌드를 따로 할 수 있다.

profile
🌑🌘🌗🌖🌕

0개의 댓글