next.js 입문

Yunes·2023년 11월 12일
0

[포스코x코딩온]

목록 보기
41/47
post-thumbnail

설치

npx create-next-app@latest
npm run dev

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

next dev : Next.js 를 개발모드에서 실행

next build : 프로덕션에서 사용할 애플리케이션을 빌드

next start : 프로덕션 서버 Next.js 실행

next lint : 만들어진 Next.js 용 ESLint 설정파일을 설정한다.

디렉토리 생성

Next.js 는 파일 시스템 라우팅 이라는 것을 사용한다. 이는 파일을 어떻게 구성하는가에 의해 애플리케이션에서 라우팅을 결정한다는 것을 의미한다.

src/app/layout.js 는 기본적인 웹 페이지의 골격을 구성한다.

app 디렉토리

새 애플리케이션에서 App Router 를 사용하는 것을 권장한다. 이는 React 의 최신 기능들을 사용할 수 있게 하고 커뮤니티의 피드백에 의해 Pages Router 에서 진화된 라우터이다.

이미지 출처 : https://nextjs.org/docs/getting-started/installation

app/ 디렉토리에 layout.tsx, page.tsx 를 추가하자. 이들이 유저가 애플리케이션에 / 로 접속할때 렌더링된다.

app/layout.tsx 에 필요한 html 과 body 태그 안에 루트 레이아웃을 만들자.

app/page.tsx 를 만들어 초기 내용을 생성할 수 있다.

// app/page.tsx
export default function Page() {
  return <h1>Hello, Next.js!</h1>
}

알아두면 좋은 것
만약 layout.tsx 생성을 잊었다고 해도 next dev 로 개발 서버를 실행하면 Next.js 가 이 파일을 자동으로 생성해준다.

pages 디렉토리 (optional)

만약 App router 대신 Pages Rouer 를 사용하고자 한다면 pages/ 디렉토리를 프로젝트 루트에 생성하여 사용할 수 있다.

그러면 index.tsx 를 pages 폴더에 추가해야 한다. 이게 홈페이지 / 가 될 것이다.

// pages/index.tsx
export default function Page() {
  return <h1>Hello, Next.js!</h1>
}

다음으로 _app.tsx 파일을 pages/ 안에 추가하여 global layout 을 추가한다.

// pages/_app.tsx

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

마지막으로 _document.tsx 파일을 pages/ 내에 추가하여 서버로부터 초기 response 를 제어할 수 있다.

// pages/_document.tsx
import { Html, Head, Main, NextScript } from 'next/document'
 
export default function Document() {
  return (
    <Html>
      <Head />
      <body>
        <Main />
        <NextScript />
      </body>
    </Html>
  )
}

알아두면 좋은 것
App RouterPages Router 나 두가지 라우터를 같은 프로젝트에서 동시에 사용해도 되지만 app 경로가 pages 보다 우선순위를 갖는다. Next.js 는 혼란을 피하기 위해 프로젝트에서 하나의 라우터만 사용할 것을 권장한다.

public folder (optional)

이미지, 폰트같은 정적 assets 를 저장하기 위해 public folder 를 생성할 수 있다. public 디렉토리 내의 파일들은 base URL / 로 시작하여 코드에서 참조할 수 있다.

Root Layout ( required )

// app/layout.tsx
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}

layout.tsx 의 children 은 같은 디렉토리의 page.tsx 가 리턴한 것을 가져온다.

root layout 은 app 디렉토리의 최상단에 정의되고 모든 라우트에 적용된다. 이 레이아웃은 서버에 의해 반환되는 초기 HTML 을 변경할 수 있도록 한다.

알아두면 좋은 것

  • app 디렉토리는 반드시 루트 레이아웃을 포함해야 한다.
  • 루트 레이아웃은 반드시 html 과 body 태그를 정의해야 하는데 이는 Next.js 가 자동으로 그들을 생성하지 않기 때문이다.
  • title 요소와 같이 head HTML 요소를 조절하기 위해 SEO 를 돕는 기능을 사용할 수 있다.
  • 다수의 루트 레이아웃들을 생성하기 위해 route groups 를 사용할 수 있다.
  • 루트 레이아웃은 기본인 서버 컴포넌트이며 클라이언트 컴포넌트에 의해서는 설정할 수 없다.

루트 레이아웃은 _app.js, _document.js 파일을 대체한다.

muliple layout

다중 루트 레이아웃들을 생성하기 위해 가장 최상단의 layout.js 를 제거하고 각 route groups 안에 layout.js 를 추가하자. 이는 완전히 다른 UI 나 경험을 갖는 섹션으로 애플리케이션을 분할하는데 유용하다. html 과 body 태그가 각 루트 레이아웃에 추가되어야 한다.

위의 예시는 marketing 과 shop 이 각각의 루트 레이아웃을 갖는다.

알아두면 좋은 것

  • route groups 의 이름은 조직을 위한 것 외에는 특별한 중요도를 갖지 않는다. URL 경로에 영향을 끼치지 않는다.
  • route group 을 포함하는 Routes 는 다른 routes 와 같은 URL 경로를 가져서는 안된다. 예를 들어 route groups 가 URL 구조에 영향을 끼치지 않기에 (marketing)/about/page.js(shop)/about/page.js 는 둘다 /about 라고 보기에 에러를 유발할 수 있다.
  • 최상단의 layout.js 없이 다중 루트레이아웃을 사용한다면 home 인 page.js 파일은 route groups 중 하나에 정의되어야 한다. ex) app/(marketing)/page.js
  • 다중 루트 레이아웃을 탐색하면 (navigating) 이는 전체 페이지 로드를 야기할 수 있다. (클라이언트 측 탐색과 대조적) 예를 들어 app/(shop)/layout.js 를 사용하는 /cart 로부터 app/(marketing)/layout.js 를 사용하는 /blog 로 탐색한다고 할때 이는 전체 페이지 로드를 야기한다. 이는 오로지 다중 루트 레이아웃에 적용된다.

Server Component

리액트의 서버 컴포넌트는 서버측에서 렌더링되고 선택적으로 캐싱될 수 있는 UI 를 작성할 수 있도록 한다.

Next.js 에서 렌더링 작업은 스트리밍과 분할된 렌더링을 가능하게 하기 위해 route segments 에 의해 분할된다. Next.js 는 세가지 렌더링 전략을 갖는다.

  1. Static Rendering

  2. Dynamic Rendering

  3. Streaming

Static Rendering

정적 렌더링과 함께 routes (경로) 는 빌드 타임 혹은 데이터 재검증 이후 백그라운드에서 렌더링된다. 결과는 캐시되어 CDN (Content Delivery Network) 로 푸시될 수 있다. 이 최적화를 통해 사용자와 서버 요청 간 렌더링 작업 결과를 공유할 수 있다.

정적 렌더링은 경로에 상용자에게 개인화되지 않은 데이터가 있고 정적 블로그 게시물이나 제품 페이지와 같이 빌드 시 알 수 있는 데이터가 있을때 유용하다.

데이터 재검증 Data Revalidation 은 데이터 캐시를 제거하고 최신 데이터를 다시 가져오는 프로세스이다. 캐시된 데이터는 다음 두가지 방법으로 유효성을 검사할 수 있다.

1. 시간 기반 재검증

  • 일정 시간이 지나면 데이터를 자동으로 재검증한다. 이는 자주 변경되지 않고 최신성이 중요하지 않은 데이터에 유용하다.
    fetch('https://...', { next: { revalidate: 3600 } })
  • 혹은 fetch 경로 세그먼트의 모든 요청을 재검증하기 위해 Segment Config Options 를 사용할 수 있다.
    // layout.js | page.js
    export const revalidate = 3600 // revalidate at most every hour

2. 주문형 재검증

  • 이벤트를 기반으로 데이터를 수동으로 재검증한다. 주문형 재검증에서는 태그 기반 또는 경로 기반 접근 방식을 사용해서 데이터 그룹을 한 번에 재검증할 수 있다.
    export default async function Page() {
      const res = await fetch('https://...', { next: { tags: ['collection'] } })
      const data = await res.json()
      // ...
    }

Dynamic Rendering

동적 렌더링을 사용하면 요청시 각 사용자에 대한 경로가 렌더링된다.

동적 렌더링은 경로에 사용자에게 맞춤화된 데이터가 있거나 쿠키나 URL 의 검색 매개변수와 같이 요청 시에만 알 수 있는 정보가 있을 때 유용하다.

Streaming

스트리밍을 사용시 서버에서 UI 를 점진적으로 렌더링할 수 있다. 작업은 여러 다누이로 분할되어 준비가 되면 클라이언트로 스트리밍된다. 이를 통해 사용자는 전체 콘텐츠의 렌더링이 완료되기 전에 페이지의 일부를 즉시 볼 수 있다.

스트리밍은 기본적으로 Next.js 앱 라우터에 내장되어 있다. 이는 초기 페이지 로딩 성능 뿐 아니라 전체 경로 렌더링을 차단하는 느린 데이터 가져오기에 의존하는 UI 를 모두 개선하는데 도움이 된다. ex) 제품 페이지 리뷰

loading.js 나 React Suspense 와 함께 UI 컴포넌트를 사용하는 스트리밍 route segment 를 시작할 수 있다.

React Suspense 는 children 의 로딩이 완료될 때까지 fallback 유틸을 보여줄 수 있다.

<Suspense fallback={<Loading />}>
  <SomeComponent />
</Suspense>

UI 로딩과 스트리밍 참고

서버 렌더링의 장점

  • 데이터 가져오기
    • 데이터 가져오기를 데이터 소스에 더 가까운 서버로 이동할 수 있으니 렌더링에 필요한 데이터를 가져오는데 걸리는 시간과 클라이언트가 수행해야 하는 요청 양을 줄여 성능을 향상시킬 수 있다.
  • 보안
    • 서버 컴포넌트를 사용시 토큰, API 키같은 민감한 데이터와 로직을 클라이언트에 노출할 위험 없이 서버에 보관할 수 있다.
  • 캐싱
    • 서버에서 렌더링하면 결과를 캐시하고 후속 요청 및 사용자 전체에서 재사용할 수 있다. 이를 통해 각 요청에 대해 수행되는 렌더링 및 데이터 가져오기 양이 줄어들어 성능이 향상되고 비용이 절감될 수 있다.
  • 번들 크기
    • 이전에 서버의 클라이언트 JS 번들 크기에 영향을 주었던 큰 종속성을 유지할 수 있다. 이는 클라이언트가 서버 컴포넌트용 JS 를 다운, 분석할 필요가 없기에 인터넷 속도가 느리거나 성능이 부족한 장치를 사용할 경우 유용하다.
  • 초기 페이지 로드 및 First Contentful Paing
    • 서버에서는 클라이언트가 페이지를 렌더링하는데 필요한 JS 를 다운, 분석 및 실행할 때까지 기다리지 않고 사용자가 즉시 페이지를 볼 수 있도록 HTML 을 생성할 수 있다.
  • 검색 엔진 최적화 및 소셜 네트워크 공유성
    • 렌더링된 HTML 은 검색엔진 봇이 페이지를 인덱스화 하는데 사용하고 소셜 네트워크 봇이 페이지에 대한 소셜 카드 미리보기를 생성하는데 사용할 수 있다.
  • 스트리밍
    • 서버 컴포넌트를 사용하면 렌더링 작업을 분할하고 준비가 되면 클라이언트로 스트리밍할 수 있다. 이를 통해 전체 페이지가 서버에서 렌더링될 때까지 기다리지 않고도 페이지의 일부를 더 일찍 볼 수 있다.

기볼적으로 Next.js 는 서버 컴포넌트를 사용하여 추가 구성없이 서버 렌더링을 자동으로 구현하며 필요시 클라이언트 컴포넌트 사용을 선택할 수 있다.

Client Component

클라이언트 컴포넌트를 사용하면 request time 에 클라잉너트에 렌더링할 수 있는 대화형 (reactive) UI 를 작성할 수 있다. Next.js 에서 클라이언트 렌더링은 옵션이다. 즉, React 가 클라이언트에서 렌더링해야 하는 컴포넌트를 명시적으로 결정해야 한다.

클라이언트 렌더링의 장점

  • 상호 작용성
    • 클라이언트 컴포넌트는 상태, Effect 및 이벤트 리스너를 사용할 수 있어 사용자에게 즉각적인 피드백을 제공하고 UI 를 업데이트할 수 있다.
  • 브라우저 API
    • 클라이언트 컴포넌트는 위치 정보, localStorage 같은 브라우저 API 에 액세스하여 특정 사용 사례를 위한 UI 를 만들 수 있게 한다.

클라이언트 컴포넌트는 어떻게 사용할 수 있을까?

상단에 use client 지시어를 추가하면 된다. 그러면 하위 구성요소를 포함하여 해당 파일로 가져온 다른 모든 모듈이 클라이언트 번들의 일부로 간주된다.

// app.couter.tsx
'use client'
 
import { useState } from 'react'
 
export default function Counter() {
  const [count, setCount] = useState(0)
 
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
    </div>
  )
}

여러 use client 진입점 정의하기

  • 리액트 컴포넌트 트리에 다중 use client 진입점을 정의할 수 있다. 이를 통해 앱을 다중 클라잉너트 번들로 쪼갤 수 있다.
  • 그러나 use client 는 클라잉너트에 렌더링될 모든 컴포넌트에 정의될 필요는 없다. 한번 경계에 정의하면 모든 자식 컴포넌트와 해당 컴포넌트로 import 된 모듈들은 클라이언트 번들의 일부로 간주된다.

Nested Layout

app/dashboard/layout.js 와 같이 폴더 내에 정의된 레이아웃은 특정한 탐색 조각에 적용될 수 있고 (acme.com/dashboard) 이 segment 들이 활성화되어있을 때 렌더링된다. 기본적으로 ㄹ파일 계층 구조 내의 레이아웃들은 중첩되며 이는 그들의 자식 레이아웃들을 children prop 을 통해 감싼다는 것을 의미한다.

export default function DashboardLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return <section>{children}</section>
}

알아두면 좋은 것

  • 오직 루트 레이아웃만 html 과 body 태그를 갖는다.

만약 위의 예시에서 두 레이아웃을 결합한다면 app/layout.jsapp.dashboard/layout.js 를 감싸 app/dashboard/* 처럼 사용한다.

Templates

템플릿은 각 자식 레이아웃이나 페이지를 감싼다는 점에서 레이아웃과 유사하다. 그러나 경로 전반에 걸쳐 지속되고 상태를 유지하는 레이아웃과 달리 템플릿은 탐색에서 각 자식마다 새로운 인스턴스를 생성한다.

이는 유저가 경로 사이에서 템플릿을 공유할 때 그 컴포넌트의 새로운 인스턴스가 마운트되고 DOM 요소가 다시 생성되고 상태는 보존되지 않으며 effect 는 다시 동기화됨을 의미한다.

다음과 같은 경우 템플릿은 layout 보다 더 나은 선택이 될 수 있다.

  • useEffect ( logging page views ), useState ( 페이지당 피드백 폼 ) 에 의존적인 기능
  • 기본적인 프레임워크 동작을 바꾸는 용도일때. 예를들어 레이아웃 내의 Suspense Boundaries 는 오로지 레이아웃이 처음 로드되었을 때만 fallback 을 보여주고 페이지를 바꿀때는 보여주지 않는다. 반면 템플릿은 매 탐색마다 fallback 을 보여준다.

템플릿은 template.js 파일에서 기본적인 리액트 컴포넌트를 export 하여 정의될 수 있다. 컴포넌트는 반드시 children prop 을 받아야 한다.

// app/template.tsx
export default function Template({ children }: { children: React.ReactNode }) {
  return <div>{children}</div>
}

중첩 측면에서 template.js 는 layout 과 layout 의 children 사이에서 렌더링된다.

<Layout>
  {/* Note that the template is given a unique key. */}
  <Template key={routeParam}>{children}</Template>
</Layout>

<head> 태그 수정하기

app 디렉토리에서 title 과 SEO 를 돕는 meta 같은 head HTML 요소를 수정할 수 있다.

Metadata 는 metadata 객체나 layout.js 혹은 page.js 파일 내의 generatedMetadata 함수를 통해 exporting 하여 정의될 수 있다.

// app.page.tsx
import { Metadata } from 'next'
 
export const metadata: Metadata = {
  title: 'Next.js',
}
 
export default function Page() {
  return '...'
}

알아두면 좋은 것

  • title, meta 같은 head 태그를 루트 레이아웃에 수동으로 추가하면 안된다. 대신 자동으로 스트리밍이나 중복 제거같은 것을 다루는 Metadata API 를 사용해야 한다.

프로젝트 구조

최상위 폴더

app : App Router

pages : Pages Router

public : Static assets to be served

src : 선택적인 애플리케이션 소스 폴더

최상위 파일

next.config.js : Next.js 설정 파일

package.json : 프로젝트 의존성과 스크립트

instrumentation.ts : OpenTelementry 와 Instrumentation 파일

middleware.ts : Next.js 요청 미들웨어

.env : 환경 변수

.env.local : 로컬 환경 변수

.env.production : 프로덕션 환경 변수

.env.development : 개발 환경 변수

.eslintrc.json : ESLint 설정 파일

.gitignore : 무시할 Git 파일과 폴더

next-env.d.ts : Next.js 를 위한 타입스크립트 선언 파일

tsconfig.json : 타입스크립트 설정 파일

jsconfig.json : 자바스클비트 설정 파일

app 라우팅 컨벤션

라우팅 파일 Routing Files

중첩 경로 Nested Routes

동적 경로 Dynamic Routes

Route Groups and Private Folders

Parallel and Intercepted Routes

메타 데이터 파일 컨벤션

App Icons

Open Graph and Twitter Images

SEO

pages 라우팅 컨벤션

Special Files

Routes

Dynamic Routes

profile
미래의 나를 만들어나가는 한 개발자의 블로그입니다.

0개의 댓글