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 Object를 index.html로 지정하고 오류 페이지(403 → /index.html)도 설정해야 새로고침 시 404가 뜨지 않는다.
마치며
서버 없이 돌아가는 블로그 파이프라인을 만들어봤다. 글 쓰는 행위(S3 업로드) 하나가 배포까지 이어지니 생각보다 쾌적하다. 다음엔 태그 필터링이나 검색 기능도 추가해볼 예정이다.