Font는 웹 사이트 디장니에 중요한 역할을 하지만 Font 파일을 갖고 와서 로드해야 하는 경우 프로젝트에서 사용자 정의 Font를 사용하면 성능에 영향을 줄 수 있다
Next.js에서는 Font 모듈을 사용할 때 응용 프로그램의 Font를 자동으로 최적화 한다.
빌드 타임에서 Font파일을 다운로드 하고 다른 static asset들과 함게 호스팅한다.
이로 인해서 사용자가 프로그램에 방문할 때 성능에 영향을 줄 Font 다운로드는 발생하지 않는다.
이미지
Next.js는 최상위 /public폴더에서 이미지와 같은 static asset을 제공할 수 있다.
일반 HTML을 사용하면 다음과 같이 이미지를 추가할 수 있다.
<img src="/hero.png" alt="Screenshots of the dashboard project showing desktop version"/>
주의 할점
이미지가 다른 화면 크기마다 반응하는지 확인하십시오.
다른 장치마다 특정 이미지 크기
이미지가 로드됨에 따라서 레이아웃 이동
웹페이지를 표시할 때 지금 화면에 보이지 않는(스크롤해야 보이는) 이미지는 바로 다운로드하지 말고, 사용자가 스크롤해서 그 이미지가 화면 근처로 올 때 로드해라.
추가 하기
/public폴더 내부에 /hero-desktop.png가 존재한다면 아래와 같이 추가할 수 있다.
import Image from 'next/image';<Image src="/hero-desktop.png" width={1000} height={760} // display: none on mobile, block on desktop // tailwind의 속성으로 크기에 맞춰서 이미지를 숨기한다. className="hidden md:block" alt="Screenshots of the dashboard project showing desktop version" />
Next.js에서 갖고 오는 이미지 태그를 사용하는 것
일반적인 Img태그보다 더 많은 기능들을 제공한다.
import Image from 'next/image';
레이아웃 및 페이지 생성
Nested 라우팅
Next.js에서는 파일 시스템(폴더) 기반 라우팅을 사용하므로, 폴더 구조가 곧 중첩 라우트를 형성합니다. 각 폴더는 하나의 라우트 세그먼트가 되어 URL의 세그먼트와 대응됩니다
flowchart LR
───────────── ② URL 경로 트리
subgraph "🌐 URL path"
direction TB
URL_ROOT(["/"])
URL_DASH(["dashboard"])
URL_INV(["invoices"])
URL_ROOT --> URL_DASH --> URL_INV
end
{ init: { "theme": "base" } } ───────────── 파일 시스템 (좌측)
subgraph FS ["📁 File system"]
direction TB
A["app/"]
A_root["page.tsx"]
A --> A_root
A_dash["dashboard/"]
A_page2["page.tsx"]
A --> A_dash --> A_page2
end
style FS fill:#ffffff,stroke:#2563eb,stroke-width:1px
───────────── 매핑
A_root == maps to ==> U_root
A_page2 == maps to ==> U_dash
레이아웃
export default function Layout({ children }: { children: React.ReactNode }) { return ( <SideNav /> ... /* 이런 방식으로 children을 활용해서 하위 컴포넌트 목록을 받을 수 있다.*/ <div className="flex-grow p-6 md:overflow-y-auto md:p-12"> {children}</div> </div> );}
이 children은 페이지가 될 수도 있고 또 다른 레이아웃이 될 수도 있다.
듀토에서 /dashboard 폴더 안의 페이지들은 자동으로 <Layout /> 내부에 중첩되어 다음과 같이 렌더링됩니다.
부분 렌더링
Next.js에서 레이아웃을 사용하면 네비게이션 시 하위 컴포넌트만 다시 그려진다.
레이아웃에 저장한 React 상태(토글, 폼 입력, Context 등)가 리셋되지 않는다
이를 부분 렌더링이라고 부른다.
루트 레이아웃
루트 레이아웃 안에 넣은 UI는 모든 페이지에서 공유된다.
특정 폴더 내부에서 적용한 레이아웃 app/dashboard/layout.tsx는 dashboard 전용 레이아웃이다.
페이지 간 탐색
네비게이션의 최적화
일반적으로 <a>태그를 사용하는데 이러면 각 페이지를 탐색할 때 마다 전체 페이지 새로고침이 발생한다.
<Link> 컴포넌트
Next.js에서는 페이지를 이동할 때 Link컴포넌트를 사용할 수 있다.
Link컴포넌트는 자바스크립트를 사용해서 client side navigation을 허용한다.
Automatic code-splitting and prefetching
정의
네비게이션 경험을 향상 시키기 위해서 페이지 단위로 분할한다.
전통적인 React SPA(Single Page Application)에서는 모든 페이지의 코드를 한 번에 브라우저로 내려보냅니다. 그래서 첫 페이지 로딩이 느려질 수 있습니다
반면 Next.js는 사용자가 특정 페이지에 접근할 때 그 페이지에 필요한 코드만 로드합니다.
이 방식 덕분에 초기 로딩 속도가 빨라지고, 불필요한 코드 파싱이 줄어든다.
장점
각 페이지가 독립적으로 동작하므로 어떤 페이지에서 에러가 발생해도, 다른 페이지에는 영향을 미치지 않는다.
전체 애플리케이션이 한 번에 깨지는 현상을 줄일 수 있다.
사전 로딩
Next.js는 사용자가 곧 방문할 가능성이 높은 페이지의 코드를 미리 백그라운드에서 다운로드한다.
사용자가 실제로 그 페이지로 이동할 때는 이미 코드가 준비되어 있어 페이지의 로딩 시간이 빨라진다.
Showing active links
'use client'import { usePathname } from 'next/navigation';
use client는 Next.js 13 이상에서 도입된 특별한 지시문이다.
이 코드를 파일의 맨 위에 작성하면, 해당 파일(또는 컴포넌트)이 클라이언트 컴포넌트임을 Next.js에 명시한다.
이 컴포넌트는 브라우저(클라이언트)에서 실행되어야 하며, 브라우저에서만 동작하는 React 훅(예: useState, useEffect, usePathname 등)을 사용할 수 있습니다.
아래와 같이 모든 입력에 대해서 처리를 하려고 하면 트래픽이 몰렸을 때 입력값 처리가 감당이 불가능하다.
function handleSearch(term: string) { console.log(`Searching... ${term}`); const params = new URLSearchParams(searchParams); if (term) { params.set('query', term); } else { params.delete('query'); } replace(`${pathname}?${params.toString()}`);}
해결 방법
이를 해결하기 위해서 Debounce를 적용해서 서버로 데이터를 전송하는 타이머를 적용해서 한 번에 많은 요청이 들어오지 않도록 막는다.
아래는 use-debounce를 사용한 예시
// ...import { useDebouncedCallback } from 'use-debounce';// Inside the Search Component...const handleSearch = useDebouncedCallback((term) => { console.log(`Searching... ${term}`); const params = new URLSearchParams(searchParams); if (term) { params.set('query', term); } else { params.delete('query'); } replace(`${pathname}?${params.toString()}`);}, 300);
Mutation 데이터
React Server Action이란
비동기 코드를 직접 서버에서 실행할 수 있게 해주는 최신 기능
기존에는 데이터를 변경하거나 서버와 통신하려면 별도의 API 엔드포인트를 만들어야 했지만 Server Action을 사용하면 React 컴포넌트 안에서 바로 서버 코드를 작성하고 실행할 수 있다.
특징
서버에서 직접 실행
API 엔드포인트 불필요
외부로 데이터를 노출하지 않아서 보안이 강화된다
서버에서 실행되는 함수는 클라이언트로 전송되지 않으므로 프론트엔드 번들 크기가 줄어들고 초기 로딩 속도가 빨라진다.
폼(Form)과의 자연스로운 통합
Form with Server action
// Server Componentexport default function Page() { // Action async function create(formData: FormData) { 'use server'; // Logic to mutate data... } // Invoke the action using the "action" attribute return <form action={create}>...</form>;}
redirect 및 revalidatePath
redirect
redirect('/dashboard/invoices')
위의 예를 들어, 새로운 인보이스를 만든 뒤 목록 페이지로 자동 이동시키고 싶을 때 사용한다.
서버 컴포넌트 또는 서버 액션에서 사용한다.
클라이언트에서 URL이 즉시 변경되고, 해당 경로로 이동한다.
revalidatePath
revalidatePath('/dashboard/invoices')
지정된 경로의 캐시를 무효화한다.
위의 예를 들어, 새로운 인보이스를 추가하거나 기존 데이터를 수정/삭제한 후, 변경된 내용을 바로 반영하고 싶을 때 사용한다.