← 목록으로

Next.js SSG로 블로그 만들기 — S3와 CloudFront까지

2026-05-11

Next.js SSG로 블로그 만들기 — S3와 CloudFront까지

개인 블로그를 운영하고 싶다면 선택지가 많다. Vercel에 그냥 올려도 되고, 워드프레스를 쓸 수도 있다. 하지만 이번엔 Next.js Static Export + S3 + CloudFront 조합으로 직접 파이프라인을 구축해봤다. 글을 쓰면 자동으로 빌드·배포되는 구조가 목표였다.

전체 아키텍처

마크다운 파일 (S3 content bucket)
        ↓ PutObject 이벤트
    Lambda 함수
        ↓ workflow_dispatch 호출
  GitHub Actions
        ↓ next build (SSG)
  S3 static bucket
        ↓
  CloudFront CDN

글 하나를 S3에 업로드하면 Lambda가 깨어나 GitHub Actions를 트리거하고, Next.js가 빌드를 돌려 정적 파일을 다른 버킷에 올린다. CloudFront는 그 버킷을 바라본다.

Next.js 설정

SSG로 내보내려면 next.config.ts에 한 줄이면 된다.

// next.config.ts
const nextConfig = {
  output: "export",
};

export default nextConfig;

output: 'export'를 설정하면 next build 결과로 out/ 폴더가 생긴다. 이 폴더를 통째로 S3에 올리면 끝이다.

S3에서 마크다운 읽기

빌드 시점에 S3에서 .md 파일을 모두 읽어 각 글의 정적 페이지를 생성한다. gray-matter로 frontmatter를 파싱하고, react-markdown으로 HTML을 렌더링한다.

// src/lib/posts.ts 핵심 부분
export async function getAllPosts(): Promise<PostMeta[]> {
  const res = await s3.send(new ListObjectsV2Command({ Bucket: BUCKET }));
  const mdFiles = res.Contents?.filter((o) => o.Key?.endsWith(".md")) ?? [];

  return Promise.all(
    mdFiles.map(async ({ Key }) => {
      const raw = await getRawContent(Key!);
      const { data } = matter(raw);
      return { slug: Key!.replace(/\.md$/, ""), ...data } as PostMeta;
    })
  );
}

CloudFront 설정 시 주의점

S3 버킷을 오리진으로 쓸 때 버킷 웹사이트 엔드포인트가 아닌 OAC(Origin Access Control) 방식을 쓰는 게 좋다. 버킷을 퍼블릭으로 열지 않아도 CloudFront만 접근 가능하게 제한할 수 있다.

next build가 만드는 정적 파일은 /blog/my-post/index.html 형태라서, CloudFront에 Default Root Objectindex.html로 지정하고 오류 페이지(403 → /index.html)도 설정해야 새로고침 시 404가 뜨지 않는다.

마치며

서버 없이 돌아가는 블로그 파이프라인을 만들어봤다. 글 쓰는 행위(S3 업로드) 하나가 배포까지 이어지니 생각보다 쾌적하다. 다음엔 태그 필터링이나 검색 기능도 추가해볼 예정이다.