Next.js는 Vercel 사에서 만든 react기반 프레임워크(framework) 입니다.
초기에 NextJS는 SSR을 위한 리액트 기반 프레임워크로 시작됐으며 현재는 단순 SSR을 위한 프레임워크가 아니라 full-stack Web application을 개발하기 위한 프레임워크라고 정의할 수 있을 정도로 많은 발전을 했습니다.
그래서 NextJS 홈페이지에서도 ‘The React Framework for the Web’이라고 타이틀을 내걸고 있는 것을 볼 수 있습니다.
풀스택 프레임워크답게 API 기능 및 서버 컴포넌트를 통한 서버측 기능과, React 컴포넌트를 통한 클라이언트 기능으로 나뉘어 통합 제공하는 프레임워크입니다. 현재 node.js 기반의 안정적인 풀스택 프레임워크로 사랑받고 있으며, SSG 방식 또한 제공하고 있습니다.
react 공식 시작하기 문서를 열람했을 때 가장 먼저 이 NextJS 프레임워크를 통한 시작하기가 가장 상단에 표시될 정도로 React의 거의 대표급 프레임워크라고 볼 수 있습니다.
File Page Router(기존)와 13부터 안정화된 폴더 방식의 App Router(아래 참조) 두가지 라우팅 방식을 지원하며, 라우팅 규칙이 충돌하지 않는 내에서 두 라우팅 방식을 모두 사용할 수 있습니다. 또한, 앱 라우터가 완전히 안정화한다 해도, 기존 페이지 라우팅 방식은 지속적으로 관리하기 때문에 차기 개발 시에도 취향이나 정책에 맞게 적절히 사용하면 될 것 같습니다.
Next.js는 React 기반의 웹 프레임워크로, 특히 서버 사이드 렌더링(SSR)과 정적 사이트 생성(Static Site Generation, SSG)을 지원하여 풀스택 개발을 용이하게 합니다. 여기서 "풀스택"이라는 용어는 다양한 소프트웨어 계층을 아우르는 개발을 의미합니다.
Next.js가 풀스택 프레임워크로 간주되는 이유는 다음과 같습니다:
1️⃣ 클라이언트 사이드 코드 (Frontend)
NextJS는 React를 기반으로 하며, 클라이언트 사이드에서 사용되는 리액트 컴포넌트를 구축하는 데 특화되어 있습니다. 이는 사용자 인터페이스를 만들고 관리하는 데 매우 효과적입니다.
2️⃣ 서버 사이드 렌더링 (SSR)
Next.js는 서버 사이드 렌더링을 지원하여 서버에서 페이지를 렌더링하고 결과를 클라이언트에 전달할 수 있습니다. 이를 통해 초기 로딩 성능을 개선하고 검색 엔진 최적화(SEO)를 향상시킬 수 있습니다.
3️⃣ API 라우팅 및 서버리스 함수
Next.js는 API 라우팅을 제공하여 서버리스 함수를 구현할 수 있게 해줍니다. 이를 통해 서버리스 아키텍처를 채택하거나 백엔드 API를 쉽게 구축할 수 있습니다.
API Routes는 NextJS 애플리케이션 내에 API를 심어서 배포할 수 있게 해주는 기능입니다.
FE 코드가 아닌 DB에서 데이터를 가져오거나 쿼리를 날리는 등의 백엔드 코드를 심어서 함께 배포할 수 있습니다. 앞에서 Next.js를 full-stack Web application을 개발하기 위한 프레임워크라고 정의한 이유가 바로 이 부분 때문입니다.
4️⃣ 정적 사이트 생성 (SSG)
Next.js는 정적 사이트 생성을 지원하여 사전에 페이지를 빌드하여 정적 파일로 제공할 수 있습니다. 이를 통해 성능을 향상시키고, 캐싱을 통한 더 빠른 로딩을 가능케 합니다.
5️⃣ data Fetching
데이터를 가져오는 다양한 방법을 제공하며, 서버 측에서 또는 클라이언트 측에서 데이터를 처리할 수 있습니다.
위와 같은 다양한 기능을 기반으로 NextJS는 프론트엔드와 백엔드 개발을 모두 다루며, 클라이언트-서버 구조부터 데이터 처리, 라우팅, 그리고 렌더링에 이르기까지 모든 측면을 다룰 수 있기 때문에 "풀스택 프레임워크"라고 불리고 있습니다.
NextJS는 React를 기반으로 한 웹 프레임워크로서 React의 기능을 보완하고 확장한 것입니다. 따라서 엄밀히 따지면 react와 NextJS가 동일한 것은 아니지만 NextJS에서 활용하는 문법은 거의 react와 99% 유사하다고 볼 수 있습니다.
1️⃣ 컴포넌트의 구조
모두 컴포넌트 기반의 구조를 가지고 있습니다. 컴포넌트는 function 형태로 작성될 수 있고, 클래스 기반의 컴포넌트도 사용 가능합니다.
// React 컴포넌트
import React from 'react';
function MyComponent() {
return <div>Hello, React!</div>;
}
// Next.js 페이지 컴포넌트
import React from 'react';
function MyPage() {
return <div>Hello, Next.js!</div>;
}
export default MyPage;
2️⃣ JSX, TSX의 사용
JSX(JavaScript XML)는 NextJS와 React에서 동일하게 사용됩니다. 이를 통해 JavaScript 코드에서 HTML과 비슷한 구문을 사용할 수 있습니다.
3️⃣ Props 전달
컴포넌트에 속성(props)을 전달하는 방법도 동일합니다.
4️⃣ life-cycle method
React의 라이프사이클 메서드와 관련된 개념은 NextJS에서도 동일하게 적용됩니다.
컴포넌트가 생성되고 업데이트되며 소멸될 때 일어나는 다양한 이벤트와 단계를 나타냅니다. React 16.3 이전에는 라이프사이클 메서드가 클래스 컴포넌트에서만 사용되었지만, React 16.3 이후부터는 함수 컴포넌트에서도 Hooks를 통해 라이프사이클과 관련된 기능을 사용할 수 있게 되었습니다.
라이프사이클 메서드와 관련된 개념은 아래와 같습니다 :
마운팅(Mounting)
constructor()
컴포넌트가 생성될 때 호출되는 메서드, 초기 설정을 수행하는 데 사용
static getDerivedStateFromProps()
props로부터 상태를 동기적으로 설정하는 메서드, 컴포넌트가 마운트되기 전에 호출
render()
컴포넌트의 UI를 렌더링하는 메서드
componentDidMount()
컴포넌트가 화면에 나타난 후 호출되는 메서드, 비동기 작업이나 데이터 로딩 등에 활용
업데이트 (Updating)
static getDerivedStateFromProps()
마운트 단계와 함께 업데이트 단계에서도 호출되는 메서드, props에 의해 상태를 업데이트
shouldComponentUpdate()
컴포넌트가 리렌더링을 해야 할지 결정하는 메서드, 성능 최적화를 위해 사용
render()
UI를 업데이트하는 메서드
getSnapshotBeforeUpdate()
DOM에 변화가 일어나기 직전에 호출되는 메서드, 이전의 상태나 속성에 대한 정보를 얻음
componentDidUpdate()
컴포넌트의 업데이트가 완료된 후에 호출되는 메서드, 이전 상태나 속성에 접근하여 처리
언마운팅 (Unmounting)
componentWillUnmount()
컴포넌트가 파괴되기 전에 호출되는 메서드, 리소스 해제나 정리 작업 등에 활용
오류 처리 (Error Handling)
static getDerivedStateFromError()
자식 컴포넌트의 렌더링 중 에러가 발생할 때 호출되는 메서드, 상태를 업데이트하여 에러를 처리
componentDidCatch()
자식 컴포넌트에서 발생한 에러를 처리하는 메서드, 로깅이나 에러 리포팅 등에 사용
// React 클래스 컴포넌트
import React, { Component } from 'react';
class MyComponent extends Component {
componentDidMount() {
console.log('Component did mount.');
}
render() {
return <div>Hello, React!</div>;
}
}
// Next.js 클래스 페이지 컴포넌트
import React, { Component } from 'react';
class MyPage extends Component {
componentDidMount() {
console.log('Page component did mount.');
}
render() {
return <div>Hello, Next.js!</div>;
}
}
export default MyPage;
// React 함수형 컴포넌트
import React, { useEffect } from 'react';
const MyFunctionalComponent = () => {
useEffect(() => {
// 컴포넌트가 마운트되었을 때 실행
console.log('Functional component mounted.');
// 컴포넌트가 언마운트되기 전에 실행되는 cleanup 함수
return () => {
console.log('Functional component will unmount.');
};
}, []); // 빈 배열을 전달하여 컴포넌트가 마운트될 때 한 번만 실행
return <div>Hello, Functional Component!</div>;
};
export default MyFunctionalComponent;
// Next.js 함수형 컴포넌트
import React, { useEffect } from 'react';
const MyNextComponent = () => {
useEffect(() => {
// 페이지가 로드될 때 실행
console.log('Next.js functional component loaded.');
// 페이지가 언로드되기 전에 실행되는 cleanup 함수
return () => {
console.log('Next.js functional component will unmount.');
};
}, []); // 빈 배열을 전달하여 페이지 로드 시 한 번만 실행
return <div>Hello, Next.js Functional Component!</div>;
};
export default MyNextComponent;
필수는 아니지만 권장 사항이긴 합니다.
NextJS와 타입스크립트를 함께 사용하게 되면 아래와 같은 장점이 있기도 합니다:
1️⃣ 기본 지원
Next.js 프로젝트를 생성할 때 next 명령어로 타입스크립트 옵션을 선택할 수 있습니다. 이로써 프로젝트 초기 구성을 쉽게 시작할 수 있습니다.
→ npx create-next-app my-app -e with-typescript
타입스크팁트의 활용을 위해 웹팩을 만지거나 바벨을 만질 필요 없습니다. 초기 세팅에서 설치하지 못했다 하더라도 실행만 시키면 자동으로 tsconfig, next-end.d.ts가 생성되어 타입스크립트로 코딩이 가능해집니다.
TS를 설치하지 않고 실행시켜도 아래와 같은 메세지를 띄운 뒤 알아서 설치해줍니다. 🙂
It looks like you're trying to use TypeScript but do not have the required package(s) installed.
typescript를 사용하려고 하시는데 설치가 되어 있지 않네요. 설치 후 실행시키겠습니다.
2️⃣ 유연한 타입 선언
타입스크립트를 사용하면 변수, 함수, 컴포넌트 등에 명시적인 타입을 지정할 수 있습니다. 이는 코드의 가독성을 높이고 오류를 빠르게 찾아내는 데 도움이 됩니다.
3️⃣ IDE 지원
대부분의 통합 개발 환경(IDE)은 타입스크립트를 지원하며, 코드 자동 완성, 타입 체크, 디버깅 등의 기능을 통해 개발 생산성을 높일 수 있습니다.
4️⃣DefinitelyTyped
NextJS와 관련된 타입 정의는 DefinitelyTyped 프로젝트에서 관리되므로, 라이브러리와의 통합이 매우 쉽습니다.
5️⃣NextJS 특화 타입
NextJs는 자체적으로 제공하는 타입도 많이 있어서, 프레임워크에 특화된 타입을 활용할 수 있습니다.
결과적으로 NextJS와 TS는 함께 사용할 때 혜택을 많이 얻을 수 있습니다. 전반적으로 코드의 안정성을 높이고 개발 생산성을 향상시키는 데 도움이 됩니다.
다른 프레임워크의 만족도는 점차 떨어지는 반면 NextJS는 꾸준히 증가하고 있는 것을 확인할 수 있습니다.
개발자들이 꾸준히 찾고 있기도 하고 있으므로 NextJS를 선택하는 것이 비교적 안전한 길이 아닐까 싶습니다.🙂
React만 사용해서 프로젝트를 구현하다보면 새로고침을 했을 때 화면이 잠깐 하얗게 보이는 시점을 목격하신 적이 있을 겁니다.
//index.html (CSR)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Roboto+Condensed:ital,wght@0,100..900;1,100..900&display=swap" rel="stylesheet">
<title>project-name</title>
</head>
<body>
<div id="root"></div> ✔️
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
그건 위에 index.html
파일 중에서 체크된 부분 때문에 깜빡이는 현상이 보이게 됩니다. 이는 javascript 엔진에서 모든 소스 코드(source code)들을 다운받은 뒤 화면에 렌더(render)해줘야 하기 때문에 다운 받는 시간동안 화면이 비어 있게 되면서 발생하는 현상입니다.
이러한 특성은 즉시 화면을 볼 수 없으며 이는 SEO 최적화에도 좋지 않습니다.
검색 엔진들은 javascript를 실행시키지 않고 검사하기 때문에 빈 화면이라고 판단하고 최적화를 해주지 않기 때문입니다.
NextJs를 사용하게 되면 SSR이 가능해집니다.
NextJs의 application 안의 모든 page가 포함하는 component들은 NextJs가 서버에서 먼저 렌더를 진행합니다. BE에서 먼저 서버를 한 뒤에 클라이언트(client)에게 보여주기 때문에 처음 접속자가 페이지에 도착했을 때에는 따로 UI를 빌드하는데 JS가 필요하지 않게 됩니다.
1. NextJs가 javascipt를 미리 server에서 render
2. 이를 HTML로 변환해 browser에 전달
3. broswer가 client가 접속했을 때 해당 UI를 보여줌
사용자가 최초 HTML(initial HMTL)을 본 뒤에는 어떤 일이 일어날까?
React는 언제 활성화가 되는 걸까?
Hydration은 웹 애플리케이션에서 클라이언트 측 JS 코드를 실행하여 서버에서 전송된 정적인 HTML을 동적으로 변환하고 인터랙티브한 사용자 인터페이스를 생성하는 프로세스를 가리킵니다.
NextJs는 JS 코드들을 미리 server에 render하고 HTML로 변환해 browser에 전달합니다. 그러면 client가 페이지에 도달했을 때 browser는 server로부터 사전에 전달받은 HTML 파일을 보여줄 수 있게 됩니다. client가 HTML로만 구성된 페이지를 보는 사이 빠르게 단순한 HTML을 react application으로 초기화해 interactive한(상호작용 가능한, 생동감 있는) 페이지로 동작하게 합니다.
anchor 태그를 예로 들면, Link 태그로 작성을 했어도 HMTL로 전달될 때에는 그저 anchor 태그에 지나지 않습니다. 그래서 만약 큰 규모의 프로젝트를 react app으로 초기화하는 시간이 오래 걸리는 와중에 사용자가 미처 초기화가 이뤄지기 전에 Link로 작성된 태그를 클릭한다면, 그 태그는 아직 초기화가 되지 않았기 때문에 Link 태그로 작성이 되어 있어도 anchor 태그의 역할 밖에 하지 못합니다.
즉, react app으로 초기화가 되었을 때 비로소 Link 태그의 역할을 할 수 있게 되는 겁니다.
CSR의 경우 두 가지 문제를 갖고 있는데 이를 해결해주는 것이 SSR입니다.
1️⃣ 사이트 접속 시 대기 시간 발생
모든 js 파일을 로드하고 사용자는 웹을 보게됩니다. 이때까지 사용자는 많은 시간을 대기해야 합니다.
→ 서버에서 자바스크립트를 사전에 로딩함으로 클라이언트 측에서는 자바스크립트를 로딩하는 시간이 줄어들게 됩니다.
2️⃣ seo 최적화의 어려움
자바스크립트가 로드 되지 않은 경우 아무런 정보를 보이지 않습니다. 구글의 검색엔진의 경우 자바스크립트가 로드되지 않은 페이지를 스캔할 때 검색에 아무 페이지도 걸리지 않게 됩니다.
→ 서버 측에서 자바스크립트, html, css를 만들어 컨텐츠를 직접 업로드하기 때문에 검색엔진에 게시글이 걸리게 됩니다. 또한 meta 태그를 자유롭게 추가함으로 seo를 용이하게 할수 있습니다.
NextJS로 프로젝트를 진행하다보면 페이지에 따라 'use client'라는 걸 코드 최상단에 적어줘야 할 때가 있습니다. 이름 때문에
하고 오해할 수도 있지만 "use client"
가 적힌 페이지도 예외 없이 전부 이에 해당됩니다.
NextJS에서 "use client"가 적혀 있으면 client component, 없으면 server component입니다.
클라이언트 컴포넌트는 클라이언트 측에서 실행되는 페이지를 말합니다.
따라서 서버 측에서는 렌더링되지 않습니다.
클라이언트 컴포넌트는 기존의 React 컴포넌트와 동일한 동작을 가지며 주로 사용자 인터랙션(interaction)과 관련된 로직, UI 업데이트 등을 처리하는 데 사용됩니다.
1️⃣ 이벤트 핸들링 (Event Handling)
예를 들어, 버튼 클릭(onClick), 폼 제출(submit), 마우스 호버(mouseEnter) 등의 이벤트에 대한 핸들러를 정의하여 사용자와 상호작용할 수 있는 것을 말합니다.
2️⃣ 조건부 렌더링 (Conditional Rendering)
사용자 인터랙션에 따라 특정 조건에서만 UI를 렌더링하는 것을 말합니다. 조건부 렌더링을 사용하여 동적으로 UI를 업데이트하고 사용자에게 특정 상태 또는 조작을 시각적으로 전달할 수 있습니다.
3️⃣ 상태 관리 (State Management)
Redux, MobX, Recoil 등의 라이브러리를 사용하여 애플리케이션 전체에서 상태를 공유하고 업데이트하는 로직을 말합니다.
4️⃣ 애니메이션 (Animation)
React의 react-spring, framer-motion 등의 라이브러리를 사용하여 간단한 또는 복잡한 애니메이션을 추가할 수 있습니다.
5️⃣ 라우팅 (Routing)
사용자가 다른 페이지로 이동하는 경우, 라우팅 라이브러리를 사용하여 페이지 간 전환을 관리할 수 있습니다. React Router 등의 라이브러리는 사용자 경로와 관련된 동적인 UI 업데이트를 처리합니다.
위 6가지는 대표적인 사용자와 상호작용을 해야하는 로직 및 UI 업데이트와 관련이 있는 로직입니다. 이러한 경우에는 반드시 클라이언트 컴포넌트임을 명시해줘야 합니다. 처음 NextJS를 사용하면 어떤 기준인지 헷갈릴 수 있지만 괜찮습니다, 빠뜨려도 NextJS에서 경고로 알려주니까 그때 추가하면 됩니다.
react 공식 팀과 협력하고 있는 서버 컴포넌트는 말 그대로 컴포넌트를 서버에서 관리하여 서버가 가지는 기능(DB 및 파일, 보안 등)에 대해 React가 가지는 기능과 결합하여 서비스를 제공하는 매우 강력한 기능입니다.
아직 RFC 단계인 이 서버 컴포넌트의 구현체를 NextJS에서 앞서 제공하고 있는 것으로 이 기술이 React에 정식 출시되면 Remix 등의 다른 React 기반 프레임워크에서도 사용이 가능해질 것으로 보입니다.
아래 다이어그램은 중첩된 구성 요소(toggle.js)에서 onClick 및 useState을 사용해야 하는데 "use client"
지시어가 정의되지 않은 경우 오류가 발생함을 보여줍니다.
이는 기본적으로 이러한 API를 사용할 수 없는 서버에서 구성 요소가 렌더링되기 때문입니다. toggle.js
에서 지시문을 정의하면 React가 API를 사용할 수 있는 클라이언트에서 구성 요소와 해당 하위 항목을 렌더링하도록 지시할 수 있습니다.
NextJS 12에서 13.4로 안정화되면서 추가된 기능입니다.
기존의 파일 기반의 라우팅
에서 폴더 기반 라우팅
으로 바뀌면서 여기에 폴더 하위에 여러 기능을 조합하여 사용할 수 있는 강력한 앱 라우팅 기능
이 제공됩니다.
기존 파일 기반의 라우팅도 관리되고 있기 때문에 기존 파일 기반 라우팅의 사용 및 병행 사용이 가능합니다. 다만, 폴더 기반의 앱 라우팅 기능을 사용하기 위해서는 폴더 기반의 경로에 아래와 같이 각각의 역할이 담긴 파일명을 사용해야 하며 반드시 이름을 동일하게 작성해야 합니다.
layout.tsx
상위에서 중첩이 가능한 페이지 틀을 담당하는 상위 컴포넌트
page.tsx
주 페이지 내용을 표시하는 컴포넌트 (기존 파일 기반 컴포넌트 역할 계승)
error.tsx
페이지에서 오류가 발생할 경우 표시되는 컴포넌트
loading.tsx
<Suspense /> 컴포넌트의 fallback 속성과 동일한 원리로 페이지를 불러오기 전 표시할 컴포넌트
not-found.tsx
하위 페이지 탐색 시 없을 경우 표시할 페이지 컴포넌트
template.tsx
해당 페이지 및 하위 페이지 이동 시 상태를 다시 관리하는 상태가 된다.(기본적으로 NextJS에서 페이지 이동 시 상태가 유지)
주로 CSS 애니메이션의 종료, 회수해야 할 라이브러리가 존재하는 등의 목적이 있을 때 주로 사용된다.
빈 레이아웃처럼 컴포넌트를 작성하면 간단히 사용할 수 있다.
default.tsx
병렬 라우팅 경로에 직접 접속했을 경우 기본적으로 렌더링할 내용을 작성
기존 리액트 애플리케이션에서는 페이지들의 경로를 구성하고 라우팅(routing) 시키기 위해서 react-router
를 사용했습니다.
하지만 NextJS에서는 자체적으로 router가 내장되어 있기 때문에 정해진 폴더 구조에 맞춰서 컴포넌트를 배치해주기만 하면 됩니다. 특정 주소로 이동하는 페이지를 만들고 싶다면 폴더를 만들어 안에 page.tsx
파일을 생성해주면 routing을 할 수 있게 됩니다.
NextJS는 폴더가 중첩된 경로를 만드는 데 사용되는 파일 시스템 라우팅을 사용합니다. 각 폴더는 URL 세그먼트 에 매핑되는 경로 세그먼트를 나타냅니다.
중첩된 경로를 만들려면 폴더를 서로 중첩하고 page.tsx
그 안에 파일을 추가하면 됩니다.
layout.tsx
및 page.tsx
파일을 사용하여 각 경로에 대해 별도의 UI를 만들 수 있습니다.
layout.tsx
파일은 애플리케이션의 모든 페이지에서 사용할 수 있는 공유 레이아웃을 만드는 가장 좋은 방법입니다.
page.tsx
는 react 구성 요소를 내보내는 특수 NextJS 파일이며 경로에 액세스(access)하기 위해 반드시 필요합니다. app폴더는 react에서 src폴더와 마찬가지로 최상단 폴더입니다. 따라서 NextJS의 app/page.tsx
는 경로와 연결된 홈 페이지로 사용자(client)들이 페이지에 들어왔을 때 보는 루트 페이지입니다.
프로덕션 환경에서 브라우저의 뷰포트에 Link 구성 요소가 나타날 때 Next.js는 백그라운드에서 연결된 경로에 대한 코드를 자동으로 미리 가져옵니다. 사용자가 링크를 클릭하면 대상 페이지의 코드가 이미 백그라운드에 로드되어 페이지 전환이 거의 즉각적으로 이루어집니다!
태그는 anchor 태그 대신 Link 태그를 사용할 겁니다.
a 태그를 사용하게 될 경우 페이지 이동 시 페이지가 강제로 새로고침 되는데 이를 hard navigation이라고 합니다.
반면에 Link 태그를 사용하면 새로고침 없이 페이지 이동이 가능합니다.
이는 클릭이 발생 시 react가 가로 채서 페이지를 새로고침 없이 보여주기 때문이고 이를 client side only navigation이라고 합니다. 이 태그는 react에서도 제공되고 있지만 next에서도 따로 제공해주고 있으므로 import 시 next/link
로 가져올 수 있도록 합니다.
NextJs에서는 경로와 관련해서 usePathname
hook 함수를 사용할 수 있습니다.
usePathname
은 client components에서만 동작하기 때문에 코드의 최상단에 "use client"
를 파일의 최상단에 적어줘야 합니다.
npm install --save clsx
상태나 다른 조건에 따라 요소의 스타일을 조건부로 지정해야 하는 경우 clsx 라이브러리를 통해 쉽게 전환할 수 있습니다.
[조건]
import clsx from 'clsx';
// or
import { clsx } from 'clsx';
export default function Sample({ status }: { status: string }) {
return (
<p
className={clsx(
{
'bg-gray-100 text-gray-500': status === 'pending',
'bg-green-500 text-white': status === 'paid',
},
)}
>
// ... other codes ...
)}
/movies/:id
NextJs에서는 동적 라우팅을 폴더의 구조로 구현할 수 있습니다.
(movies)/movies/[id]/page.tsx
(movies)
로 되어 있기 때문에 /movies
경로로만 이동했을 때에는 아무 것도 보이지 않지만 특정 id 값을 뒤에 전달하게 되면 선택된 영화의 detail-page로 넘어갈 수 있도록 할 수 있습니다.
저 dataId 값은 해당 page의 props를 console에 찍어보면 알 수 있습니다.
터미널에 정상적으로 제가 적은 id 값이 찍히는 걸 확인할 수 있습니다.
params 뿐만 아니라 searchParams 값도 가져올 수 있습니다.
글꼴을 사용하면 브라우저가 처음에 대체 글꼴이나 시스템 글꼴로 텍스트를 렌더링한 다음 로드된 후 사용자 지정 글꼴로 교체할 때 레이아웃 변경이 발생합니다.
이 교체로 인해 텍스트 크기, 간격 또는 레이아웃이 변경되고 그 주위의 요소가 이동될 수 있습니다.
Next.js는 모듈을 사용할 때 애플리케이션의 글꼴을 next/font
를 통해 자동으로 최적화합니다. (build 시 글꼴 파일을 다운로드하고 다른 정적 자산과 함께 호스팅)
따라서 사용자가 애플리케이션을 방문할 때 성능에 영향을 미칠 수 있는 글꼴에 대한 추가 네트워크 요청을 하지 않아도 됩니다.
1️⃣ app/ui
폴더 하위에 fonts.ts
파일 생성
2️⃣ next/font/google
에서 글꼴 가져오기 → 하위 집합(subsets) 설정
import { Inter } from 'next/font/google';
export const inter = Inter({ subsets: ['latin'] });
3️⃣ app/layout.tsx
에 적용
import '@/app/ui/global.css';
import { inter } from '@/app/ui/fonts';
export default function RootLayout({children}: {children: React.ReactNode}) {
return (
<html lang="en">
<body className={`${inter.className} antialiased`}>{children}</body>
</html>
);
}
body에 inter 요소를 추가하면 해당 글꼴이 애플리케이션 전체에 적용
antialiased → 글꼴을 부드럽게 만드는 클래스 (tailwind)
애플리케이션의 특정 요소에 글꼴 적용 가능 → 공식 홈페이지 참고
1️⃣ 각각 export
글꼴을 내보내고, 가져와서 className필요한 곳에 적용하는 유틸리티 기능을 만드는 것입니다.
이렇게 하면 글꼴이 렌더링될 때만 미리 로드됩니다.
import { Inter, Roboto_Mono } from 'next/font/google';
// main font
export const inter = Inter({
subsets: ['latin'],
display: 'swap',
});
// sub font
export const roboto_mono = Roboto_Mono({
subsets: ['latin'],
display: 'swap',
});
이렇게 할 경우 Inter는 전역적으로 적용되는 상태에서 Roboto Mono는 필요에 따라 import해서 적용할 수 있습니다.
// app/layout.tsx
import { inter } from './fonts'
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" className={inter.className}>
<body>
<div>{children}</div>
</body>
</html>
)
}
// app/page.tsx
import { roboto_mono } from './fonts'
export default function Page() {
return (
<>
<h1 className={roboto_mono.className}>My page</h1> /* 특정 요소에만 적용 */
</>
)
}
2️⃣ CSS 변수 생성
// app/layout.tsx
import { Inter, Roboto_Mono } from 'next/font/google'
import styles from './global.css'
const inter = Inter({
subsets: ['latin'],
variable: '--font-inter',
display: 'swap',
})
const roboto_mono = Roboto_Mono({
subsets: ['latin'],
variable: '--font-roboto-mono',
display: 'swap',
})
export default function RootLayout({children}: {children: React.ReactNode}) {
return (
<html lang="en" className={`${inter.variable} ${roboto_mono.variable}`}>
<body>
<h1>My App</h1>
<div>{children}</div>
</body>
</html>
)
}
이렇게 사용하는 경우 Inter는 전역적으로 적용되며 Roboto Mono는 모든 h1 태그의 스타일에 지정됩니다.
// app/ui/global.css
html {font-family: var(--font-inter);}
h1 {font-family: var(--font-roboto-mono);}
3️⃣ 페이지마다 특정 font 적용하기
1️⃣ 크기 최적화(Size Optimization)
디바이스마다 적절한 사이즈의 이미지를 자동으로 제공하고, WebP 및 AVIF와 같은 최신 이미지 형식을 사용합니다.
2️⃣ 시각적 안정성(Visual Stability)
이미지 로드 전 placeholder를 제공하여 CLS(Cumulative Layout Shift) 방지합니다. 즉, 이미지가 로드될 때 레이아웃이 자동으로 전환되는 것을 방지합니다.
3️⃣ 더 빠른 페이지 로드(Faster Page Loads)
viewport에 들어왔을 때만 이미지를 로드하고, 작은 사이즈의 blur 이미지를 미리 로딩하여 사용자에게 더 빠른 페이지를 보여줄 수 있습니다.
Next/Image 컴포넌트에서 제공하는 대표적인 기능은 다음의 3가지입니다.
lazy loading이란 이미지 로드하는 시점을 필요할 때까지 지연시키는 기술을 의미합니다. 예시로 스크린 밖에 있는 이미지들은 로딩을 지연시키고 스크린 안에 있는 이미지만을 로드해 불필요한 대역폭 사용을 줄이고 필요한 이미지만 빠르게 로드하는 것을 들 수 있습니다.
Next/Image를 사용하게 되면 자동으로 lazy loading이 적용됩니다. 중요한 이미지 일부에 lazy loading을 적용하고 싶지 않은 경우에는 해당 기능을 끌 수도 있습니다. Image 컴포넌트의 priority
라는 prop을 true로 설정하거나, loading = eager
을 설정하면 됩니다. 둘 중에선 priorty 값을 설정하는 것이 더 권장되는 방식입니다.
Next/Image는 디바이스 크기 별로 srcSet
을 미리 지정해두고, 사용자의 디바이스에 맞는 이미지를 다운로드할 수 있게 지원합니다. layout prop 설정에 따라 어떤 srcSet 목록이 변경됩니다. 또한 NextJS는 이미지를 webp와 같은 용량이 작은 포맷으로 이미지를 변환해서 제공합니다.
사용자의 디바이스에 맞는 이미지 사이즈를 만들고, 용량이 작은 webp 포맷으로 변환하는 작업은 이미지에 대한 최초 요청 시에 NextJS 서버에서 진행됩니다. 이후 요청에는 캐시가 만료될 때까지 캐시 된 이미지가 제공되기 때문에 첫 번째 요청보다 훨씬 빠르게 이미지를 서빙할 수 있습니다. 캐시가 만료된 후 요청이 들어오면 오래된 이미지를 우선 제공하고, 백그라운드에서 이미지 최적화를 다시 진행합니다.
어떤 웹 사이트에 방문했을 때 이미지가 로드되는 동시에 이미지가 갖는 크기만큼 영역이 늘어나며 레이아웃이 흔들리는 것을 목격해보신 적 있나요?
이를 CLS(Cumulative Layout Shift)
라고 부릅니다.
Next/Image는 레이아웃이 흔들리는 현상을 방지하기 위해 placeholder를 제공합니다. 이미지가 로드되기 전에도 이미지 높이만큼 영역을 표시해서 이미지가 로드된 후에 레이아웃이 흔들리지 않도록 하는 것입니다. placeholder는 빈 영역 또는 blur 이미지(로컬 이미지의 경우 build 타임에 생성, 리모트 이미지의 경우에는 base64로 인코딩된 data url 을 지정해 줘야 함)로 적용할 수도 있고, 직접(custom) 설정할 수도 있습니다.
// local image(로컬 이미지)
import Image from 'next/image'
import profilePic from './me.png'
export default function Page() {
return (
<Image
src={profilePic}
alt="Picture of the author"
// width={500} automatically provided
// height={500} automatically provided
// blurDataURL="data:..." automatically provided
// placeholder="blur" // Optional blur-up while loading
/>
)
}
// remote image(원격 이미지)
import Image from 'next/image'
export default function Page() {
return (
<Image
src="https://s3.amazonaws.com/my-bucket/profile.png"
alt="Picture of the author"
width={500}
height={500}
/>
)
}
-- 임시 저장해가며 작성 중 --
5분 안에 빠른 설명을 원한다 👉 codingApple
실제 적용해보며 습득하고 싶다 👉 nomadcoders
단계 별로 꼼꼼히 톱아보고 싶다 👉 officialTutorial
nomad coders 👉 movie-trailer
official tutorial 👉 next-tutorial