[Next.js] 암 온더 넥스트 레벨 📈

hyeonbin·2024년 1월 24일

FE-log

목록 보기
9/9
post-thumbnail

📃 Next.js

⚙️ 기본 개념

이번 글은 Next.js를 처음 접할 때 반드시 알아야 할 개념들을 필두로 작성한 글이다!

React 개념을 아는 상태에서 Next.js를 접하면 더 쉽게 접근할 수 있지만, 몰라도 이해하기 쉽게 글을 작성해 볼 것이다 😁😗

이제 Next.js 특징을 알아보자!

1. React 프레임워크

  • Next.js는 React.js 라이브러리의 프레임워크다.

2. SSR (Server Side Rendering)

  • 페이지를 서버에서 렌더링해 초기 로딩 속도를 향상시키고 SEO(검색 엔진 최적화)를 개선한다.
  • 클라이언트에서 자바스크립트를 실행하기 전에 서버에서 완전히 렌더링된 HTML을 제공하므로 사용자 경험이 향상된다.

3. SSG (Static Site Generation)

  • 빌드 시점에 페이지를 미리 렌더링하여 정적인 HTML 파일을 생성하며, 캐싱을 통해 빠른 페이지 로딩을 제공한다.

4. 자동 코드 분할 (Code Splitting)

  • 페이지 단위로 코드를 분할해 필요한 부분만 로드한다. 이를 통해 초기 로딩 속도 개선하고, 불필요한 코드 로드를 방지한다.

5. 파일 기반 라우팅

  • 파일 시스템을 기반으로 라우트를 자동으로 생성한다.

6. API 라우트

  • 서버리스 함수(Serverless Functions)를 사용해 API 엔드포인트를 쉽게 만들 수 있다. 이를 통해 서버 없이 백엔드 기능을 구현할 수 있다.

7. 이미지 최적화 (Image Optimization)

  • Next.js는 빌트인 이미지 최적화 기능을 제공해 이미지를 자동으로 최적화하고, 다양한 화면 크기에 맞게 적절한 크기로 조정한다.

등등 여러 특징들이 있는데, 조금 더 깊게 들어가 보자!



⚙️ CSR vs SSR

React와 같이 JavaScript 기반 애플리케이션은 JavaScript로 동작하기 때문에 JS 파일을 다운로드한 후에 브라우저에서 자바스크립트가 실행되어야 화면에 UI가 표시된다.

이러한 방식을 바로 클라이언트 쪽에서 렌더링하는 CSR = Client Side Rendering이다.

자바스크립트가 동작하지 않는 환경에서는 화면이 표시되지 않기 때문에 검색엔진과 같은 로봇들이 컨텐츠를 이용할 수 없다는 치명적인 단점이 있다.

그러나, SSR = Server Side Rendering은 서버쪽에서 자바스크립트가 실행된다.

브라우저한테는 완성된 html을 전송하기 때문에 자바스크립트를 실행할 수 없는 환경에서도 잘 동작한다. 이는 검색엔진에 친화적이고, 다운로드 받는 즉시 실행되기 때문에 사용자 입장에서도 금방 웹페이지가 나타나는 느낌을 받는다.

그러나, SSR은 매 요청마다 서버에서 페이지를 렌더링하기 때문에 페이지 렌더링 작업이 무거운 경우(예를 들어 실시간 데이터 등을 제공하는 페이지) 서버 부하를 증가시킨다.

따라서 SEO와 성능 향상만을 위해 React가 아닌 Next.js를 선택하는 것보다 React에서도 SEO, 성능 향상을 할 수 있는 방법이 많기 때문에 적절한 판단으로 상황에 맞게 사용해야 할 것 같다.



⚙️ 패키지 설치

Node.js가 설치되어 있어야 하며, 아래의 명령어 중에 해당하는 명령어로 Next.js를 사용할 개발 환경을 설치한다!

# 폴더 함께 생성
npx create-next-app 폴더명

# 현재 폴더
npx create-next-app@latest . 

# 현재 폴더에 타입스크립트 바로 추가
npx create-next-app@latest . --ts

명령어를 입력하면 아래와 같이 질문들이 나오는데, 자신의 프로젝트 환경에 맞게 No / Yes 중에 고르면 된다.


App Router를 선택했기 때문에 그 기준으로 폴더 구조가 생성된다!



⚙️ Page Router vs App Router

Next.js의 App Router는 기존 Page Router에 더불어 더욱 강력하고 유연한 라우팅 기능을 제공한다!

1. Page Router

/pages
  /_app.js        # 모든 페이지에 적용되는 전역 설정 및 레이아웃
  /_document.js   # HTML 문서 구조 커스터마이징
  /index.js       # 홈 페이지: /
  /create
    /index.js     # 생성 페이지: /create
  • pages 폴더 내의 별도의 폴더나 파일로 정의된다.
  • 파일 이름 기반 라우팅 - 페이지 파일 이름은 URL 경로와 일치해 자동으로 경로를 설정한다.
  • 데이터 패칭 함수 - getStaticProps , getServerSideProps 등의 데이터 페칭을 위한 서버 측 함수가 제공된다.

2. App Router

/app
  /layout.js     # 모든 페이지에 적용되는 레이아웃
  /page.js       # 홈 페이지: /
  /create
    /layout.js   # /create 페이지에만 적용되는 레이아웃
    /page.js     # 생성 페이지: /create
  • React18을 사용하며, 기본이 서버 컴포넌트다.
  • app 폴더에 자유로운 폴더 구조를 사용해 코드를 구성한다.
  • 파일 이름 기반 라우팅 확장 - 패턴 기반 라우팅을 지원해 URL 패턴에 따라 라우팅을 정의한다.
  • 서버 측 데이터 패칭을 위한 data 함수가 제공되며, 클라이언트 측에서도 사용 가능하지만, useEffect 같은 리액트 훅을 이용한다.
  • 공통 레이아웃 및 맞춤 데이터 로직을 위한 기능이 추가됐다.

이제부터는 Next.js 13에서 도입된 새로운 라우팅 시스템 App Router를 기준으로 좀 더 알아보겠다!


✔️ layout.js -> page.js

app 폴더 안의 layout.js, pages.js는 다음과 같은 역할을 한다.

1. layout.js
모든 페이지에 공통적으로 적용될 레이아웃을 정의한다.
예를 들어 header, footer, navBar 등 반복적으로 사용되는 UI 요소를 포함한다.
layout.js는 페이지 컴포넌트를 감싸며 page.js는 레이아웃이 제공하는 공간에 위치한다.

import Link from 'next/link';
import './globals.css';

export const metadata = {
  title: '웹 테스트',
  description: 'Generated by hyeonbinnn',
};

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <h1>
          <Link href="/"></Link>
        </h1>
        {children}
        <ul>
          <li>
            <Link href="/create">생성</Link>
          </li>
        </ul>
      </body>
    </html>
  );
}

2. page.js
페이지별 컴포넌트를 구현하며 특정 페이지에 표시될 컨텐츠와 기능을 담는다.
page.js 파일의 함수 이름은 해당 페이지의 URL 경로와 일치해야 한다.

export default function Home() {
  return (
    <>
      <h2>환영합니다!</h2>
      기능
      <img src="/hi.png" />
    </>
  );
}


✔️ 경로 설정 & 상위 레이아웃 적용

아래와 같이 app 폴더 안에 하위 폴더를 만들고, 각 폴더 안에 page.js, layout.js 파일을 만들면 리액트의 라우팅과 유사한 방식으로 페이지를 작동시킬 수 있다.

app/create/page.js => /create


다시 말해, /create 경로로 접속을 하고 싶다면 Next.js는 app 폴더 밑에서 create 폴더가 있는지 찾고, 이 폴더가 page.js를 가지고 있는지 찾는다!

가지고 있으면, pages.js의 내용을 create 폴더 안의 layout.js와 결합하는데, 이때 layout.js의 함수는 props를 반드시 받아야 한다.

그리고 {props.children}을 통해 해당 경로에 매핑되는 page.js 파일의 컴포넌트를 렌더링한다.

// create 폴더의 layout.js
const Layout = (props) => {
  return (
    <form>
      <h2>생성</h2>
      {props.children} // create 폴더의 page.js
    </form>
  );
};

export default Layout;

// create 폴더의 page.js
const Create = () => {
  return <>Create</>;
};

export default Create;

그 다음, 상위 폴더의 layout.js를 찾아보고 존재하면, 지금까지 만들어진 컨텐츠를 상위 layout.js의 {children}에 포함시킨다.

아래의 그림을 통해 레이아웃 구조를 한번 더 이해해 보자!


✔️ 다이나믹 라우팅

다이나믹 라우팅은 URL 경로에 동적인 값을 포함하는 라우팅 방법이다.

위 예시를 이어받아 /create/1 또는 /create/2 등 동적인 경로를 처리하기 위해, 세그먼트에 id라는 이름을 부여하고 create 폴더 안에 [id] 폴더를 만든다.

그리고 page.js 파일을 추가하면 /create/[id]/page.js 같이 경로를 사용할 수 있다.

id라고 칭한 값을 컨텐츠로 가져오는 방법은 URL에 전달되는 파라미터를 컴포넌트에서 사용하기 위해 Create 컴포넌트에 props를 주고 props 밑에 params.id를 통해 [id]에 해당하는 값을 가져올 수 있다.

// create 폴더의 [id] 폴더의 layout.js
const Layout = (props) => {
  return (
    <form>
      <h2>생성</h2>
      {props.children} // create 폴더의 [id] 폴더의 page.js
    </form>
  );
};

export default Layout;

// create 폴더의 [id] 폴더의 page.js
const Create = (props) => {
  return <>Create : {props.params.id}</>;
};

export default Create;

또는 create 폴더를 만들고 page.js 대신 [id].js 파일을 추가하여 /create/[id].js 경로를 사용하는 방법도 있다.

이는 다이나믹 라우팅을 처리하는 방식은 동일하지만, 파일 위치가 다를 뿐이다!


✔️ 리다이렉션

리다이렉션은 사용자가 요청한 URL을 다른 URL로 안내하는 것을 의미한다.

웹 브라우저에서는 사용자가 특정 주소를 입력하거나, 링크를 클릭하면 서버는 해당 주소에 대한 콘텐츠를 제공한다. 하지만 특정 상황에서 서버는 사용자를 다른 주소로 보내야 할 수도 있다.

이때 발생하는 것이 바로 리다이렉션이다.

App Router에서는 이전 버전의 next/router 대신 next/navigation을 사용한다.
이를 통해 현재 페이지의 라우터 객체에 대한 접근 권한을 가진다.

router.push 함수를 통해 현재 페이지에서 원하는 페이지 경로로 리다이렉션한다.

import { useRouter } from 'next/navigation';

const Create = () => {
  const router = useRouter();
    
  if (someCondition) {
    router.push('/new-page'); // 새로운 페이지로 이동
    router.replace('/new-page'); // 현재 페이지를 새로운 페이지로 교체
    router.redirect('/new-page'); // 서버 측 리다이렉션 수행
  }
  
  return <>
    <h1>create</h1>
  </>;
};

export default Create;


⚙️ Sercer vs Client 컴포넌트

Next.js는 서버 컴포넌트와 클라이언트 컴포넌트를 구분해서 사용할 수 있다.

기본적으로 Next.js 13에서는 페이지 컴포넌트와 레이아웃 컴포넌트를 use server 서버 컴포넌트로 간주한다. 그러나, 클라이언트 사이드에서만 동작하는 기능이나 특정 컴포넌트를 클라이언트 컴포넌트로 만들고 싶을 때는 use client를 사용하면 된다.

예를 들어, 사용자와 상호작용하는 UI, 이벤트 핸들링, 브라우저 전용 API 사용, 상태 관리 및 수명 주기 효과 사용 등이 있다.

그래서 use client를 파일 최상단에 입력하면 하위 구성 요소를 포함해 해당 파일로 가져온 다른 모듈이 클라이언트 번들의 일부로 간주된다. 즉, 모든 파일에서 선언하지 않아도, 진입점에서 한 번 정의해주면 된다.

'use client';

const BrowserInfo = () => {
  return (
    <div>
      <p>User Agent: {navigator.userAgent}</p>
    </div>
  );
}

export default BrowserInfo;

반대로, use client를 사용하지 못하는 경우는 use server 서버 컴포넌트인 경우다.

이유는 간단하다. 서버 컴포넌트는 서버 사이드에서 렌더링되기 때문에 서버 사이드에서 데이터 패칭, 동적 렌더링 등을 수행할 때는 클라이언트 사이드 기능을 필요로 하지 않기 때문이다.

const UserInfo = ({ user }) => {
  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
    </div>
  );
}

export default UserInfo;

공식 문서를 참고한 서버 컴포넌트와 클라이언트 컴포넌트의 특징을 요약해 보자!

1. 서버 컴포넌트 (use server)

  • 서버에서 직접 데이터 가져오기
  • 보안에 민감한 정보에 안전하게 접근하기
  • 렌더링에 필요한 라이브러리를 번들에 포함하지 않음으로써 JS 사이즈 줄이기

2. 클라이언트 컴포넌트 (use client)

  • onClick, onChange와 같은 이벤트 리스너 사용하기
  • useState, useReducer, useEffect와 같은 상태와 생명주기 활용하기
  • 브라우저에서 제공하는 api 사용하기
  • state, effect, brower api를 사용하는 커스텀 훅 만들기



⚙️ 배포

개발자 도구 들어가서 네트워크를 눌러 새로고침해 보면 6.5 MB 리소스라고 나오는데, 이건 서버에서 클라이언트로 전송한 용량이 이정도라는 것이다.

엄청 큰 용량인데, 용량을 줄이고 실제 서버에 최적화된 버전이 필요하다!


package.json을 들어가 보면, 아래의 스크립트 안에 여러 명렁어들이 있다.

현재 npm run dev이기 때문에 개발 환경을 ctrl+c로 끄고, npm run build를 입력하면 배포 가능한 버전의 애플리케이션을 생성한다.

.next 폴더가 생성되고 여기에는 개발이나, 실제 서버로 빌드를 하거나 사용자에게 서버스되는 내용이 저장된다.

즉, 배포판이 이 .next 폴더에 만들어진다. 이제 npm run start를 입력하면 생성된 배포 버전을 실행하며 서비스를 시작한다.

"scripts": {
  "dev": "next dev",
  "build": "next build",
  "start": "next start",
  "lint": "next lint"
},

이렇게 실서버용으로 배포판을 만들어 네트워크를 확인하면 357 KB 리소스로 훨씬 용량이 작아진 것을 알 수 있다.


아직 공부해야할 것들이 많다! 분발하자! 킵고잉!

profile
할 수 있다고 믿는 사람은 결국 그렇게 된다 😄😊

0개의 댓글