Defining Routes
Next.js는 라우터를 별도로 설치하지 않고, app 디렉토리를 기반으로 하는 파일 기반 라우팅을 지원함
- 폴더 기반 라우팅 : 각 폴더는 URL 세그먼트에 매핑되는 경로를 나타냄
- 특수 파일 : ‘page.js’ , ‘layout.js’, ‘loading.js’, ‘error.js’ 등의 특수 파일을 사용하여 각 라우트의 UI와 동작을 정의
- 중첩 라우팅 : 폴더를 중첩하여 복잡한 라우트 구조를 만듦
- 레이아웃 공유 : ‘layout.js’ 파일을 사용하여 여러 페이지에서 UI를 쉽게 공유할 수 있음
- 서버 컴포넌트 : 기본적으로 ‘app’ 디렉토리의 모든 컴포넌트는 React 서버 컴포넌트로 처리
Not Found
not-found.tsx는 일치하지 않는 전역 URL을 처리.
app폴더 root 경로에 만듦.
https://nextjs.org/docs/app/api-reference/file-conventions/not-found
[File Conventions: not-found.js | Next.js
API reference for the not-found.js file.
nextjs.org](https://nextjs.org/docs/app/api-reference/file-conventions/not-found)
Link
HTML요소를 확장하여 경로 간 프리페칭(사용자가 경로를 방문하기 전에 백그라운드에서 경로를 미리 로드하는 방법) 및 클라이언트 측 탐색을 제공하는 React 구성 요소. Next.js에서 경로간을 탐색하는 기본 방법
// app/components/Navigation.tsx
import Link from 'next/link'
export default function Navigation() {
return (
<nav>
<ul>
<li><Link href="/">Home</Link></li>
</ul>
</nav>
)
}
usePathname
usePathname은 현재 URL의 pathname을 읽을 수 있게 해주는 클라이언트 컴포넌트 훅. 현재 URL의 경로이름 문자열을 반환
웹 주소 : / → 반환된 값 : ‘/’
https://nextjs.org/docs/app/api-reference/functions/use-pathname
// app/components/Navigation.tsx
'use client'
import Link from 'next/link'
import { usePathname } from 'next/navigation'
export default function Navigation() {
const path = usePathname()
return (
<nav>
<ul>
// 현재위치가 path경로와 같다면 이모지 렌더링
<li>
<Link href="/">Home</Link> {path === "/" ? "👌": ""}
</li>
<li>
<Link href="/some">Some</Link> {path === "/some" ? "👌": ""}
</li>
</ul>
</nav>
)
}
파일 상단에 ‘use client’ 를 추가하여 클라이언트 컴포넌트 선언을 함. 서버 컴포넌트와 클라이언트 컴포넌트를 적절히 조합하여 최적의 성능을 달성하는데 도움이 됨
‘use client’ 지시문을 사용하는 이유
- 클라이언트 사이드 렌더링 활성화 : usePathname은 클라이언트 컴포넌트에서만 사용할 수 있는 훅이기 때문에 ‘use client’를 선언함으로써 해당 컴포넌트가 클라이언트 사이드에서 렌더링되도록 지정
- 서버 컴포넌트와의 구분: Next.js는 기본적으로 SSR을 지원하기 때문에 ‘use client’ 선언을 통해 해당 컴포넌트가 클라이언트에서 실행되어야 함을 명시
- 브라우저 API 접근: usePathname과 같은 브라우저 관련 기능을 사용하기 위해 클라이언트 컴포넌트 선언
- 동적 상호작용 지원: 클라이언트 컴포넌트는 사용자 상호작용에 즉시 반응할 수 있어, URL 변경과 같은 동적인 기능을 구현하는데 필요.
CSR vs SSR : Next.js에서의 렌더링 방식 비교
렌더링은 React컴포넌트를 브라우저가 이해할 수 있는 HTML로 변환하는 과정. Next.js에서는 주로 클라이언트 사이드 렌더링(CSR)과 서버 사이드 렌더링(SSR) 두 가지 방식을 사용.
클라이언트 사이드 렌더링 (CSR)
- 작동방식:
- 서버는 최소한의 HTML 문서와 JS파일을 브라우저로 전송
- 브라우저가 JS를 실행하여 동적으로 UI를 구축하고 콘텐츠를 렌더링함
- 특징:
- 렌더링 위치 : 클라이언트(브라우저)에서 전적으로 이루어짐
- 성능: 초기 로딩이 느리지만, 로드 후 상호작용은 빠름
- SEO: 검색 엔진 최적화에 불리
- Next.js 예시: 컴포넌트에
use client를 사용하면 클라이언트 측 렌더링
서버 사이드 렌더링 (SSR)
- 작동방식:
- 서버에서 각 요청마다 페이지의 완전한 HTML을 생성
- 서버가 완전히 렌더링 된 HTML을 브라우저로 전송
- 특징:
- 렌더링 위치: 서버에서 이루어짐
- 성능: 초기 로딩이 빠르지만, 상호작용 시간이 느림
- SEO: 검색 엔진 최적화에 매우 유리
- Next.js 예시: Next.js는 기본적으로 SSR을 사용. ‘getServerSideProps’를 통해 명시적으로 구현가능
Next.js의 렌더링 동작
- Next.js에서는
use client사용 여부와 관계없이 모든 컴포넌트와 페이지가 기본적으로 서버에서 사전 렌더링. - 개발자는 애플리케이션의 요구에 따라 CSR과 SSR을 선택적 사용
- CSR: SEO가 중요하지않은 상호작용이 많은 페이지
- SSR: 빠른 초기 로딩과 SEO최적화가 필요한 페이지에 적합
Hydration
하이드레이션은 서버에서 렌더링(SSR)된 정적 HTML을 클라이언트 측에서 JS를 사용하여 완전한 상호작용이 가능한 React 애플리케이션으로 전환하는 과정.
SSR을 이점을 살리면서도 React의 동적인 특성을 활용할 수 있게 해주는 중요과정. 더 나은 사용자 경험과 성능을 제공할 수 있음.
하이드레이션 과정
- 초기 HTML 로드: 사용자가 페이지에 접속하면 서버에서 생성된 정적 HTML을 받음
- JS로드: 브라우저가 HTML을 파싱하면서 필요한 JS파일들을 로드
- React 초기화: JS가 로드되면 React가 초기화되고, 컴포넌트를 로드
- 이벤트 리스너 부착: React는 기존 HTML 요소들에 이벤트 리스너 부착
- 상호작용 가능 상태: 이 과정이 완료되면 페이지가 완전히 상호작용 가능한 React 애플리케이션으로 변환됨
하이드레이션의 이점
- 빠른 초기 로딩: 서버에서 렌더링된 HTML을 먼저 보여주어 사용자가 빠르게 콘텐츠를 볼 수 있음
- SEO 개선: 검색 엔진이 정적 HTML을 쉽게 크롤링할 수 있어 SEO에 유리
- 클라이언트 사이드 네비게이션: 페이지 전체를 다시 로드하지 않고 빠르게 페이지 간 이동이 가능
주의사항
- 서버에서 렌더링된 HTML과 클라이언트에서 렌더링되는 컴포넌트의 내용이 일치해야 함. 불일치가 있을 경우 하이드레이션 오류가 발생할 수 있음
use client
use client 지시어는 해당 컴포넌트가 클라이언트 사이드에서 하이드레이션 될 것임을 나타냄
- 서버에서 먼저 렌더링됨
- 클라이언트로 전송된 후, 브라우저에서 JS가 실행되면 해당 컴포넌트가 하이드레이션.
hydration 과정은 모든 component에 대해 발생하지 않음
server side render는 모든 component에 대해 발생.
client에서 hydrate되는 components는 오직 use client 지시어를 맨 위에 갖고 있는 component들 뿐임.
이는 성능 최적화를 위한 것으로, 필요한 컴포넌트만 상호작용을 하게 됨.use client 는 오직 client에서만 render된다는 것을 의미하지 않음.
backend에서 render되고 frontend에서 hydrate됨을 의미. (use hydrate 라고 생각하는게 나을수도)
client component안에 server component를 가질 수 없다
But, sever component안에 client component는 가질 수 있음
server component안에서 DB와 통신할 때, API key를 사용해서 API를 fetch한다고 하면 해당 코드는 client로는 절대 가지 않기 때문에 보안을 신경쓰지 않아도 되는 이점이 있음.
(client side에 API를 그대로 쓰는것은 편한 이점이지만, deploy를 위해 github repo에 저장할때 민감한 정보 노출이라는 경고메일을 받은 적이 있다. 따라서 API는 그냥 별도의 구성파일에 저장하고 .gitignore로 제외해주는게 좋다)
Layouts
Next.js에서 layout은 여러 페이지 간에 공유되는 UI컴포넌트를 정의하는 중요한 개념. 레이아웃은 코드 재사용성을 높이고 일관된 사용자 경험을 제공하는데 도움이 됨.
레이아웃의 주요 특징
- 공통 UI요소: 레이아웃은 주로 헤더, 푸터, 네이게이션 바와 같은 공통 UI요소를 포함.
- 중첩 가능: 레이아웃은 중첩될 수 있으며, 이는 더 세분화된 UI구조를 만들 수 있게 함
- 상태 유지: 레이아웃은 페이지 간 이동 시 상태를 유지하고 불필요한 리렌더링을 방지함
- 루트 레이아웃: 모든 Next.js 애플리케이션은 최소한 하나의 루트 레이아웃을 가져야 함
중첩 레이아웃
중첩 레이아웃의 핵심 개념은 레이아웃이 서로 상쇄되지 않고 중첩된다는 것.
- 계층 구조: 상위 레이아웃이 하위 레이아웃을 감싸는 형태로 구성
- 독립적 적용: 각 레이아웃은 해당 경로와 그 하위 경로에만 적용
- 유연한 구조: 필요에 따라 특정 페이지나 섹션에 추가적인 레이아웃을 적용할 수 있음
예시 코드 및 디렉토리 구조
app/
├── layout.js (루트 레이아웃)
├── page.js (홈 페이지)
├── about/
│ ├── layout.js (about 섹션 레이아웃)
│ └── page.js (about 페이지)
└── dashboard/
├── layout.js (대시보드 레이아웃)
├── page.js (대시보드 메인 페이지)
└── settings/
└── page.js (대시보드 설정 페이지)
// app/layout.js (루트 레이아웃)
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<header>글로벌 헤더</header>
{children}
<footer>글로벌 푸터</footer>
</body>
</html>
);
}
// app/about/layout.js (about 섹션 레이아웃)
export default function AboutLayout({ children }) {
return (
<div>
<nav>About 네비게이션</nav>
{children}
</div>
);
}
// app/dashboard/layout.js (대시보드 레이아웃)
export default function DashboardLayout({ children }) {
return (
<div>
<nav>대시보드 사이드바</nav>
{children}
</div>
);
}
위 구조에서 ‘/dashboard/settings’ 경로에 접근하면, 루트 레이아웃, 대시보드 레이아웃, 그리고 settings 페이지 컴포넌트가 순차적으로 중첩되어 렌더링 됨. 각 레이아웃은 자신의 고유한 UI요소를 추가하면서 하위 컴포넌트를 ‘children’ prop으로 감싸게 됨.
Metadata
route의 app에 폴더 (home)을 만들었을 때 home은 url을 생성하지 않음
Next.js 에는 향상된 SEO를 위해 애플리케이션 메타데이터를 정의하는 사용할 수 있는 API가 있음.
https://nextjs.org/docs/app/building-your-application/optimizing/metadata
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: "%s | Next Movies", // %s 부분이 병합
description: '이것은 내 웹사이트의 설명입니다.',
}
export default function Page() {
// ...
}
동적 메타데이터 예시
import type { Metadata } from 'next'
export async function generateMetadata({ params }): Promise<Metadata> {
const product = await getProduct(params.id)
return {
title: product.name,
description: product.description,
}
}
export default function Page({ params }) {
// ...
}
메타데이터 병합
Metadata API는 layout과 page에서만 사용이 가능하고,
여러 곳에서 사용한 Metadata는 자동으로 병합됨.
// app/layout.js
export const metadata = {
title: '내 웹사이트',
openGraph: {
title: '내 웹사이트',
description: '웹사이트 소개',
},
}
// app/blog/page.js
export const metadata = {
title: '블로그',
openGraph: {
title: '내 블로그',
},
}
// 최종 출력:
// <title>블로그</title>
// <meta property="og:title" content="내 블로그" />
// <meta property="og:description" content="웹사이트 소개" />
Dynamic Routes
https://nextjs.org/docs/app/building-your-application/routing/dynamic-routes
URL의 일부를 동적으로 처리할 수 있게 해주는 라우팅 기능.
유연하고 확장 가능한 웹 애플리케이션 구축가능.
- 동적 세그먼트: 파일 이름에 대괄호 [] 를 사용하여 동적 세그먼트를 정의.
ex)[id].js,[slug].js - params 객체: 동적 세그먼트의 값은 커포넌트의 props로 전달되는
params객체를 통해 접근가능 - 여러 동적 세그먼트: 여러 레벨의 동적 세그먼트를 사용할 수 있음.
ex)[category]/[id].js - Catch-all 라우트:
[...slug].js와 같이 세 개의 점(…)을 사용하여 여러 세그먼트를 캡처 할 수 있음
기본적인 동적 라우트
// pages/posts/[id].js
import { useRouter } from 'next/router'
export default function Post() {
const router = useRouter()
const { id } = router.query
return <p>포스트 ID: {id}</p>
}
/posts/1, /posts/2 등의 URL에 접근할 수 있음.
서버 컴포넌트에서 params사용
// app/blog/[slug]/page.tsx
export default function BlogPost({ params }: { params: { slug: string } }) {
return <h1>블로그 포스트: {params.slug}</h1>
}
이 예시에서 /blog/my-first-post로 접근하면 params.slug의 값은 'my-first-post' 임.
서버컴포넌트에서 searchParams 사용 예시
searchParams는 URL의 쿼리 문자열을 처리하는데 사용
// app/search/page.tsx
export default function SearchPage({ searchParams }: { searchParams: { region: string } }) {
return <h1>검색어: {searchParams.region}</h1>
}
이 예시에서 /search?region=kr로 접근하면 searchParams.region의 값은 'kr' 임.
기본적인 Dynamic Route구조
app/
├── blog/
│ ├── [slug]/
│ │ └── page.js
│ └── page.js
├── products/
│ ├── [id]/
│ │ └── page.js
│ └── page.js
└── layout.js
/blog/[slug]는 각 블로그 포스트를 위한 동적 라우트./products/[id]는 각 제품 페이지를 위한 동적 라우트.
중첩된 Dynamic Routes
app/
├── users/
│ ├── [userId]/
│ │ ├── posts/
│ │ │ ├── [postId]/
│ │ │ │ └── page.js
│ │ │ └── page.js
│ │ └── page.js
│ └── page.js
└── layout.js
/users: 모든 사용자 목록/users/[userId]: 특정 사용자의 프로필/users/[userId]/posts: 특정 사용자의 모든 게시물/users/[userId]/posts/[postId]: 특정 사용자의 특정 게시물
'FE > Next' 카테고리의 다른 글
| [SideProject] NewsMorn - AI 뉴스요약 (0) | 2025.03.28 |
|---|---|
| #0. Next 시작하기 (4) | 2025.01.22 |