Nextjs는 기본적으로 모든 페이지를 사전 렌더링합니다. (Pre-rendering) 이라고 합니다. Pre-rendering에는 정적 생성과 Server Side Rendering ( SSR ) 이 있는데, HTML 파일을 언제 생성하는데의 차이가 있습니다. 또한 검색엔진최적화 ( SEO ) 에도 용이합니다.
Pre-rendering 이 없다면? Javascript가 실행되기 전까지 빈 화면이였다가 javascript가 실행되고 나서 페이지가 완성됩니다.
개발자 도구에서 javascript 비활성화 를 이용해서 알 수 있습니다. 반대로 Pre-rendering을 사용하면 초기에 사전에 만들어진 HTML이 생성되고 이후에 Javascript가 실행됩니다. 이것을 Hydration 이라고 하며, 정적인 요소에 동작을 불어넣는것이라 합니다.
정적 생성은 프로젝트가 빌드할때 HTML 파일을 생성하고 요청이 그 파일을 재사용합니다. ( 파일을 만들어놓고 재사용 )
getStaticProps 과 getStaticPaths 를 이용합니다.
HTML 파일을 만들어둡니다. 사전 렌더링은 더 나은 성능으로 Next.js 는 퍼포먼스 이유로 정적 생성을 권고합니다.
Next 빌드시에 HTML 생성하는 Pre-rendering 방식이고 이후 렌더링되면 HTML 을 사용자 요청에 따라 파일을 제공합니다. SSR은 요청을 하면 페이지를 만들어서 제공하기에, NEXT.js 는 정적 생성, SSR 생성을 선택해서 구현할 수 있다.
Server Side Rendering은 매 요청마다 HTML 파일을 생성합니다. 유저가 요청하기전에 페이지를 만들고, SSR 은 요청마다 HTML 을 만들기 때문에 개발 환경에 따라 선택해서 사용하시면 되겠습니다. getServerSideProps 를 사용합니다. 느릴수도 있겠지만 항상 최신 상태가 유지됩니다. 최신 상태 유지란 만약 서버측에서 데이터를 변경한다 하더라도 사용자가 웹 사이트를 리렌더링 하기때문에 변경된 값을 바로 볼 수 있습니다.
페이지를 Build 시점에 만들어두고 CDN을 통해 설치해 재사용하기 때문에 대부분 정적 생성이 빠르고 좋습니다. Next.js 에서는 해당과 같은 페이지에 정적생성을 하라고 권고합니다.
정적 생성 사용을 권고하는 페이지
- 마케팅 페이지
- 블로그 게시물
- 제품 목록
- 도움말 , 문서
유저의 요청을 보내기전에 페이지를 미리 만들어도 되는 경우 정적 생성을 사용합니다.
다음은 요청이 많지 않고, 항상 최신 상태를 유지해야 하는경우 SSR 을 사용합니다.
SSR 사용을 권고하는 페이지
- 항상 최신 상태 유지
- 관리자 페이지
- 분석 차트
//_app.js
function MyApp({Component , pageProps}) {
return <Component {...pageProps}/>
}
export default MyApp;
레이아웃을 이용하려면 _app.js를 이용합니다. 모든 페이지는 _app.js를 거칩니다. 글로벌 css를 여기에 정의하며 추가적인 데이터를 이곳에 정의하며, componentDidCatch 로 커스텀 에러 핸들링 가능하고 페이지 전환시 상태값을 유지할 수 있습니다. getInitialProps 메소드를 사용해 전체 페이지가 사용할 공통 속성을 설정할 수 있습니다. 즉 _app.js는 로직, 전역 스타일 등 컴포넌트에 공통 데이터를 다룰때 사용됩니다.
컴포넌트 인자
getInitialProps, getStaticProps, getServerSideProps 메소드로 미리 가져온 초기 객체입니다. 자세한건 하단의 SSR를 참조해주세요. import { Html, Head, Main, NextScript } from 'next/document'
export default function Document() {
return (
<Html lang="ko">
<Head>
<title>C17AN's Devlog</title>
<meta charSet="utf-8"></meta>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
next 에서 제공하는 document를 커스텀마이징을 할 수 있습니다. NEXT는 마크업 정의를 건너뛰기에, HTML 이나 Head 태그, Body 태그안에 속성을 만져야 할때는 _document.js 파일을 필수적으로 선언해주세요. 즉 이 파일은 서버에서만 렌더링되므로 다음과 같은 태그들에 이벤트 핸들러를 사용할 수 없습니다 <html> <body> onClick _document 또한 _document.js 파일은 <Html>, <Head>, <Main>, <NextScript> 태그를 무조건 return 해야합니다.
_document.js는 다음과 같은 설정을 하기위해 사용됩니다.
에러 페이지에서 공통으로 사용됩니다. 작성하지 않을 경우 Nextjs에서 디폴트 값을 알아서 사용되며 , _error.js 내부에서는 세부적으로 404.js , _error.js 으로 사용할 수 있습니다. 이 세션은 하단의 404, 500 오류 페이지 태그에서 자세하게 다룹니다.
먼저 최상단의 pages 폴더는 기본적으로 index.js 파일을 가지며, 같은 레벨의 다른 이름을 가진 js 파일을 가질 수 있습니다. pages의 index.js는 메인 시작 페이지라고 할 수 있습니다. HTML에서 루트파일인 index.HTML 처럼 도메인 뒤에 아무런 페이지가 없을경우 이 페이지가 실행됩니다.
Next 는 React 와 달리 Css 를 적용하는 방식이 약간 다릅니다.
우선 _app.js 파일의 경우 애플리케이션에 스타일시트를 추가하려면 import '../styles/globals.css' 로 css 파일을 불러 오면 됩니다. 이러한 스타일( styles.css)은 응용 프로그램의 모든 페이지와 구성 요소에 적용됩니다.
하지만 Components.js 의 경우 일반적인 방법으로 Css가 적용되지가 않는데, Next.js는 파일 명명 규칙 을 사용하여 CSS 모듈 을 지원합니다.[name].module.css
import styles from '../styles/module.css';
<!-- itemlist.js 파일 -->
<div className={styles.items}>
<img className={styles.itemimg} src={item.image_link} alt={item.name}></img>
<span>
<strong className={styles.itemname}>{item.name} </strong>
</span>
<strong className={styles.itemprice}>${item.price}</strong>
</div>
// module.css
.itemname {
font-size: 15px;
display:block;
text-align: center;
}
.itemprice {
font-size: 18px;
display: block;
text-align: center;
}
.itemimg {
width: 200px;
margin: 0 auto;
}
.items {
display: flex;
flex-direction: column;
}
다음과 같이 사용자 정의 컴포넌트는 styles.items 으로 접근해야 Css가 적용됩니다.
해당 경로에 다양한 정보나, 정해지지 않는 정보로 HTML 파일을 만드는데 한계가 있으면 우선 url로 정보를 받고 javascript 내부 파일에서 정보를 처리하는 방법을 사용합니다.

이렇게 [filename].js 로 이름을 설정하면 localhost/p/data 이나 localhost/p/dev 경로로 들어온 파일은 모두 저 파일로 들어가게됩니다. 이후에 하단 세션의 url 파라미터나 url 쿼리로 해당 데이터를 처리하면 됩니다.
export default function Profile() {
const router = useRouter();
return (
<div>
<Header />
<Layout>
<p>Hello, my name is {router.query.profile}. I use Next.js</p>
</Layout>
</div>
);
}

import { useRouter } from "next/router";
const router = useRouter();
const { id } = router.query;
return(
<div>주소 값은 {id}</div>
)
주소가 localhost:3000/p/Jake 일때 data 변수에는 Jake 가 들어갑니다. 여기서 URL 파라미터를 사용할 때 주의사항이 있습니다.
Dynamic Router의 파일명과 같아야 합니다. 만약 [id].js 로 설정했다면 { id } 로 설정해주세요.
위의 주소와 같이 데이터 title=Peter를 받는데, 데이터를 받아 추출해야합니다.
export default () =>{
const router = useRouter();
const { title } = router.query; // 변수명과 파라미터 이름이 같아야합니다.
return(
<Layout>
<p>Hello, my name is {title}.</p>
</Layout>
)
}
URL 쿼리는 다이나믹라우터에서 사용하기 어렵습니다.
import Head from "next/head";
<Head>
<title>내 블로그</title>
<meta keyword="My Blog" />
<meta content="My blog" />
</Head>
Head는 상위 파일에 선언해 줘야합니다. 하위 폴더에 선언시 상위 폴더에서 적용이 안될 수 있습니다. Head의 title로 페이지의 이름을 선언하고 meta 태그로 페이지 설명을 설정할 수 있습니다. 만일 어떤 페이지라도 title 명을 같게 하고싶다면 _app.js 에 선언해주세요.
import Link from 'next/link'
function Home() {
return (
<ul>
<li>
<Link href="/">
<a>Home</a>
</Link>
</li>
</ul>
)
}
export default Home
Link 안에 href 로 이동될 페이지 주소를 입력해줍니다. Link 태그 안에서는 href 와 as 속성을 주로 사용하는데 그 외에 속성은 Next.org 사이트에서 참조해주세요.
import Link from "next/link";
const ProfileLink = props => (
<div>
<Link href={`/p/[profile]`} as={`/p/${props.profile}`}>
<a>Go to {props.profile}'s profile</a>
</Link>
</div>
);
const QueryLink = props => (
<div>
<Link href={`/p/post?title=${props.title}`}>
<a>Go to {props.title}'s profile</a>
</Link>
</div>
);
<ProfileLink profile="Jake" />
<QueryLink title="Peter" />
Link 내부에 href 는 실제로 Next.js 가 가리킬 주소, as 태그는 사용자 url에 보여질 태그입니다. 만일 같은 javascript 파일 안에서 페이지 이동이 필요하면 위의 변수를 이와같이 접근할 수 있습니다. HTML 태그는 화살표 함수 혹은 함수명을 가리키고, 속성은 URL 파라미터 / URL 쿼리 값 과 인자 값은 url 파라미터의 값입니다.
a 태그와 window.location 대신에 Link를 쓰는이유는 a 태그나 location으로 사용시 새로고침 되면서 이동됩니다. next 링크는 내부 내용만 바뀌면 요청이 늘어나며 SPA 구현했을 때 장점이 사라집니다. REDUX 사용시 값 초기화될 수 있습니다. 그래서 개발하는 환경에 따라 선택해서 사용하시면 될것같습니다.
const router = useRouter();
const gotohome = () => {
router.push("/"); // HOME 으로 주소 이동
console.log(router.pathname); // 현재 URL 정보 노출
}
Link 태그와 Router.push는 공통적으로 페이지를 새로고침하지 않고 페이지를 이동한다는 공통점이 있습니다. 하지만 Link 태그와 Router.push는 언제 사용하면될까요?
우선 둘 차이점은 Link는 HTML 안에서, Router.push안에서 사용한다는 차이가 있습니다. 이말은 a 태그와 location.href 코드와의 차이점이 똑같다는것이죠. 만약 3초 후에 페이지를 전환 한다거나, 페이지 이동 전에 검사해야될 사항이 있다거나, 메세지를 보여줘야 되거나 하는경우에는 Router.push 코드를 사용해서 사용하시면 될거같습니다.
서버 사이드 렌더링은 상단에 설명을 했지만 매 요청마다 HTML을 생성합니다. 항상 최신상태를 유지한다는 장점이 있지만, 매 요청마다 흰 화면(로딩화면) 을 잠시동안 보이는데 이것을 활용하는 방법을 알아봅시다.
SSR 특성상 Server 측에서 Rendering을 해서 HTML 을 사용자에게 전송하는 방식이라 첫 구간에 로딩이 걸릴 수 있습니다. 이에 사용자가 이탈하지 않게 로딩화면을 구현해줄 수 있습니다.
const [loading, setloading] = useState(true);
useEffect(() => {
axios.get("").then((res) => {
setloading(false);
}); // async,await 이나 promise 를 이용해서 비동기적 설정 후
//axios가 파일을 모두 받아올 경우 setloading 으로 값을 변경
}, []);
{loading && // 로딩화면일때 보여질 태그}
{!loading && // 로딩화면이 끝났을때 보여주는 태그}
혹은...
{loading ? 로딩태그 : 로딩끝났을때 태그}
Javascript의 AND 연산자인 && 를 이용해서 loading이 참, 거짓일때 화면을 보여주게됩니다.
getServerSideProps() 를 이용해서 서버에서 데이터를 가져와 정보를 주입시켜줄 수 있습니다. 이는 서버에서 API를 읽어 오기 때문에 잠시나마 로딩이 걸릴 수 있습니다.
export async function getServerSideProps(context) {
// context 로 URL 쿼리 , 응답 , 요청 , params 를 담고있습니다.
const id = context.params.id;
const res = await axios.get(``);
const data = res.data;
return {
props: {
item: data
}
}
}
getServerSideProps() 함수로 SSR를 구현했습니다. 사실 getServerSideProps() 를 사용하지 않아도, axios 를 이용해서 SSR를 구현할 수 있습니다. 아무튼 ssr 로 구현되었기에 이 페이지는 항상 최신의 상태를 유지할 것 입니다. 다만 요청시간이 늘어나면 퍼포먼스가 떨어질 수 있습니다.
서비스 제공중 존재하지 않는 파일이나 500 오류시 처리할 파일이 필요합니다. Next.js 에서는 오류를 처리할 기능을 기본 제공을 하는데 파일명을 404.js 혹은 _error.js 와 같이 이름은 똑같이 만들면 됩니다. _error.js 파일은 404에러도 포함하기에 사용자 입맛에 맞게 작성하시면 됩니다.
//404.js
export default function error() {
return (
<div>
404 ERRORs
</div>
)
}
// _error.js
function Error({ statusCode }) {
return (
<p>
{statusCode
? `An error ${statusCode} occurred on server`
: 'An error occurred on client'}
</p>
)
}
Error.getInitialProps = ({ res, err }) => {
const statusCode = res ? res.statusCode : err ? err.statusCode : 404
return { statusCode }
}
export default Error
기본 환경에서와 다른 환경에서 다른 리스트가 나올때, yarn dev로 테스트 환경에서 사용하는 데이터가 Build 된 웹에서 나오면 곤란합니다.
Nodejs 환경변수 파일은 다음과 같이 선언합니다.
process.env.변수명
Browser 환경에서는 다음과 같이 사용합니다.
process.env.NEXT_PUBLIC_변수명
환경변수파일은 pages 폴더 안에 생성해주시면 됩니다.
예시 :
// .env.development 파일
name=DEVELOPMENT
NEXT_PUBLIC_API_URL=http://makeup-api.herokuapp.com/api/v1/products.json?brand=maybelline
밑에 있는 코드로 해당 환경에 맞게 접근을 합니다. 밑의 코드의 경우 브라우저에서 보여질것이기 때문에 브라우저 환경에서만 해당됩니다. 만약 개발환경 (노드환경) 에서 실행하면 오류가 발생할 수 있습니다.
const API = process.env.NEXT_PUBLIC_API_URL;
위에서 설명한것처럼 정적 생성은 미리 만들어진 페이지를 보여줍니다. Server Side Rendering의 경우 사용자가 각각의 페이지마다 로딩을 기다리면 짜증이 날 수 있습니다. 이에 밑의 함수를 이용해서 미리 렌더링할 변수나 HTML 파일을 설정할 수 있습니다.
정적 생성이며 페이지 콘텐츠가 외부데이터에 연동됩니다. API에서 데이터를 가져올때 사용됩니다. 이 함수는 한번 실행되고 이후엔 데이터를 재활용합니다.
export async function getStaticProps() {
const res = await axios.get(`http://makeup-api.herokuapp.com/api/v1/products.json?brand=maybelline`);
const data = res.data;
return {
props: {
item: data ,
}
}
}
이 함수로 F5를 했을 때 원래 데이터 HTML로 생성 후 재 사용함으로써 로딩 시간을 줄일 수 있습니다.
처음 파일에 접속하는 사람은 잠시 빈화면이 나오겠지만 이후에는 빠르게 접속할 수 있습니다. 정적생성되어있는 페이지는 javascript 가 중지되어도 작동하며 NextLink 는 prefetch 요소로 프리로드 ( 정적생성 ) 합니다.
HTML 갯수가 한정적이고 먼저 몇가지 페이지만 먼저 보여줘야 한다면 이 함수를 사용합니다.
getStaticPaths() 는 getStaticProps() 과 같이 사용해야합니다.
export async function getStaticPaths() {
return {
paths: [
{params: { id : '130'}}, // 미리 생성할 HTML 파일명 미리 렌더링할 페이지명을 기재합니다.
{params: { id : '131'}} //
],
// paths: Array.map((data) => ({params: {profile: data.id.toString()}})) 를 이용하는것도 좋다.
// 처음엔 오래걸릴수도 있더라도 이후 접속할땐 빠르다.
fallback: true
};
}
// getStaticPaths 는 getStaticProps 없이는 사용할 수 없습니다.
export async function getStaticProps(context) {
const id = context.params.id;
const res = await axios.get(`http://makeup-api.herokuapp.com/api/v1/products/${id}.json`);
const data = res.data;
return {
props: {
item: data ,
}
}
}
여기서 fallback 요소는 false 값이면 없는 페이지면 404 에러발생 , true 면 getStaticPaths 가 HTML과 JSON빌드를 만듭니다. 이후에는 페이지를 재사용
const rout = useRouter();
rout.isFallback // 결과값 true 나 false 을 반환
rout.isFallback은 페이지가 생성이 안되어있어, 렌더링 해야할때 true를 반환, 페이지가 존재해서 사용자에게 띄울때 false 를 반환합니다. 이 함수를 이용해 파일을 불러올때 로딩화면을 구현할 수 있습니다.
먼저 package.json 파일을 조회해주세요.
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start"
},
그 다음 밑에 있는 명령어로 빌드를 시켜주세요.
> npm run build
그 후에 포트를 지정해서 Run 해주시면됩니다.
npm start
package.json 을 수정해서 빌드 포트를 바꿔줄 수 있습니다.
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start -p 8000"
},