- 로컬 개발 서버는 내 컴퓨터에서만 동작하지만, 배포된 블로그는 외부 사용자가 언제든 접근할 수 있어야 한다.
- Vercel은 Next.js 프로젝트를 빠르게 운영 환경에 올릴 수 있고, 개인 블로그의 초기 운영 부담을 줄여준다.
- 이 블로그는 GitHub Actions와 submodule pointer를 통해 어떤 글 버전을 production에 배포할지 명확하게 제어한다.
블로그를 만들었다고 해서 바로 다른 사람이 볼 수 있는 것은 아니다.
이 글은 Next.js로 만든 개인 블로그를 실제 운영 환경에 배포하기 위해 어떤 선택을 했고, Vercel과 GitHub Actions를 어떻게 연결했는지 정리한 기록이다.
내 컴퓨터에서 실행되는 블로그는 개발 중인 화면에 가깝다. 실제 블로그가 되려면 항상 켜져 있고, 외부에서 접근할 수 있고, 코드가 바뀌었을 때 다시 배포될 수 있는 환경이 필요했다.
그래서 이번에는 블로그를 어디에 배포할지, 왜 Vercel을 선택했는지, 그리고 지금 프로젝트에서는 어떤 자동 배포 흐름으로 production에 올리고 있는지를 정리해보려고 한다.
왜 배포 플랫폼을 고민했나
로컬에서 npm run dev로 블로그를 띄우는 것과 실제로 운영 가능한 사이트를 만드는 것은 다른 문제다.
로컬 개발 환경에서는 내 컴퓨터가 서버 역할을 한다. 내가 터미널을 끄거나 컴퓨터가 꺼지면 블로그도 함께 사라진다.
하지만 실제 블로그는 달라야 한다.
- 내 컴퓨터가 꺼져 있어도 접근할 수 있어야 한다.
- 다른 사람이 브라우저에서 주소를 입력해 접속할 수 있어야 한다.
- 코드가 바뀌면 다시 빌드되고 배포되어야 한다.
- 배포된 결과물이 안정적으로 유지되어야 한다.
즉, 배포 환경은 "항상 운영되고 있는 서버"에 가깝다. 직접 서버를 운영할 수도 있고, 배포 플랫폼에 맡길 수도 있다.
처음에는 AWS, S3와 CloudFront, Cloudflare Pages, Vercel을 함께 비교했다.
AWS는 자유도가 높다. EC2, S3, CloudFront, Route 53 같은 서비스를 조합하면 원하는 구조를 만들 수 있다. 하지만 작은 개인 블로그를 시작하는 단계에서는 설정과 운영 부담이 컸다.
S3와 CloudFront는 정적 파일을 빠르게 제공하기 좋다. 다만 이 블로그는 Next.js App Router를 사용하고 있고, route handler와 metadata 같은 기능도 함께 사용한다. 단순 정적 파일 배포만 생각하기에는 고려할 것이 늘어났다.
Cloudflare Pages도 좋은 선택지였다. 빠르고, Git 기반 배포도 가능하고, 비용 면에서도 매력적이다. 나중에 다른 프로젝트에서 충분히 연결해볼 수 있는 선택지라고 생각했다.
이번 블로그에서는 Vercel이 가장 단순했다.
배포 플랫폼을 볼 때 본 기준
- Next.js 프로젝트를 자연스럽게 배포할 수 있는가
- 초기 비용 부담이 크지 않은가
- 다른 프로젝트로 확장해볼 가능성이 있는가
- 배포 흐름을 GitHub Actions와 연결하기 쉬운가
- 개인 블로그 운영에 비해 설정이 과하지 않은가
Vercel을 선택한 이유
이유는 길지 않다.
Next.js로 만든 블로그였고, Vercel은 Next.js 배포 경험이 가장 단순했다.
이 블로그에는 글 목록, 상세 페이지, metadata, route handler 같은 Next.js 기능이 들어간다. Vercel을 사용하면 이런 기능을 배포하기 위해 별도 서버 구성을 길게 고민하지 않아도 된다.
또 하나는 비용이었다. 개인 블로그를 시작하는 단계에서 매달 고정 비용이 커지는 구조는 부담스러웠다. Vercel은 작은 개인 프로젝트를 시작하기에 비용 부담이 적고, 필요해지면 나중에 다른 방식으로 확장할 수도 있다.
물론 Vercel이 모든 상황에서 정답은 아니다.
인프라를 세밀하게 제어해야 하거나, 특정 클라우드 리소스와 깊게 연결해야 한다면 AWS가 더 적합할 수 있다. 완전히 정적인 사이트라면 S3와 CloudFront만으로도 충분할 수 있다. Cloudflare Pages도 다른 프로젝트에서는 좋은 선택지가 될 수 있다.
하지만 지금 필요한 것은 인프라 실험이 아니라 블로그를 안정적으로 공개하는 일이었다.
Vercel Git 배포를 그대로 쓰지 않은 이유
Vercel은 GitHub 저장소와 연결하면 push만으로 자동 배포할 수 있다. 보통은 이 방식이 가장 단순하다.
하지만 이 블로그는 글 저장소를 따로 둔다.
blog 저장소는 블로그 앱을 가지고 있고, blog-posts 저장소는 실제 글 원본을 가지고 있다. 그리고 blog 저장소 안의 content-source/posts submodule pointer가 이번 배포에 사용할 blog-posts commit을 결정한다.
구조를 그림으로 보면 이렇다.
핵심은 blog-posts의 최신 commit을 무조건 배포하지 않는다는 점이다.
그래서 Vercel의 Git 자동 배포를 그대로 사용하지 않았다.
대신 GitHub Actions에서 배포 과정을 직접 제어하기로 했다. vercel.json에서는 Git 기반 자동 배포를 끄고, GitHub Actions가 필요한 순서대로 저장소를 가져오고, 글을 검증하고, Vercel에 배포한다.
{
"git": {
"deploymentEnabled": false
}
}이렇게 하면 배포 흐름을 Vercel에 모두 맡기는 대신, 내가 원하는 commit을 기준으로 배포할 수 있다.
이 프로젝트의 자동 배포 흐름
현재 배포는 blog 저장소의 main 브랜치에 push될 때 시작된다.
blog-posts에 글을 push하는 것만으로는 production 글이 바뀌지 않는다. 글 내용이 production에 반영되려면 blog 저장소의 content-source/posts pointer가 새 blog-posts commit을 가리키고, 그 변경이 blog 저장소의 main에 push되어야 한다.
전체 흐름은 이렇게 정리할 수 있다.
- 1
blog 저장소 main 브랜치에 push한다.
- 2
GitHub Actions가 blog 저장소를 가져온다.
- 3
content-source/posts가 가리키는 blog-posts commit을 확인한다.
- 4
해당 commit의 글 파일만 content-source/posts 위치에 가져온다.
- 5
npm ci로 의존성을 설치한다.
- 6
npm run validate:posts로 글 메타데이터와 구조를 검증한다.
- 7
Vercel 환경 정보를 pull한다.
- 8
Vercel 방식으로 production build를 만든다.
- 9
prebuilt 결과물을 Vercel production에 배포한다.
이 흐름에서 가장 중요한 단계는 배포에 사용할 글 버전을 가져오는 일이다.
GitHub Actions는 먼저 blog 저장소가 가리키는 content-source/posts commit을 읽는다. 그리고 그 commit을 기준으로 blog-posts 저장소의 글 파일을 가져온다.
그래야 production 배포에 들어가는 글이 항상 blog 저장소의 pointer와 일치한다.
그 다음에는 validate:posts를 실행한다.
글 파일은 단순한 텍스트처럼 보이지만 실제 블로그에서는 metadata, sitemap, 상세 페이지 렌더링에 함께 사용된다. 그래서 배포 전에 글의 frontmatter나 기본 구조가 깨지지 않았는지 확인하는 단계가 필요했다.
검증이 끝나면 Vercel CLI를 사용해 production 배포를 진행한다.
$ vercel pull$ vercel build --prod$ vercel deploy --prebuilt --prod실제 GitHub Actions에서는 위 명령을 npx vercel@latest와 VERCEL_TOKEN을 붙여 실행한다.
이 방식은 Vercel이 제공하는 배포 환경을 사용하면서도, 배포 전 단계는 GitHub Actions에서 내가 원하는 대로 조립할 수 있다는 장점이 있었다.
도메인은 어디까지 연결했나
배포가 끝나면 Vercel이 기본 도메인을 제공한다. 하지만 개인 블로그라면 별도의 도메인을 쓰는 것이 더 자연스럽다고 생각했다.
그래서 Gabia에서 yunkoo.dev 도메인을 구매하고, Vercel 프로젝트에 custom domain으로 연결했다.
이 글에서 도메인은 배포 결과물을 내 주소로 보여주기 위한 마지막 연결 지점이다.
도메인 연결에서 한 일
- Gabia에서 yunkoo.dev 도메인을 구매했다.
- Vercel 프로젝트에 custom domain을 추가했다.
- Vercel 기본 주소 대신 yunkoo.dev로 블로그에 접근할 수 있게 했다.
SSL은 어떻게 처리했나
도메인을 연결할 때 함께 신경 써야 하는 것이 SSL이다.
SSL은 브라우저와 사이트 사이의 통신을 암호화해주는 장치다. 사용자가 https://yunkoo.dev로 접속할 수 있으려면 도메인 연결뿐 아니라 SSL 인증서도 필요하다.
직접 서버를 운영한다면 인증서를 발급하고, 갱신하고, 웹 서버 설정에 연결하는 과정까지 직접 챙겨야 한다. 작은 블로그에서는 이 부분도 은근히 부담이 될 수 있다.
Vercel을 사용하면 custom domain을 연결한 뒤 SSL 인증서 발급과 HTTPS 적용을 플랫폼이 함께 처리해준다. 이 덕분에 블로그 배포 과정에서 HTTPS 설정을 별도로 크게 다루지 않아도 됐다.
배포를 만들고 나서 달라진 점
배포 구조를 만들고 나니 블로그를 다루는 기준이 더 분명해졌다.
- 로컬에서 화면을 확인하는 것은 개발 단계다.
blog-posts에 글을 저장하는 것은 작성 단계다.blog저장소의 pointer를 갱신하는 것은 공개할 글 버전을 선택하는 단계다.- GitHub Actions와 Vercel을 통해 production에 올리는 것이 실제 배포 단계다.
- 1
로컬에서 블로그를 개발한다.
- 2
blog-posts에서 글을 작성하고 저장한다.
- 3
blog에서 공개할 blog-posts commit을 선택한다.
- 4
GitHub Actions가 선택된 commit으로 빌드한다.
- 5
Vercel production에 배포한다.
- 6
Gabia에서 구매한 yunkoo.dev 도메인으로 접근한다.
이 과정을 나누고 나니, 글쓰기와 배포가 한 덩어리로 섞이지 않게 되었다.
글을 저장한다고 바로 공개되지 않고, 배포한다고 무조건 최신 글 저장소를 가져오지도 않는다. 내가 선택한 commit을 기준으로 검증하고 배포한다.
마무리
이번 글은 블로그를 실제로 배포 가능한 형태로 만드는 과정에 대한 기록이다.
Vercel을 선택한 이유는 단순했다. 이 블로그는 Next.js로 만들었고, 나는 배포 플랫폼 자체를 운영하기보다 글을 쓰고 블로그를 개선하는 데 집중하고 싶었다.
실제로 배포 흐름을 만들어보니 blog 저장소와 blog-posts 저장소를 나눈 이유도 더 선명하게 이해됐다.
글을 쓰는 곳과 배포 기준을 정하는 곳이 분리되어 있으니, 초안을 저장하는 일과 production에 공개하는 일을 구분할 수 있었다. 그리고 GitHub Actions에서 그 기준을 그대로 따라가도록 만들면서 구조가 더 납득되기 시작했다.
정리
- Vercel은 Next.js 블로그를 빠르게 배포하기 좋은 선택이었다.
- 초기 비용과 운영 부담을 줄이면서 production 배포를 시작할 수 있었다.
- 이 프로젝트는 Vercel Git 자동 배포 대신 GitHub Actions와 Vercel CLI를 사용한다.
- 배포 기준은 blog 저장소에 기록된 content-source/posts pointer다.
- 도메인 연결과 SSL 적용은 Vercel 프로젝트에서 함께 처리했다.