menulogo

Next.js 프로젝트를 정적 페이지로 배포하기 (Github pages)

@corinthioniaJune 18, 2024

Next.js로 만든 블로그를 정적 페이지로 배포하기

Next.js 14 버전 사용
@next/mdx 사용하지 않음

Next.js 14버전에서 마크다운 파일을 다루기 쉽도록 @next/mdx를 소개하고 있다. .mdx 확장자를 가진 파일들을 페이지로 손쉽게 만들어 주는 라이브러리이다. 하지만 내가 이를 사용하지 않은 이유는 단순히 원격 저장소에 내 마크다운 파일들이 공개되는 게 싫어서 였다. 사실 블로그에 다 보이는 글이라 상관없는 글도 있긴 하지만, 마크다운 파일에 draft 옵션을 추가하여 임시저장된 글도 있고, 오래되어 노출하고 싶지 않은 글도 있기 때문이다.

@next/mdx를 사용하려면 모든 마크다운 파일이 원격 저장소에 업로드 되어야 한다. 따라서 위의 방법을 사용하지 않고 gray-matter 라이브러리를 이용해서 로컬에 있는 마크다운 파일을 html 파일로 변환하여 블로그를 구현했다.

또 Next.js 프로젝트를 배포할 때 찰떡인 vercel을 사용하지 않은 이유는 단순히 github.io 도메인이 멋있어 보여서 그랬다... 😉 (간죽간살)


그런데 간편한 방법을 두고 복잡한 방법을 선택했다 보니 추가적으로 설정해야 하는 것도 많았고, 시행착오도 많이 있었다.

정적 배포 시 고려해야 할 점

  1. slug 부분을 static 하게 불러올 수 있어야 한다
  2. 정적 블로그에서는 next/image의 이미지 최적화가 불가능하기 때문에 최적화 기능을 꺼 둔다
  3. 정적 페이지로 빌드하기 위해 추가적인 설정을 진행한다

1. Slug

현재 프로젝트에서는 Dynamic route를 이용하여 post 페이지를 구성한다. [...slug] 부분이 해당 부분이다.

src
├── app
│   ├── post
│   │   ├── [...slug]
│   │   │   └── page.tsx

그런데 SSR 환경에서는 페이지를 요청할 때마다 route가 동적으로 생성된다. 하지만 정적으로 배포할 경우 Next.js 서버를 사용할 수 없기 때문에 추가적인 설정을 진행해야 한다.

먼저 Next.js의 generateStaticParams를 Dynamic route를 사용하는 페이지 컴포넌트 내부에 작성해 주면 된다.

The generateStaticParams function can be used in combination with dynamic route segments to statically generate routes at build time instead of on-demand at request time.

generateStaticParams 함수는 빌드시간에 정적으로 Route를 생성하기 위해 Dynamic route segment와 함께 사용된다. 또한 generateStaticParams를 이용해 모든 포스트 정보를 불러오고, 이를 토대로 현재 페이지의 slug와 일치하는 포스트를 찾는 방식인 듯하다.

export async function generateStaticParams() {
  const posts: Post[] = await getAllPosts(CONTENTS_PATH.POST_PATH);

  if (!posts || posts.length === 0) {
    return [{ slug: 'not-found' }];
  }

  return posts.map(post => ({
    slug: post.fields.slug.split('/'),
  }));
}

posts가 nullable 하거나 없을 때 에러가 나기 때문에 이 경우에는 not-found 페이지로 이동시키도록 했다.

2. next/image

SSG 환경에서는 next/image의 최적화를 사용할 수 없다. 이미지 최적화 작업도 nextjs 서버에서 해 주는 일이기 때문이다. 따라서 next.config.js에 이미지 최적화를 사용하지 않음으로 설정한다.
공식문서에도 이 방법이 명시되어 있다.

const path = require('path');

module.exports = {
  images: {
    unoptimized: true,
  },
};

3. 정적 export 설정

프로젝트 빌드 시 정적 페이지로 빌드될 수 있게 몇 가지 설정을 추가해야 한다.
우선 next.config.json에 아래 옵션을 추가한다.

const path = require('path');

module.exports = {
  output: 'export',
};

나는 github pages로 배포하기 때문에 gh-pages 라이브러리를 설치한 후 package.json에 다음과 같은 스크립트를 추가했다.

"scripts": {
    "build": "next build",
    "deploy": "touch out/.nojekyll && gh-pages -d out -b deploy"
  },

빌드가 완료되면 out 이라는 폴더가 생성되는데, deploy 명령어를 수행하여 out 폴더에 .nojekyll 파일을 생성하도록 한다. 이는 github pages에서 정적 배포 시 jekyll 을 사용하는 것을 방지하기 위함이다. (밑에서 자세히 설명 예정) 그 후 gh-pages 를 이용해 프로젝트를 배포한다.

gh-pages는 기본적으로 지킬 기반으로 동작하는데, 그러면 _로 시작하는 파일들은 제대로 동작하지 않는다. 결과물을 빌드하면 js와 css 번들 모두 _next/static/ 아래에 포함되어 있어 404 에러가 뜨는 것 같았다. (정확하진 않고 추측이다,, 아무리 찾아봐도 명확한 답변을 못 찾았다 🥹)

따라서 이를 해결하기 위해서는 배포할 때 jekyll을 사용하지 않으면 된다. 그러기 위해서는 빌드 결과물의 루트 경로에 .nojekyll 이라는 제목의 파일을 생성하고, 내용은 비워 두면 된다.

github pages로 배포

"deploy": "gh-pages -d out -b deploy"

위에서 작성한 명령어는 빌드한 폴더를 deploy 브랜치에 업로드 될 수 있게 한다. 이때 github actions를 사용하여 deploy 브랜치에 푸시되면 자동으로 배포되게 할 수도 있지만, 굳이 그렇게 하지 않아도 레포지토리에서 설정할 수 있다.

레포지토리의 Setting > Pages 탭에 들어가서 Source를 Deploy from a branch 로 설정하고, 배포할 브랜치(여기에서는 deploy)를 선택하면 된다.

배포 후 JS와 CSS 적용이 되지 않는 문제

배포 후 확인해 보니 css가 하나도 적용되지 않고 있었다.

이게 왜 이럴까...

네트워크 탭을 확인해 보니 css 파일뿐만 아니라 js 번들도 404 에러가 뜨는 것을 확인할 수 있다.

열심히 서치해 보니 next.config.jsassetPrefix 값을 추가하면 된다고 한다. (결국 안 됨)

const isProd = process.env.NODE_ENV === 'production';

module.exports = {
  // Use the CDN in production and localhost for development.
  assetPrefix: isProd ? 'https://cdn.mydomain.com' : undefined,
};

공식문서를 보면 위와같이 되어 있다. assetPrefix 값으로 ., / 또는 도메인 주소를 입력하라고 해서 모두 시도해 봤지만 모두 다 안 됐다.

그런데 빌드 결과물을 확인해 보니, 분명 deploy script에 touch out/.nojekyll 을 명시했음에도 불구하고 .nojekyll 파일이 생성되지 않고 있었다.

어디 있니...?

그래서 script를 아래와같이 수정했다.

{
  "scripts": {
    "deploy": "touch out/.nojekyll && gh-pages -d out -b deploy -t true"
  }
}

그냥 -t true를 추가한 건데, .으로 시작하는 파일을 포함하여 배포하도록 하는 옵션이다. 따라서 위 옵션을 추가하여 .nojekyll 파일도 빌드 결과물에 포함되게 할 수 있다.

위와 같이 package.json을 수정한 후 다시 빌드 & 배포하면 원격 저장소의 deploy 브랜치에 .nojekyll 파일이 성공적으로 올라가 있다.

그리고 배포 페이지에 들어가 보면 더 이상 404 에러가 뜨지 않는다

감격스럽다...


확실이 Next.js에서 제공하는 @next/mdx + vercel 조합이 쉽고 빠르게 정적 블로그를 만들 수 있는 방법 같다. 하지만 나처럼 원격 저장소에 임시 저장 글을 올리고 싶지 않다든가, 비공개 글을 작성하고 싶다면 위와 같은 방법을 사용할 수도 있다~!

← 이전 글NextJS 특정 페이지의 OG 태그 다르게 적용하기
다음 글 →스타일링 라이브러리 비교하기