React에서 서버 사이드 렌더링을 지원해주는 프레임워크이다.
ttv 문제: 클라이언트 렌더링의 경우 모든 js 파일을 로드하고 사용자는 웹을 보게된다. 이때까지 사용자는 많은 시간을 대기해야 한다다.
seo 문제: 클라이언트 사이드의 경우 첫 랜더링시 빈html파일을 받아오므로 검색엔진이 데이터가 없는줄알고 나가버린다.
서버부하: 페이지 이동시마다 서버에 데이터를 요청하므로 이는 곧 서버부하로 이어진다
ttI 문제: 서버에서 랜더링해서 브라우저로 보여지므로 TTV가 빠른반면 ,브라우저에서 js를 로드하는 시간이 있으므로 TTI가 느림
사용자 경험: 페이지 이동마다 서버에 페이지를 받아오므로 새로고침이 발생하므로 인터렉션이 느리다
csr,ssr 각각의 단점의 해결책으로 나온게 next.js이다
서버에 페이지 접속을 요청하면 SSR 방식으로 HTML을 렌더링 해 온다(SSR).
그리고 브라우저에서 js를 다운로드하고 리액트를 실행한다. (→ 그래서 초기 로딩 성능도 개선하고, 이미 콘텐츠가 다 포함된 상태라 SEO도 가능함)
그리고 나서 사용자가 상호작용하면서 다른 페이지로 이동할 때는, SSR이 아닌 CSR 방식으로 브라우저에서 처리하는 것이다. (→ 그래서 CSR의 장점도 유지함)
// 설치
npx create-next-app
// 실행
npm run dev
개발 중 저장되는 코드는 자동으로 새로고침된다.
React에서는 라우터가 없어서 보통 react-router-dom을 사용해서 렌더링한다
하지만 Nest.js의 경우 별 다른 설정없이 Routing을 할 수 있다.
폴더 내부의 파일위치나 파일명에 따라서 routing을 할 수 있다.
다른 컴포넌트에 정의한 경우 다른 클래스와 겹쳐 오류를 발생할 수 있음으로 _app에서만 허용된다.
// _app.tsx
import "./globals.css";
function MyApp({ Component, pageProps }) {
return <Component ponent {...pageProps} />;
}
export default MyApp;
pages 폴더에 있는 파일은 해당 파일 이름으로 라우팅된다. (pages/page1.tsx -> localhost:3000/page1)
public 폴더도 pages의 폴더와 동일하게 라우팅 할수있다. 그러나 모든 사람이 페이지에 접근할 수 있으므로 지양하는게 좋다.
import Link from "next/link"; //client side navigation
import { useRouter } from "next/router";
import styles from '../styles/Navbar.module.css' //css module
function Navba() {
const router = useRouter(); //현재 위치한 location에 대한정보가 있음
console.log(router)
return <nav>
<Link href="/"><a className={router.pathname==="/" ? styles.active:""}>home</a></Link>
<Link href="about"><a className={router.pathname==="/about" ? styles.active:""}>about</a></Link>
</nav>;
}
export default Navba;
import { useEffect } from "react";
import { useRouter } from "next/router";
import posts from "../posts.json";
export default () => {
const router = useRouter();
const post = posts[router.query.id as string];
if (!post) return <p>noting</p>;
useEffect(() => {
router.prefetch("/test");
}, []);
return (
<>
<h1>{post.title}</h1>
<h1>{post.content}</h1>
<button onClick={() => router.push("test")}>go to Test</button>
</>
);
};
// 동적url
import { useRouter } from "next/router";
export default () => {
const router = useRouter();
return (
<>
<h1>post</h1>
<p>postid: {router.query.id}</p>
</>
);
};
import Router from 'next/router';
const greeting = 'hello'
// 해당 파일 경로와 보내고 싶은 props를 query형태로 보내준다.
const SendQuery = () => {
Router.push({
pathname: '/register',
query: { greetingK: '안녕', greetingE: greeting },
});
};
<button onClick={SendQuery}>클릭</button>
import { useRouter } from 'next/router';
// router를 이용해 data를 query를 통해 받아온다
const router = useRouter();
console.log('K : ', router.query.greetingK);
console.log('E : ', router.query.greetingE);
클라이언트 렌더링과 다르게 서버에서 html을 rendering한다.
타입스크립트 활용을 위해 웹팩을 만지거나 바벨을 만질 필요 없다. 타입스크립트를 설치하고
(npm i typescript @types/node @types/react), (npm run dev)만 하면 자동으로 tsconfig, next-end.d.ts가 생성되어 타입스크립트로 코딩이 가능해진다.
보통 페이지간 이동은 a 태그를 사용하나 next에서는 사용하지 않는다.
a 태그를 사용하면 처음 페이지에 진입시 번들 파일을 받고, a 태그에 의해 라우팅 되면 다시 번들 파일을 받기 때문이다. 또한 redux을 쓰시는 경우 라면 store의 state 값이 증발되는 현상도 일어난다. 그렇기 때문에 a 태그는 전혀 다른 사이트로 페이지를 이동시켜 다시 돌아오지 않는 경우만 사용하고, 그 이외에는 모두 Link 태그를 사용한다다.
import Link from "next/link";
const Index = () => (
<div>
<Link href="/blog">
<a>Blog</a>
</Link>
// 동적 link시 [] 사용
<Link href="/blog/[address]">
<a>Blog</a>
</Link>
</div>
);
dynamic component import는 react에서 lazy하게 component를 import 해오는 방식과 유사하다.
dynamic하게 처음 보여줘도 안되는 컴포넌트는 import 하지 않게 됨으로, 초기 화면 렌더링 속도를 상승시키기 위해 사용한다.(맨처음 보이는 컴포넌트라면 dynamic 메소드를 사용할 이유가 없음)
import React, { useState } from "react";
import dynamic from "next/dynamic";
const DynamicComponent = dynamic<{ nowTab: number }>(() =>
import("./DynamicComponent")
);
const Index = () => {
const [nowTab, setNowTab] = useState(0);
return (
<>
{nowTab === 0 && <div>0 tab</div>}
{nowTab === 1 && <DynamicComponent nowTab={nowTab} />}
</>
);
};
port Link from "next/link"; //client side navigation
import { useRouter } from "next/router";
import styles from '../styles/Navbar.module.css' //css module
function Navba() {
const router = useRouter(); //현재 위치한 location에 대한정보가 있음
console.log(router)
return <nav>
<Link href="/"><a className={`${styles.link} ${router.pathname==="/" ? styles.active:""}`}>home</a></Link>
<Link href="about"><a className={ `${styles.link} ${router.pathname==="/about" ? styles.active:""}`}>about</a></Link>
</nav>;
}
export default Navba;
// css module -> 브라우저에서 코드를열면 <nav className={styles.nav}>
// 이부분 className은 무작위로 선정됨 따라서 다른 컴포넌트에서 똑같은 클래스명을 지어도 충돌할 걱정이없음
// 클래스를 여러개쓸거면 ``사용
// 1개는 : className={router.pathname==="/" ? styles.active:""}
.active{
color: tomato;
}
.link{
text-decoration: none;
}
백그라운드에서 페이지를 미리 가져온다. 기본값은 true. 뷰포트에있는 모든 항목 (초기 또는 스크롤을 통해)이 미리 로드된다. 정적 생성 을 사용하는 JSON페이지는 더 빠른 페이지 전환을 위해 데이터가 포함 된 파일을 미리로드 한다.
이건 Link 컴포넌트를 사용해서 이뤄지는데, 링크 컴포넌트를 렌더링할때 형식으로 prefetch 값을 전달해주면 데이터를 먼저 불러온다음에 라우팅을 시작한다. 프로덕션 레벨에서만 이루어진다.
우리는 페이지의 header로 다음과 같은 정보를 추가할 수 있습니다.
import Head from "next/head";
export default () => (
<div>
<Head>
<title>새로 만들어진 타이틀 입니다</title>
</Head>
<div>...</div>
</div>
);
next/head로 부터 Head 컴포넌트를 받아 모든 컴포넌트에서 사용할 수 있다.
next.js가 해당 컴포넌트가 mount 할때, Head내 태그들을 페이지의 HTML의 Head에 포함 시킨다. 마찬가지로 unMount 할때, 해당 태그를 제거한다.
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />;
}
export default MyApp;
meta 태그를 정의하거나, 전체 페이지에 관려하는 컴포넌트이다.
// pages/_document.tsx
import Document, { Html, Head, Main, NextScript } from "next/document";
export default class CustomDocument extends Document {
render() {
return (
<Html>
<Head>
// 모든페이지에 아래 메타테크가 head에 들어감, 루트파일이기에 가능한 적은 코드만 넣어야함
<meta property="custom" content="123123" />
</Head>
<body>
<Main />
</body>
<NextScript />
</Html>
);
}
}
라우팅 경로를 설정하지 않더라도 에러가 발생한다면 에러 페이지로 넘어가게 된다.
추가적으로 에러 상황에 따라서 500, 404 등도 추가할 수 있다
/pages/404.js
import Header from 'header.js';
import Footer from 'footer.js';
export default function Custom404() {
return (
<>
<Header />
<h1>Custom 404 Error Page</h1>
<Footer />
</>
)
}