PM2와 Nginx 배포 후 공개 확인 루틴 만들기
Futory의 Next.js Markdown 블로그 운영을 기준으로 PM2 재시작, 내부 포트 확인, Nginx 프록시, 공개 URL 검증을 하나의 배포 루틴으로 묶는 방법을 정리했습니다.
요약
AI와 함께 블로그를 만들고 운영하면 새 글을 작성하는 속도보다 배포와 검증을 안정화하는 일이 더 중요해집니다. Futory는 Next.js 기반 Markdown 블로그이고, 글은 content/posts/*.md에 추가됩니다. 하지만 글 파일이 생겼다고 곧바로 독자가 볼 수 있는 것은 아닙니다. 스테이징에서 콘텐츠 검증과 테마 검증, 빌드 검증을 통과한 뒤 호스트의 라이브 경로로 동기화되고, PM2 프로세스가 재시작되며, Nginx가 공개 도메인으로 요청을 전달해야 비로소 한 편의 글이 공개됩니다.
이 흐름에서 자주 생기는 착각은 “PM2 재시작이 성공했으니 배포도 성공했다”는 판단입니다. 실제 운영에서는 PM2가 살아 있어도 내부 포트가 아직 부팅 중일 수 있고, 내부 포트는 정상이어도 Nginx 프록시나 도메인 응답이 실패할 수 있습니다. 반대로 공개 페이지가 캐시나 지연 때문에 늦게 보이는 경우도 있습니다. 그래서 Futory 자동 게시에서는 PM2, 내부 포트, Nginx, 공개 URL을 분리해서 확인하는 루틴이 필요합니다.
이 글은 바이브코딩으로 만든 Next.js Markdown 블로그를 운영할 때, 배포 후 공개 확인을 어떻게 반복 가능한 절차로 만들 수 있는지 정리한 기록입니다.
배포 성공을 한 단계로 보지 않기
블로그 운영에서 배포 성공은 하나의 명령 결과가 아닙니다. 최소한 네 단계로 나누어 볼 수 있습니다. 첫째, 프로젝트가 빌드 가능한 상태여야 합니다. 둘째, 호스트 라이브 경로에 필요한 파일이 정확히 반영되어야 합니다. 셋째, PM2 프로세스가 새 빌드를 기준으로 다시 실행되어야 합니다. 넷째, 공개 도메인에서 새 글과 글 목록이 실제로 200 응답을 반환해야 합니다.
Futory의 경우 스테이징 경로는 /opt/data/wordblog_next_staging이고, 호스트 라이브 경로는 /opt/wordblog입니다. 컨테이너 안에서 보이는 경로와 호스트에서 보이는 경로가 다르기 때문에, 배포 자동화는 파일이 어느 위치에서 어느 위치로 이동하는지 명확히 알고 있어야 합니다. .env, node_modules, .next처럼 라이브 환경에 맞게 유지해야 하는 항목도 무심코 덮어쓰면 안 됩니다.
이런 이유로 “파일 복사 완료”, “빌드 완료”, “PM2 재시작 완료”, “공개 확인 완료”는 서로 다른 상태로 기록해야 합니다. 그래야 문제가 생겼을 때 어느 지점에서 멈췄는지 빠르게 알 수 있습니다.
PM2 재시작은 끝이 아니라 시작이다
PM2는 Node.js 앱을 계속 실행해 주는 프로세스 매니저입니다. Futory에서는 futory-wordblog 프로세스가 내부 포트 127.0.0.1:8042에서 Next.js 앱을 실행합니다. 배포 후 pm2 restart futory-wordblog --update-env를 실행하면 새 빌드와 환경 변수를 반영할 수 있지만, 이 명령이 성공했다는 사실만으로 공개 상태를 확정하면 위험합니다.
Next.js 앱은 재시작 직후 몇 초 동안 준비 중일 수 있습니다. 이때 곧바로 curl을 실행하면 일시적으로 실패할 수 있습니다. 따라서 자동화에서는 짧은 재시도 루프를 두는 편이 안전합니다. 예를 들어 내부 포트에 여러 번 요청하면서 200 응답이 나오는지 확인하고, 일정 시간 안에 성공하지 못하면 그때 실패로 기록합니다. 이렇게 하면 정상적인 부팅 지연과 실제 장애를 구분할 수 있습니다.
또한 .env나 실행 환경이 바뀐 경우에는 PM2가 예전 환경을 들고 있을 수 있습니다. 그래서 환경 변경이 포함된 배포에서는 --update-env를 사용하고, 필요하면 프로세스를 삭제한 뒤 다시 시작하는 기준도 마련해 두는 것이 좋습니다. 자동화가 이 차이를 모르면 빌드는 성공했는데 런타임에서만 다른 설정으로 동작하는 문제가 생깁니다.
내부 포트와 공개 도메인을 나누어 확인하기
운영 검증에서 가장 중요한 습관은 내부와 외부를 나누어 보는 것입니다. 내부 포트 확인은 Next.js 앱 자체가 살아 있는지 보는 단계입니다. 공개 도메인 확인은 Nginx, 인증서, 프록시, 라우팅까지 포함한 사용자 관점의 단계입니다. 둘 중 하나만 확인하면 원인 파악이 어려워집니다.
예를 들어 127.0.0.1:8042에서는 새 글이 잘 보이는데 https://futory.oig.kr/posts가 실패한다면 Next.js 앱보다는 Nginx 설정, 도메인, 인증서, 프록시 경로를 의심해야 합니다. 반대로 공개 도메인이 실패하고 내부 포트도 실패한다면 PM2 프로세스, 빌드 결과, 런타임 환경 변수를 먼저 확인해야 합니다.
Futory 같은 Next.js Markdown 블로그에서는 새 글 상세 URL과 목록 URL을 함께 확인해야 합니다. 상세 URL만 200이면 글 파일은 페이지로 생성되었을 가능성이 높지만, 목록에 오늘 날짜가 보이지 않는다면 정렬, frontmatter, 콘텐츠 로딩 로직에 문제가 있을 수 있습니다. 목록만 열리고 상세가 실패한다면 slug나 파일명, 라우팅 문제를 확인해야 합니다.
공개 확인에 오늘 날짜를 포함하는 이유
자동 게시에서는 단순히 200 응답만 보는 것으로 부족합니다. 이미 예전 페이지가 200으로 열릴 수도 있고, 글 목록이 캐시된 상태로 응답할 수도 있습니다. 그래서 Futory 자동화는 공개 URL에서 새 글 slug와 오늘 날짜가 노출되는지 함께 확인합니다. 오늘 KST 날짜가 2026-05-17이라면 새 글 frontmatter에도 같은 날짜가 들어가야 하고, 공개 페이지에서도 그 날짜가 확인되어야 합니다.
이 검증은 중복 게시 방지와도 연결됩니다. 자동화는 먼저 content/posts/*.md 안에 오늘 날짜의 글이 있는지 확인합니다. 없다면 새 글을 만들고, 검증과 배포 후 공개 페이지에서 같은 날짜를 다시 확인합니다. 시작 지점과 끝 지점에서 같은 날짜를 기준으로 삼으면 “오늘의 글이 실제로 공개되었는가”를 더 명확하게 판단할 수 있습니다.
날짜 기준은 서버 시간이 아니라 한국 시간이어야 합니다. cron은 UTC 기준으로 실행될 수 있지만, 블로그 운영 리듬은 KST 기준입니다. 따라서 게시 시간 선택, frontmatter 날짜, 중복 게시 검사, 공개 확인의 기준 날짜는 모두 KST로 맞추는 것이 좋습니다.
테마 검증을 배포 루틴에 포함하기
배포 후 공개 확인 루틴에서 콘텐츠만 보면 UI 기능이 빠질 수 있습니다. Futory에는 기존 다크/라이트 모드 기능이 있고, components/ThemeToggle.tsx, app/theme-init.tsx, CSS의 data-theme 변수가 유지되어야 합니다. 새 글 하나를 추가하는 작업이라도 전체 프로젝트를 라이브로 동기화하기 때문에, 테마 관련 파일이 사라지거나 CSS 토큰이 깨지면 사용자 경험이 손상됩니다.
그래서 배포 전에는 npm run test:theme를 실행하고, 호스트에서도 다시 같은 검증을 실행하는 것이 안전합니다. 이 테스트는 글의 품질을 보는 것이 아니라 블로그의 사용 경험이 유지되는지 확인하는 장치입니다. AI가 생성한 콘텐츠 자동화에서는 이런 검증이 특히 중요합니다. AI는 사용자가 명시한 글 작성 목표에는 집중하지만, 주변 기능 보존까지 항상 자연스럽게 챙기지는 못하기 때문입니다.
운영 자동화의 목적은 새 글을 빠르게 올리는 것만이 아니라 기존 독서 경험을 깨뜨리지 않는 것입니다. 테마 검증은 그 원칙을 코드로 만든 안전장치입니다.
자동화 보고에는 무엇을 남길까
자동 게시가 성공하면 보고에는 제목, 날짜, URL, 스테이징 검증 결과, 호스트 검증 결과, 공개 확인 결과가 들어가면 충분합니다. 실패했다면 어느 단계에서 실패했는지를 남겨야 합니다. 선택 시간이 아니라서 건너뛴 경우와 이미 오늘 글이 있어서 건너뛴 경우는 실패가 아니므로 짧은 스킵 메시지로 구분하면 됩니다.
좋은 보고는 길지 않아도 됩니다. 중요한 것은 운영자가 바로 다음 행동을 판단할 수 있어야 한다는 점입니다. “빌드 실패”라고만 쓰는 것보다 “스테이징 빌드 실패, 호스트 배포 미실행”이라고 적는 편이 안전합니다. “공개 URL 확인 실패”라면 PM2 재시작과 내부 포트 확인은 성공했는지 함께 남겨야 원인을 좁히기 쉽습니다.
Futory의 주제는 AI와 함께 만들고, 자동화하고, 운영하는 기록입니다. 따라서 자동화 보고 자체도 블로그 운영의 일부입니다. 매일 쌓이는 성공, 스킵, 실패 신호는 나중에 더 나은 배포 루틴과 콘텐츠 운영 전략을 만드는 데이터가 됩니다.
자주 묻는 질문
PM2가 online이면 공개 확인을 생략해도 되나요?
생략하지 않는 것이 좋습니다. PM2의 online 상태는 프로세스가 실행 중이라는 뜻이지, Nginx와 공개 도메인을 통해 독자가 새 글을 볼 수 있다는 뜻은 아닙니다. 내부 포트와 공개 URL을 따로 확인해야 합니다.
내부 포트는 정상인데 공개 도메인이 실패하면 어디를 봐야 하나요?
먼저 Nginx 프록시 설정과 도메인 연결, 인증서 상태를 확인하는 것이 좋습니다. 앱 자체가 내부에서 정상이라면 문제는 대개 외부 요청이 내부 앱으로 전달되는 경로에 있습니다. 이때 공개 도메인의 /posts와 새 글 상세 URL을 함께 확인하면 원인을 더 빨리 좁힐 수 있습니다.
글 하나만 추가해도 PM2를 재시작해야 하나요?
Next.js 빌드 결과를 기준으로 서비스하는 운영 방식이라면 재시작이 필요합니다. 새 Markdown 파일이 빌드에 반영되고, PM2가 새 빌드 결과를 사용해야 공개 페이지에 나타납니다. 단, 빌드 검증이 실패한 상태에서는 절대 재시작하지 않는 것이 원칙입니다.
공개 페이지가 바로 바뀌지 않으면 배포 실패인가요?
항상 그렇지는 않습니다. 재시작 직후 부팅 지연이나 캐시 때문에 잠깐 늦게 보일 수 있습니다. 하지만 일정 시간 재시도해도 새 slug와 오늘 날짜가 확인되지 않는다면 공개 검증 실패로 보고하고, 내부 포트와 Nginx 경로를 나누어 점검해야 합니다.
결론
바이브코딩으로 블로그를 운영할 때 배포 자동화는 글을 만드는 속도만큼 검증의 깊이가 중요합니다. Futory의 자동 게시 흐름은 KST 기준 시간 선택, 중복 게시 방지, 새 Markdown 글 작성, 테마 검증, 콘텐츠 검증, Next.js 빌드, 호스트 동기화, PM2 재시작, 공개 URL 확인으로 이어집니다. 이 중 하나라도 빠지면 “작성은 됐지만 공개되지 않은 글”이나 “공개는 됐지만 기존 기능이 깨진 배포”가 생길 수 있습니다.
PM2와 Nginx 배포 후 공개 확인 루틴은 이런 위험을 줄이는 운영 습관입니다. 내부 프로세스와 공개 도메인을 나누어 확인하고, 새 글 slug와 오늘 날짜를 기준으로 최종 검증하면 자동화의 결과를 더 신뢰할 수 있습니다. Futory가 기록하려는 것은 AI가 만든 결과물만이 아니라, 그 결과물을 실제 서비스로 안전하게 운영하는 과정입니다. 매일의 작은 검증 루틴이 쌓일수록 블로그는 더 안정적인 실험장이 됩니다.