본문 바로가기

WINK-(Web & App)/WINK 공홈 부수기

[WINK 공식 홈페이지] Next.js 스터디 정리 💫 [backend / 유건]

반응형

 

공식 docs. 를 통해 넥제스를 공부하면서 느낀건, Next.js는 풀스택 프레임워크이지만 프론트엔드 성향이 좀 더 짙어서 SSR을 공부하기 전까지는 아직 프론트엔드에 대한 기초 지식이 필요하다고 생각이 든다.

(프론트엔드는 너무 어려워)

 

지금까지 공부했던 내용들을 정리해보고자 한다.

1. Next.js란?

Next.js는 풀스택 웹 애플리케이션을 구축하기 위한 React 프레임워크이다. React 구성 요소를 사용하여 사용자 인터페이스를 구축하고 Next.js를 사용하여 추가 기능과 최적화를 수행할 수 있다.

 

2. Next.js 시작하기

다음 명령어로 프로젝트를 생성할 수 있다.

npx create-next-app@latest

 

이 방식은 react, react-dom, next 라이브러리를 자동적으로 설치해주는 방식이다.

 

해당 명령어를 실행하면 다음과 같은 프롬프트가 나온다.

What is your project named? my-app
Would you like to use TypeScript? No / Yes
Would you like to use ESLint? No / Yes
Would you like to use Tailwind CSS? No / Yes
Would you like to use `src/` directory? No / Yes
Would you like to use App Router? (recommended) No / Yes
Would you like to customize the default import alias (@/*)? No / Yes
What import alias would you like configured? @/*

 

Next.js는 이제 기본적으로 TypeScript , ESLint  Tailwind CSS 구성과 함께 제공된다. 따라서 프로젝트 자동 생성 시 package.json에 typescript, eslint, tailwind에 대한 패키지가 자동 설치된다.

 

테스트 겸 개발 서버를 실행해보자.

npm run dev 또는 yarn dev

 

실행하면 http://localhost:3000 주소로 app/page.tsx에 대한 페이지가 렌더링된다.

 

3. 프로젝트 구조 살펴보기

Next.js는 파일 시스템 라우팅, 즉, 파일 구조에 따라 애플리케이션의 경로가 결정되는 방식을 사용한다.

 

이는 두 가지로 나뉘는데, 기존의 page router 방식과 Next 13 버전부터 새로이 생겨난 app router 방식이다.

 

이 중에서 App Router 방식으로 스터디를 진행하고자 한다.

출처: Next.js 공식 문서

 

생성된 프로젝트 구조를 위 사진처럼 src/ 구조 또는 최상위 폴더 내에 app과 pages를 두는 경우로 나눌 수 있다.

 

app 앱 라우터
pages 페이지 라우터
public 제공할 정적 자산
src 선택적 애플리케이션 소스 폴더

 

본인은 npx create-next-app 으로 생성된 프로젝트를 기준으로 살펴보도록 하겠다.

 

my_app 프로젝트 안에 app, public 이 존재한다.

 

최상위 경로 파일

애플리케이션 구성, 종속성 관리, 미들웨어 실행, 모니터링 도구 통합 및 환경 변수 정의에 사용된다

next.config.js Next.js 구성 파일
package.json 프로젝트 종속성 및 스크립트
instrumentation.ts OpenTelemetry 및 계측 파일
middleware.ts Next.js 요청 미들웨어
.env 환경 변수
.env.local 로컬 환경 변수
.env.production 프로덕션 환경 변수
.env.development 개발 환경 변수
.eslintrc.json ESLint용 구성 파일
.gitignore 무시할 Git 파일 및 폴더
next-env.d.ts Next.js용 TypeScript 선언 파일
tsconfig.json TypeScript용 구성 파일
jsconfig.json JavaScript용 구성 파일

 

4. App Router

버전 13 이후부터 Next.js는 공유 레이아웃, 중첩 라우팅, 로딩 상태, 오류 처리 등을 지원하는 React Server Components를 기반으로 구축된 App Router 방식을 사용한다.

 

app이라는 디렉터리 내에서 작동하는데 app. 디렉터리 는 디렉터리 app와 함께 작동하여 pages점진적인 채택을 허용합니다. 이를 통해 이전 동작에 대한 디렉터리의 다른 경로를 유지하면서 애플리케이션의 일부 경로를 새 동작으로 선택할 수 있다.

 

- 폴더

폴더는 경로를 정의하는데 사용된다. 경로는 root segment부터 파일이 포함된 최종 leaf segment까지 파일 시스템 계층 구조를 따라가는 중첩된 폴더의 단일 경로이다.

 

- 파일

파일은 경로 세그먼트에 표시되는 UI를 만드는 데 사용된다.

app router에서 사용 가능한 특정 유형의 tsx 파일들을 살펴보자

layout.tsx // 세그먼트 및 해당 하위 항목에 대한 공유 UI

page.tsx // 경로의 고유한 UI 및 경로에 공개적으로 액세스 가능

loading.tsx // 로딩 중 UI

not-found.tsx // 404 Not Found UI

error.tsx //error 처리 파일

global-error.tsx // 전역 오류 UI

route.tsx // API 엔드포인트 UI

template.tsx // 다시 렌더링 되는 layout

default.tsx // 병렬 경로 대체 page

 


Routing에 대하여...

1. 구조

- Nested Routing (중첩 경로)

원하는 엔드포인트를 설정하기 위해 directory/directory 구조로 중첩하여 생성하는 것이다.

 

중첩 경로에서는 세그먼트의 구성 요소 가 상위 세그먼트의 구성 요소 내에 중첩된다.

- Dynamic Routing(동적 라우팅)

사진처럼 Path Variable에 따라 동적으로 페이지를 렌더링하는 방식이다.

 

2. 레이아웃 & 템플릿

특수 파일인 layout.js  template.js를 사용하면 경로 간에 공유되는 UI를 생성할 수 있다.

 

- layout

레이아웃은 여러 경로 간에 공유되는 UI이다. 탐색 시 레이아웃은 상태를 유지하고 대화형을 유지하며 다시 렌더링되지 않는 특징을 지닌다.

 

기본적으로 layout.js을 통해 React component를 export할 수 있다. 이는 하위 page를 wrapping하는 효과를 가진다.

또한 해당 component는 children을 통해서 하위 페이지들을 보여준다.

 

예시로, 아래 사진처럼 dashboard/layout.js 는 /dashboard 페이지와 /dashboard/settings 페이지 모두에게 적용된다. 

 

- root layout

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>
        {/* Layout UI */}
        <main>{children}</main>
      </body>
    </html>
  )
}

 

app 디렉터리의 최상위 수준에서 정의되며 모든 경로에 적용된다.

이 레이아웃은 필수이며 서버에서 리턴된 초기 HTML을 수정할 수 있도록 html과 body 태그 포함해야 한다.

 

webstorm에서 작업 시 해당 layout.tsx 파일이 없으면 자동으로 생성되는 현상을 발견했다. 필수라는 뜻이겠지.

 

- Nested Layout

기본적으로 폴더 계층 구조의 레이아웃은 중첩되어 있다.  즉, children props를 통해 하위 레이아웃 또는 페이지를 wrapping 한다는 말이다.

 

밑의 사진처럼 layout.js를 특정 경로의 세그먼트 내부에 추가하여 레이아웃을 중첩할 수 있다 .

 

 

- app/dashboard/layout.js

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

 

 

실행 결과

- Template

기본적으로 Layout은 페이지를 이동해도 re-rendering이 발생하지 않는다. Layout에게도 mount 되어야 하는 상황이 발생하게 되면 사용하는 것이 바로 Template이다.

 

Template은 레이아웃과 유사하게 자식 레이아웃이나 페이지를 감싸는 역할을 한다. Layout과 다른 점은 탐색할 때, 자식 요소마다 새로운 인스턴스를 생성한다. 같은 템플릿을 사용하는 router로 이동할 때도 새로운 인스턴스가 마운트되고, DOM 요소가 다시 생성되며 상태가 보존되지 않는다.

 

따라서 

  • useEffect 탐색 시 재동기화.
  • 하위 클라이언트 component의 상태 재설정.
  • 페이지를 넘나들 때마다 무언가를 기록해야 하는 상황.

등과 같은 상황에서 사용된다.

 

공식 문서에 따르면, layout을 사용하는게 더 좋다고 명시되어 있음.

 

 

- app/template.tsx

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

 

 

- Template 렌더링 위치

중첩 측면에서 template.js 레이아웃과 해당 하위 항목 사이에 렌더링된다.

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

 

- metadata

metadata API 를 사용하여 title, meta와 같은 <head> HTML 요소를 수정할 수 있다.

 

app/page.tsx

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

 

- usePathname()

현재 url을 알 수 있는 Client Component hook이다. 사용 시 'use client'를 꼭 써줘야 한다.

 

ex> app/ui/nav-links.tsx

'use client'

import { usePathname } from 'next/navigation'
import Link from 'next/link'

export function NavLinks() {
    const pathname = usePathname()

    return (
        <nav>
            <Link className={`link ${pathname === '/' ? 'active' : ''}`} href="/">
                Home
            </Link>
            <br/>
            <Link className={`link ${pathname === '/about' ? 'active' : ''}`} href="/about">
                About
            </Link>
        </nav>
    )
}

 

app/layout.tsx

import { NavLinks } from '@/app/ui/nav-links'
 
export default function Layout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <NavLinks />
        <main>{children}</main>
      </body>
    </html>
  )
}

 

3. Linking & Navigating

Next.js에서 경로 탐색 방법은 다음과 같다.

  • <Link> component 사용
  • useRouter hook 사용 (Client Component)
  • redirect 기능 사용 (Server Component)
  • Navigate History API 사용

 

- <Link> Component

<Link>는 HTML <a> 태그를 확장하여 경로 간 프리패치 및 클라이언트 측 탐색을 제공하는 내장 구성 요소이다.

 

app/page.tsx

import Link from 'next/link'
 
export default function Page() {
  return <Link href="/dashboard">Dashboard</Link>
}

 

- 동적 세그먼트 연결

ex> 블로그 목록을 props로 받아 동적으로 블로그 게시글 목록 생성하기.

import Link from 'next/link'
 
export default function PostList({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>
          <Link href={`/blog/${post.slug}`}>{post.title}</Link>
        </li>
      ))}
    </ul>
  )
}

 

- ID로 스크롤

  • Next.js 앱 라우터의 기본 동작은 새 경로의 맨 위로 스크롤하거나 앞뒤 탐색을 위해 스크롤 위치를 유지하는 것.
  • 탐색 시 특정 ID로 스크롤하려면 # 해시 링크를 URL에 추가하거나 해시 링크를 href 속성에 전달
  • 이는 <Link> 컴포넌트가 <a> 태그로 렌더링되기 때문에 가능하다!
<Link href="/dashboard#settings">Settings</Link>
 
// Output
<a href="/dashboard#settings">Settings</a>

스크롤 복원 비활성화

위에서 얘기했듯, next.js는 기본적으로 앞뒤 탐색을 위해 스크롤 위치를 유지할 수 있는데, 이 동작을 비활성화하려면 <Link> 컴포넌트에 scroll={false}를 전달해주거나 router.push 또는 router.replace()에 scroll: false를 전달한다.

// next/link
<Link href="/dashboard" scroll={false}>
  Dashboard
</Link>

// useRouter
import { useRouter } from 'next/navigation'
 
const router = useRouter()
 
router.push('/dashboard', { scroll: false })

 

 

useRouter() Hook

  • useRouter() hook을 사용하면 프로그래밍 방식으로 경로를 변경할 수 있다.
  • 이 hook은 client-component 내에서만 사용할 수 있으며 next/navigation에서 가져올 수 있다. (client-side에서만 실행되기 때문!)
'use client'
 
import { useRouter } from 'next/navigation'
 
export default function Page() {
  const router = useRouter()
 
  return (
    <button type="button" onClick={() => router.push('/dashboard')}>
      Dashboard
    </button>
  )
}

 

useRouter를 사용해야 하는 특별한 요구 사항이 없을 경우 <Link> 컴포넌트를 사용하는 것을 권장한다.

 

 

- redirect

server component에서는, redirect 함수를 사용한다.

import { redirect } from 'next/navigation'
 
async function fetchTeam(id: string) {
  const res = await fetch('https://...')
  if (!res.ok) return undefined
  return res.json()
}
 
export default async function Profile({ params }: { params: { id: string } }) {
  const team = await fetchTeam(params.id)
  if (!team) {
    redirect('/login')
  }
 
  // ...
}

 

4. Routing 및 Navigation 작동 방식

  • App Router는 라우팅 및 탐색을 위해 하이브리드 접근 방식을 사용한다. 서버에서 애플리케이션 코드는 경로 세그먼트 별로 자동으로 코드 분할된다.
  • 클라이언트에서 Next.js는 경로 세그먼트를 미리 가져오고 캐시함. 즉 사용자가 새 경로로 이동할 때 브라우저는 페이지를 다시 로드하지 않고 변경된 경로 세그먼트만 다시 렌더링하여 탐색 경험과 성능을 향상시킨다.

1. Prefetching (프리패칭)

  • 프리 패칭은 사용자가 경로를 방문하기 전에 백그라운드에서 경로를 미리 로드하는 방법. Next.js에서 경로를 미리 가져오는 방법에는 두 가지가 있습니다.
    • <Link> 컴포넌트 : 경로가 사용자의 뷰포트에 표시되면 자동으로 미리 가져온다. 프리 패칭는 페이지가 처음 로드될 때 또는 스크롤을 통해 표시될 때 발생
    • router.prefetch() : useRouter hook을 사용하여 프로그래밍 방식으로 경로를 미리 가져올 수 있다.
  • <Link>의 프리패치 동작은 정적 경로와 동적 경로에 따라 다름.
    • 정적 경로 : prefetch 기본값은 true. 전체 경로가 프리패치되고 캐시된다.
    • 동적 경로 : prefetch 기본값은 auto. 첫 번째 loading.js 파일이 프리패치되고 30초동안 캐시될 때까지 공유 레이아웃만 표시됨.
    • 이렇게 하면 전체 동적 경로를 가져오는 비용이 줄어들고 사용자에게 더 나은 시각적 피드백을 제공하기 위해 즉시 로드 상태를 표시할 수 있다.
  • prefetch prop을 false로 설정하여 프리패칭을 비활성화 할 수 있다.
  • 프리페칭은 개발 중에는 활성화되지 않고 프로덕션에서만 활성화된다.

2. Caching (캐싱)

  • Next.js에는 라우터 캐시라는 메모리 내 클라이언트 측 캐시가 있다.
  • 사용자가 앱을 탐색할 때 미리 가져온 경로 세그먼트와 방문한 경로의 React Server 컴포넌트 페이로드가 캐시에 저장됨.
  • 이는 탐색 시 서버에 새로운 요청을 하는 대신 캐시를 최대한 재사용하여 요청 수와 전송되는 데이터 수를 줄여 성능을 향상한다는 의미.

3. Partial Rendering (부분 렌더링)

  • 클라이언트에서 탐색을 다시 렌더링할 때 변경되는 경로 세그먼트만 의미하며 모든 공유 세그먼트는 보존됨.
  • 예를 들어 두 형제 경로 및 사이를 탐색하는 경우 /dashboard/settings 에서 /dashboard/analytics 로 이동하는 경우 analytics page만 렌더링되고 dashboard의 layout.js은 유지.

4. Soft Navigation (소프트 네비게이션)

  • 기본적으로 브라우저는 페이지 간 하드 탐색을 수행한다. useState는 브라우저가 페이지를 다시 로드하고 앱의 hook과 같은 React 상태와 사용자의 스크롤 위치 또는 초점이 맞춰진 요소와 같은 브라우저 상태로 재설정한다는 것을 의미.
  • 그러나 Next.js에서 앱 라우터는 소프트 탐색을 사용한다. 즉, React가 React와 브라우저 상태를 유지하면서 변경된 세그먼트만 렌더링하며 전체 페이지를 다시 로드하지 않는다.

5. Back and Forward Navigation (뒤로 또는 앞으로 탐색)

  • 기본적으로 Next.js는 앞뒤 탐색을 위한 스크롤 위치를 유지하고 라우터 캐시의 경로 세그먼트를 재사용한다.

참고

 

Routing: Linking and Navigating | Next.js

Learn how navigation works in Next.js, and how to use the Link Component and `useRouter` hook.

nextjs.org

 

반응형