웹 사이트는 일반적으로 많은 페이지들을 갖고 있다. 어플리케이션에 페이지를 추가하는 방법에 대해 알아보자.
Next.js에서 페이지는 pages
디렉토리의 파일에서 가져온 리액트 컴포넌트다. 페이지들은 파일 이름을 기반으로 경로와 연결된다. 즉, 단순히 pages
디렉토리의 하위 js 파일을 만들면 그 경로가 URL 경로가 된다.
file name | route |
---|---|
pages/index.js | / |
pages/posts/first-post.js | posts/first-post |
웹사이트의 페이지를 연결해야 할 때 우리는 <a>
태그를 사용한다. Next.js에서는 어플리케이션의 페이지들을 연결하기 위해 next/link
의 Link
컴포넌트를 사용한다.
Link
는 Next.js에서 SPA를 매우 쉽게 구현할 수 있도록 도와주는 도구다. SPA는 하나의 페이지에서 모든 작업을 처리하는 앱으로, 서버로부터 가져올 데이터가 있다면 ajax 같은 방법을 이용하여 동적으로 로딩한다.
import Link from 'next/link';
<h1 className='title'>
Read <Link href='/post/first-post'>this page!</Link>
</h1>
Link
컴포넌트는 같은 Next.js 어플리케이션의 두 페이지간 클라이언트 측 탐색을 허용한다. 클라이언트 측 탐색은 자바스크립트를 이용한 페이지 전환을 의미한다.
Next.js는 자동으로 코드 분할을 제공한다. 따라서 페이지에 필요한 것만 불러온다. 즉. 홈페이지가 렌더링 되었을 때, 다른 페이지의 코드는 초기에 제공되지 않는다. 이는 홈페이지가 수백개의 페이지를 갖고 있어도 빠르게 로드할 수 있음을 의미한다.
요청한 페이지에 필요한 코드만 불러온다는 것은 페이지가 독립적이다는 것이다. 어떤 페이지가 에러를 던지더라도 어플리케이션의 나머지는 여전히 잘 작동한다.
또한 Next.js는 Link
컴포넌트가 브라우저의 뷰포트에 나타나면 자동으로 백그라운드에서 연결된 페이지에 대한 코드를 프리패치한다. 링크를 클릭하면 대상 페이지의 코드를 이미 백그라운드에서 불러왔기 때문에 페이지 전환이 즉각적으로 이루어진다.
Next.js는 최상위 레벨의 public
디렉토리에서 이미지 같은 정적 자산들을 제공한다.
일반 HTML은 다음과 같이 이미지를 추가한다.
<img src='/images/profile.jpg' alt='Kim Rina' />
하지만 이 방법은 다음을 수동적으로 처리해야 한다.
Next.js는 이를 컨트롤할 수 있는 Image
컴포넌트를 제공한다.
next/image
는 HTML의 <img>
태그를 확장한 것으로 기본적으로 이미지 최적화를 제공한다. 이미지 크기 조정을 제공하여 뷰포트가 더 작은 장치에 큰 이미지를 전달하는 것을 막을 수 있다.
빌드 시에 이미지를 최적화하는 대신, Next.js는 수요가 있을 때 이미지를 최적화한다. 즉, 이미지가 천만 개가 있다하더라도 빌드 시간이 늘어나지 않는다.
import Image from 'next/image';
const YourComponent = () => (
<Image
src='/images/profile.jpg'
height={200}
width={200}
alt='Kim Rina'
/>
);
HTML의 <title>
태그처럼 페이지의 메타데이터를 수정하고 싶다면 어떻게 해야 할까?
title
은 웹 페이지의 제목을 나타내는 태그다. 웹 페이지 본문에는 보이지 않으며, 브라우저 탭이나 개발자 도구 등에서 확인할 수 있다.
import Link from 'next/link';
import Head from 'next/head';
export default function FirstPost() {
return (
<>
<Head>
<title>First Post</title>
</Head>
<h1>First Post</h1>
<h2>
<Link href='/'>Back to home</Link>
</h2>
</>
);
}
일반적으로 third-party 스크립트는 사이트에 새로운 기능을 추가하기 위해 사용한다. next/script
는 HTML의 <script>
태그를 확장한 것으로 추가적인 스크립트를 가져오고 실행할 때 최적화된다.
import Link from 'next/link';
import Head from 'next/head';
import Script from 'next/script';
export default function FirstPost() {
return (
<>
<Head>
<title>First Post</title>
</Head>
<Script
src='https://connect.facebook.net/en_US/sdk.js'
strategy='lazyOnload'
onLoad={() => {
console.log('script loaded');
}}
/>
<h1>First Post</h1>
<h2>
<Link href='/'>Back to home</Link>
</h2>
</>
);
}
strategy
: 언제 third-party 스크립트를 로드할지 조절한다.
파일 구조를 살펴보면 두 개의 CSS 파일을 포함한styles
폴더를 볼 수 있다.
globals.css
: global stylesheetHome.module.css
: CSS moduleCSS 모듈은 컴포넌트 레벨의 스타일을 적용할 때 사용한다. CSS 클래스를 불러와 사용할 때, 클래스명을 고유한 이름으로 자동 변환해줌으로써 CSS 클래스명이 서로 중첩되는 현상을 방지해주는 기술이다.
특정 모듈만을 위한 CSS 파일 : [모듈명].module.css
CSS 모듈을 적용한 파일에 작성된 클래스는 아래 코드처럼
styles
객체를 활용하여 프로퍼티 형식을 참조할 수 있다.
import styles from './layout.module.css';
export default function Layout({ children }) {
return <div className={styles.container}>{children}</div>
}
브라우저의 개발자 도구에서 HTML을 살펴보면 Layout
클래스 이름이 다음과 같은 것을 알수 있다. layout_container__...
이처럼 CSS 모듈은 고유의 클래스 이름을 생성한다. 즉, CSS 모듈을 사용하면 클래스 이름 충돌에 대해 걱정할 필요가 없다.
모든 페이지에 CSS 파일을 로드하고 싶다면 어떻게 해야할까? 글로벌 CSS를 불러오려면 pages/_app.js
파일을 생성해야 한다.
export default function App({ Component, pageProps }) {
return <Component {...pageProps} />;
}
_app.js
는 어플리케이션의 모든 페이지를 감싸는 최상위 리액트 컴포넌트다. Next.js는 pages/_app.js
에 글로벌 CSS 파일을 추가하여 사용할 수 있다.
글로벌 CSS 파일은 어디에나 생성할 수 있고 어느 이름이나 가능하다.
pages/_app.js
를 업데이트 할 때는 서버를 재시작해야 한다.
clsx
library to toggle classesclsx
는 클래스 이름을 쉽게 전환할 수 있는 라이브러리다.
npm install clsx
success
와 error
타입을 갖는 Alert
컴포넌트를 만들어보자. sucess
는 초록색 텍스트, error
는 빨간색 텍스트를 표시한다.
/* alert.module.cs */
.success {
color: green;
}
.error {
color: red;
}
import styles from './alert.module.css';
import { clsx } from 'clsx';
export default function Alert({ children, type }) {
return (
<div
className={clsx({
[styles.success]: type === 'success',
[styles.error]: type === 'error',
})}
>
{children}
</div>
);
}
이제 외부 데이터에 의존하는 페이지 경로를 알아보자. Next.js를 사용하면 외부 데이터에 의존하는 경로가 포함된 페이지를 정적으로 생성할 수 있다.
그림처럼 id라는 외부 데이터에 의존하는 posts
경로를 만들어보자. 먼저, pages/posts
의 하위에 [id].js
이름의 페이지 파일을 생성한다.
export default function Post() { // post page
return <> ... </>
}
다음으로 페이지와 함께 getStaticPaths
함수를 내보낸다. 해당 함수는 가능한 id
리스트를 반환한다.
export default function Post() { ... }
export async function getStaticPaths() { ... }
마지막으로 getStaticProps
를 구현한다.
SSG와 getStaticProps
export default function Post() { ... }
export async function getStaticPaths() { ... }
export async function getStaticProps({ params }) { ... }
api 경로를 사용하면 어플리케이션 내에 api 엔드포인트를 생성할 수 있다. 이를 구현하기 위해서는 pages/api
디렉토리 내에 함수를 생성해야 한다.
api/hello
를 만들기 위해서는 pages/api
내부에 hello.js
이름의 파일을 생성한다.
export default function handler(req, res) {
res.status(200).json({text: 'Hello'});
}
참고자료
ofcourse : title 태그
TCP School : CSS Module
NEXT.js : Navigate Between Pages
NEXT.js : Dynamic Routes