Next.js로 정적 웹사이트 만들기 - 1. Next.js 구조

2019.05.06(7달 전)

Next.js는 현재 가장 유명한 React용 서버 사이드 렌더링 프레임워크이다.

프레임워크로써 이해하기 쉬운 구조와 수많은 예제들, 그리고 다양한 플러그인들을 지원하고 있어 많은 사람들에게 사랑받고 있다.

또한, Next.js는 next export라는 명령어로 routing 경로로 하여금 정적 웹사이트를 만들 수 있는 기능도 제공하고 있다.

Next.js의 최신 버전은 8.x.x인데 최신 버전에서는 export 기능이 제대로 동작하지 않으며, 현재 next export 기능은 7.x.x 버전에서 에러없이 잘 동작한다. [Static export failed when upgraded to v8.0.0 #6251]

이 글은 Next.js의 export 기능을 사용하여 정적 웹사이트를 만드는 것에 대해서 설명하려고 한다.

이 글은 서버 사이드 렌더링이 아닌 정적 웹사이트를 만드는 것에 중점으로 설명한다.

Next.js 기본 구조

Next.js의 기본 구조는 다음처럼 구성된다.

pages/ // HTML Document, Application Container, 각종 페이지 등을 작성한다.
    _document.js // HTML Document.
    _app.js // Application Container. 공통의 레이아웃을 작성한다.
    _error.js // Error Page.
    index.js // Root Page /로 시작되는 경로를 말한다.
    hello.js // Hello Page /hello로 시작되는 경로를 말한다.
static/ // 정적 파일 (이미지, 파일 등)을 업로드 한다.
next.config.js // Next.js의 환경 설정 파일이다. 라우팅 설정, typescript, less 등의 webpack 플러그인을 설정한다.

_document.js

_document.js는 SPA에서 시작점이 되는 index.html이라고 생각하면 된다.

Custom Document를 만들때만 작성이 필요하며, 생략된 경우 Next.js가 기본 값을 사용한다.

작성 예시는 다음과 같다.

import Document, { Head, Main, NextScript } from 'next/document';

export default class RootDocument extends Document {
    render() {
        return (
            <html>
                <Head>
                    <meta charSet="utf-8" />
                    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=no" />
                    <meta name="description" content="Dev.log"/>
                    <meta name="keywords" content="blog,react,antd,webpack,css,javascript" />
                    <link rel="manifest" href="/static/manifest.json" />
                    <link rel="shortcut icon" href="/static/favicon.ico" />
                    <link rel="stylesheet" href="https://fonts.googleapis.com/earlyaccess/notosanskr.css" />
                    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/3.0.1/github-markdown.min.css" />
                    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.15.6/styles/railscasts.min.css" />
                </Head>
                <body>
                    <Main />
                    <NextScript />
                </body>
            </html>
        );
    }
}

_document.js에서 React와 같은 동작을 수행할 수 있을 것처럼 보이지만 React LifecycleData Fetching이 불가능하다.

document-app-differences

React에서는 <div id="root" />처럼 React 컴포넌트가 마운트 되는 최초 요소를 작성하게 된다. 하지만 Next.js에서는 따로 작성할 필요 없이, MainNextScript 클래스에 내장 되어 있다.

MainNextScript의 자세한 설명은 이 글을 참조한다. NextJS: Main and Nextscript

_app.js

React에서 대부분 App.js이라는 이름으로 공통의 레이아웃을 작성하듯이 Next.js에서는 Application의 공통 레이아웃을 _app.js에서 작성할 수 있다.

생략된 경우 Next.js가 기본 값을 사용한다.

작성 예시는 다음과 같다.

import App, { Container } from 'next/app';
import React from 'react';
import NProgress from 'nprogress';
import Router from 'next/router';
import Helmet from 'react-helmet';
import moment from 'moment';

import Layout from '../components/Layout';
import '../styles/index.less';

moment.locale('ko');

Router.events.on('routeChangeStart', (url) => NProgress.start());
Router.events.on('routeChangeComplete', () => NProgress.done());
Router.events.on('routeChangeError', () => NProgress.done());

export default class RootApp extends App {
    render() {
        const { Component, ...other } = this.props;
        return (
            <Container>
                <Helmet title="Dev.log" />
                <Layout {...other} {...this.state}>
                    <Component {...other} {...this.state} />
                </Layout>
            </Container>
        );
    }
}

이곳에서 ComponentDidCatch로 Error Handling을 한다던지, Routing이 되었을 때의 Progressing 처리, 스타일 시트 Import 등을 작성한다.

_error.js

Error 처리를 공통으로 하고자 할 때, 공통적으로 사용할 수 있는 Error Page를 작성할 수 있다.

생략된 경우 Next.js가 기본 값을 사용한다.

작성 예시는 다음과 같다.

import React, { Component } from 'react';

import ErrorPage from '../components/ErrorPage';

export default class RootError extends Component {
    render() {
        const { statusCode } = this.props;
        return <ErrorPage statusCode={statusCode} />;
    }
}

next.config.js

webpack plugin들과 Next.js의 라우팅 설정을 작성한다.

작성 예시는 다음과 같다.

const withLess = require('@zeit/next-less');
const withTypescript = require('@zeit/next-typescript');

// fix: prevents error when .less files are required by node
if (typeof require !== 'undefined') {
    require.extensions['.less'] = file => {}
}

module.exports = withTypescript(withLess({
    lessLoaderOptions: {
        javascriptEnabled: true,
    },
    exportPathMap: () => return {
        '/': { page: '/' },
        '/about': { page: '/about' },
        '/readme.md': { page: '/readme' },
        '/p/hello-nextjs': { page: '/post', query: { title: 'hello-nextjs' } },
        '/p/learn-nextjs': { page: '/post', query: { title: 'learn-nextjs' } },
        '/p/deploy-nextjs': { page: '/post', query: { title: 'deploy-nextjs' } }
    },
}));

Next.js와 관련된 webpack plugin들은 @zeit/next Prefix로 패키지를 제공하고 있으며, 사용할 때는 with prefix로 선언해서 사용한다.

exportPathMap이 Next.js의 라우팅 경로이다.

Next.js로 정적 웹사이트 만들기 목차

react
nextjs
website
Sung Gyun Oh
Sung Gyun Oh
Hello world!