STEP 01배포 전에 현재 버전과 되돌릴 위치를 적기

서비스 중인 폴더에 파일을 하나씩 덮어쓰면 새 HTML과 이전 CSS가 섞이는 중간 상태가 생길 수 있습니다. 릴리스마다 독립된 폴더를 만들고, 웹 서버가 읽는 current 링크만 바꾸는 방식은 파일 준비와 공개 시점을 나눕니다.

site/
  releases/
    release-a/          이전 HTML과 자산
    release-b/          검사할 새 HTML과 자산
  current -> releases/release-a/

여기서 폴더 이름은 예시입니다. 실제 작업 기록에는 현재 링크가 가리키는 경로, 새 후보 경로, 전환 예정 시각을 남깁니다. 이전 폴더는 검사가 끝나기 전에 지우지 않습니다. 동시 배포가 가능하다면 배포 작업을 하나로 제한하는 장치도 별도로 필요합니다.

펀치블로그의 격리 실험에서는 동일한 Nginx 프로세스에서 링크를 바꿔 본문이 release-a에서 release-b로 변하는 것을 확인했습니다. 이 실험이 모든 파일 캐시 설정이나 동시 요청 조건을 검증한 것은 아닙니다.

STEP 02후보 폴더는 웹 서버의 권한으로 읽어보기

빌드가 성공해도 Nginx 작업 프로세스가 파일을 읽을 수 없다면 공개 요청은 실패합니다. 파일의 읽기 권한뿐 아니라 상위 디렉터리를 통과하는 실행 권한도 확인해야 합니다. 빌드를 실행한 사용자가 읽었다는 결과로 대신하지 않습니다.

# Ubuntu에서 Nginx 작업 사용자가 www-data인 경우의 예시
# 경로와 사용자는 실제 설정에 맞게 바꿉니다.
sudo -u www-data test -r /srv/example/releases/release-b/index.html
printf 'readable_exit=%s
' "$?"
namei -l /srv/example/releases/release-b/index.html

test 종료 코드 0은 그 파일의 읽기 가능 여부를 확인한 결과일 뿐, 전체 사이트의 정상 동작을 뜻하지 않습니다. 후보의 글과 CSS도 확인합니다. 403을 없애려고 사이트 전체를 누구나 쓰게 만들지 말고, 어느 디렉터리에서 접근이 막히는지 좁혀 필요한 읽기·통과 권한을 설정합니다.

공개 폴더에는 생성된 HTML·CSS·공개용 자료만 둡니다. 원본 설정, 접근 키, 비공개 기록을 같은 루트에 놓고 URL 추측을 막는 방식에 의존하지 않습니다.

STEP 03이전 HTML이 요청할 자산도 준비하기

링크 교체는 한 번의 파일 시스템 작업이지만 브라우저의 HTML·CSS·이미지 요청은 한 번에 끝나지 않습니다. 이전 HTML을 받은 사용자가 전환 직후 이전 CSS를 요청할 수 있습니다. 파일명에 해시를 붙였다면 새 릴리스에도 필요한 이전 해시 자산을 남기거나, 버전별 자산 주소를 유지해야 합니다.

격리 실험의 조건이전 CSS 요청배포 설계에 반영할 점
새 릴리스에 이전 자산 없음404새 HTML만 검사하면 놓침
이전 자산을 새 릴리스에도 보관200기존 방문자의 후속 요청도 확인

값은 기존 자산 경계 실험의 관찰입니다. 보관 기간은 HTML의 캐시 정책과 사용 패턴에 맞춰 정합니다. 파일명은 같고 내용만 바꾸는 배포라면 오래된 캐시와 새 본문을 구분하기 어려우므로 ETag와 재검증 조건도 함께 확인합니다.

STEP 04설정 변경과 파일 전환을 구분하기

Nginx 설정을 바꿨다면 실제 서비스가 사용할 설정을 nginx -t로 검사하고, 성공 후 반영한 다음 응답을 확인합니다. 별도 경로의 후보만 검사하고 다른 설정을 반영하면 검사와 운영이 어긋납니다. 후보 설정 실험에서 검사 전후의 본문과 반영 후 본문을 구분한 이유입니다.

고정된 root 아래의 current 링크만 바꾸는 설계라면 설정 파일 변경과는 다른 작업입니다. 다음은 Linux의 동일 파일 시스템에서 링크를 교체하는 핵심 구조입니다. 배포 도구 내부의 일부이며 동시 작업 통제, 후보 검증, 오류 복구는 별도로 구현해야 합니다.

from pathlib import Path
import os

site_root = Path('/srv/example')  # 예시 경로
candidate = site_root / 'releases' / 'release-b'
pending = site_root / 'current-next'
# pending이 이미 있다면 실패하게 두고 이전 작업 상태를 확인합니다.
pending.symlink_to(candidate, target_is_directory=True)
os.replace(pending, site_root / 'current')

성공한 os.replace의 경로 교체와 여러 HTTP 요청이 하나의 버전만 보는 것은 같은 보장이 아닙니다. 파일 캐시를 사용한다면 기존 파일 정보가 남는 조건도 살펴봅니다. 전환 명령의 성공 메시지가 아닌 공개 응답으로 다음 단계를 판단합니다.

STEP 05공개 주소에서 성공·실패 경로를 함께 확인하기

요청 대상기대하는 확인실패하면 볼 것
홈과 대표 글200, 새 버전의 실제 문장링크 목적지, root, 캐시
새 CSS와 이전 CSS둘 다 올바른 스타일 파일자산 복사·보관 정책
없는 글·없는 CSS정적 블로그에서는 404홈 fallback 설정
비공개 소스 경로내용이 공개되지 않음공개 루트의 파일 구성

상태 코드뿐 아니라 Content-Type과 본문도 봅니다. 없는 CSS 주소에서 홈 HTML이 200으로 돌아오면 전송은 성공해도 화면은 깨집니다. 휴대전화 폭에서 메뉴와 코드 블록이 화면을 밀어내지 않는지도 실제 브라우저로 확인합니다.

새 HTML에만 필요한 자산을 빠뜨렸거나 읽기 권한이 틀린 경우에는 원인을 추적하는 동안 잘못된 버전을 계속 공개할 필요가 없습니다. 미리 남긴 이전 릴리스로 돌아갈 수 있어야 합니다.

STEP 06되돌리기도 같은 검사를 통과해야 끝난다

롤백은 current를 기록해 둔 이전 릴리스로 다시 전환하는 작업입니다. 단, 이전 폴더가 온전하고 그 버전의 자산을 읽을 수 있는지 먼저 확인합니다. 현재 링크가 자신이 예상한 배포 버전인지도 대조해 다른 작업자의 새 배포를 덮지 않도록 합니다.

되돌린 뒤에는 홈·대표 글·자산을 다시 GET으로 검사하고 실제 이전 문장이 나오는지 확인합니다. 이 절차는 정적 파일에 대한 것입니다. 함께 수행한 DB 변경이나 외부 API의 변경까지 되돌려 주지는 않습니다.

  • 후보 HTML과 자산을 웹 서버가 읽을 수 있다.
  • 이전 HTML의 자산 요청까지 검사했다.
  • 새 본문과 404 경로를 공개 URL에서 확인했다.
  • 이전 릴리스를 보관했고, 되돌린 뒤 확인할 요청을 정했다.

배포 기록에는 명령 성공 여부와 별도로 “어떤 URL에서 어떤 본문을 확인했는가”를 남깁니다. 데이터가 있는 사이트라면 다음 SQLite 복구 가이드에서 파일 전환과 데이터 복구의 경계를 확인하세요.

2026-10-17의 롤백 후속 실험에서는 이전 HTML이 200이어도 새 HTML의 CSS가 404였습니다. 되돌릴 후보에도 필요한 새 자산을 준비하거나 자산을 독립 경로에 보관하고, 롤백 후 신구 자산 요청을 모두 확인합니다. 기존 릴리스를 즉석에서 수정하기보다 검사할 후보를 별도로 준비하는 방법도 검토합니다.

함께 확인한 공식 문서

작성 기록

최초 작성 2026-10-15 · 검토 2026-10-17 · 기존 관찰 자료와 후속 실험을 대조해 확인 순서와 판단표를 정리했습니다. 관찰 당시의 결과이며 현재 모든 환경의 동작을 보장하지 않습니다. 정정 요청