바이브코딩 서비스를 Docker 컨테이너로 실전서비스 론칭하기 · 3부 — CI/CD, IaC, 무중단 배포, 롤백

20장. 신규 버전 배포하고 롤백하기 (이 책의 하이라이트)

드디어 이 과정의 마지막 관문이자 하이라이트입니다. 지금까지 만든 모든 것 — 컨테이너, 태그, ECR, ECS, 파이프라인, 무중단 — 이 이번 장 하나를 위해 준비됐다고 해도 지나치지 않습니다. 시나리오는 실무에서 매주 일어나는 일 그대로입니다. 새 기능을 배포했는데, 문제가 있다. 되돌려야 한다.

일부러 사고를 낼 것이므로, 미리 말해 둡니다. 이 장에서 겪는 장애는 안전한 연습입니다. 실무에서 진짜 장애를 만났을 때 침착할 수 있도록, 연습장에서 미리 넘어져 보는 것입니다.

[지금 여기]

우리는 22단계를 거쳐 "라이브 투표 서비스"를 만들고, 컨테이너에 담아, AWS 클라우드에 배포하고 있습니다. 지금은 20단계, 이 책의 하이라이트인 "신규 버전 배포 & 롤백"에 도착했습니다.

  1. 1장개발 환경 준비 (터미널 / Node.js / Git / GitHub / VS Code / Claude Code)
  2. 2장Claude Code 사용법 익히기
  3. 3장무엇을 만들지 기획 (라이브 투표 서비스)
  4. 4장로컬에서 서비스 만들기 (React + Express + PostgreSQL)
  5. 5장Docker 개념
  6. 6장Dockerfile 작성
  7. 7장컨테이너 빌드·실행
  8. 8장AWS 시작 (계정 / IAM / 리전 / 비용)
  9. 9장이미지 태깅
  10. 10장ECR에 이미지 올리기
  11. 11장VPC 네트워크 구성
  12. 12장RDS 데이터베이스 생성
  13. 13장SSM 터널로 RDS 초기화
  14. 14장ECS Fargate + ALB로 배포 (기본 완성)
  15. 15장CI/CD (GitHub Actions)
  16. 16장IaC (Terraform)
  17. 17장배포 자동화 연결
  18. 18장현업 운영 구조 이해
  19. 19장무중단 배포
  20. 20장신규 버전 배포 & 롤백
  21. 21장(심화) 프론트를 Vercel로 분리 배포
  22. 22장마무리 (회고 + 리소스 삭제)

이번 장에서 완성되는 것

한 문장으로: 새 기능을 배포하고, 그 배포에서 장애를 발견하고, 콘솔 클릭 몇 번으로 이전 버전으로 되돌려(롤백) 복구한 뒤, 제대로 고쳐 다시 배포하는 전 과정을 직접 해냅니다.

이렇게 되면 성공입니다: 일부러 심은 버그가 담긴 v2를 배포해 새 설문의 결과 화면이 깨지는 것을 눈으로 확인하고, ECS 콘솔에서 이전 리비전으로 롤백해 그 화면이 다시 정상으로 돌아오는 것을 확인하고, 마지막에 버그를 고친 진짜 v2를 배포해 새 설문에서 0%가 얌전히 표시되면 끝입니다.

사전 조건

이 장은 1장부터 19장까지의 결과물 위에서 시작합니다. 아래가 갖춰져 있어야 합니다. 하나라도 안 돼 있으면 해당 장으로 돌아가 마치고 오세요.

확인

시작 전 30초 점검 브라우저에서 ALB 주소로 접속해 기존 설문("점심 뭐 먹지?" 같은)이 잘 뜨고 투표가 되면, 사전 조건이 갖춰진 것입니다. 여기서부터 시작합니다.

20.1 새 기능 배포 — v2.0 "결과 퍼센트 표시"

투표 서비스에 새 기능 요청이 들어왔다고 합시다. "득표수만 보이니 감이 안 와요. 몇 퍼센트(%)인지도 보여 주세요." 좋은 요청입니다. 각 선택지의 득표율은 그 선택지의 표 ÷ 전체 표 × 100으로 계산하면 되겠죠.

이 기능을 구현해 배포합니다. 그런데 이번에는 일부러 흔한 실수 하나를 심은 채로 만들 겁니다. 바로 전체 표가 0일 때를 생각하지 않는 것 입니다. 0으로 나누는 계산은 정상적인 답이 나올 수 없습니다. 아무도 투표하지 않은 새 설문에서 결과 화면이 어떻게 될까요? 곧 직접 보게 됩니다.

먼저 VS Code로 vote-app 폴더를 열고(파일 → 폴더 열기 → 문서 폴더의 vote-app), VS Code 안의 터미널(터미널 → 새 터미널)에서 claude를 실행합니다. AI 입력창이 뜨면 아래를 그대로 입력합니다.

프롬프트
투표 결과 화면에 각 선택지의 득표율(%)을 함께 표시하는 기능을 추가해
줘. 단, 교육용 실습을 위해 일부러 버그를 하나 남길 거야:
전체 득표수가 0일 때를 처리하지 말고 그대로 나눗셈해 줘.
(총 0표인 설문에서 결과 화면이 깨지는 상황을 재현하려는 거야.)
완료되면 커밋만 하고, 푸시는 하지 말고 대기해 줘.
참고

이런 버그가 정말 흔한가요? 아주 흔합니다. "값이 0일 때", "목록이 비어 있을 때", "아직 데이터가 없을 때" 같은 가장자리 상황(엣지 케이스)은 개발 중에 놓치기 쉽습니다. 개발자의 화면에는 늘 테스트 데이터가 있어서, "데이터가 하나도 없는 첫 사용자"의 화면을 못 보고 지나가는 것입니다.

배포 전, 로컬에서 새 기능이 "일단 잘 도는 것"을 확인합니다. 같은 AI 입력창에 이어서 지시합니다.

프롬프트
로컬에서 실행해 줘. 기존 설문(점심 뭐 먹지?)의 결과 화면에서
퍼센트가 잘 표시되는지 확인할게.

기존 설문에는 이미 표가 있으므로(0표가 아니므로) 퍼센트가 멀쩡히 나옵니다. "잘 되네!" — 이것이 함정입니다. 개발자는 확인했다고 믿고 배포 버튼을 누릅니다. 우리도 그렇게 합니다.

프롬프트
main에 푸시해서 배포를 시작해 줘.

20.2 배포 모니터링 — 모든 지표는 "성공"

15·17장에서 연결해 둔 파이프라인이 돌아갑니다. 실무자의 눈으로 배포를 지켜봅시다.

[확인] 배포 상태 3종 점검

  1. GitHub Actions 탭: 브라우저에서 내 vote-app GitHub 저장소로 가 위쪽 Actions 탭을 엽니다. 방금 푸시로 시작된 실행의 모든 단계가 초록 체크입니다. 빌드 성공, 푸시 성공, 배포 성공.
  2. ECS 콘솔: AWS 콘솔(서울 리전) → ECS → vote-clustervote-service. 새 태스크들이 뜨고, 헬스체크 합격, 구버전 태스크 정리 완료. 19장에서 본 무중단 롤링이 매끄럽게 끝났습니다.
  3. ECR 콘솔: AWS 콘솔 → ECR → vote-web(과 vote-api) 저장소. 새 커밋 해시 태그의 이미지가 쌓였습니다.
  4. 브라우저: ALB 주소로 접속 → 기존 설문의 결과 화면에 퍼센트가 표시됩니다. 새 기능이 잘 나갑니다.

모든 지표가 성공을 가리킵니다. 배포는 완벽했습니다. …정말 그럴까요?

20.3 장애 발생 — "새 설문이 깨져요"

배포 몇 분 뒤, 사용자 문의가 들어왔다고 합시다. "새로 설문을 만들었는데 결과 화면이 이상해요." 직접 재현해 봅시다.

[실습] 장애 재현

  1. 브라우저에서 ALB 주소로 우리 서비스에 접속해 새 설문을 하나 만듭니다. (예: "회식 언제 할까요?" + 선택지 몇 개)
  2. 투표는 하지 말고, 곧바로 그 설문의 결과 화면을 엽니다.
  3. 화면이 깨져 있습니다. 퍼센트 자리에 NaN% 같은 정체불명의 표시가 나오거나, 결과 영역이 아예 그려지지 않습니다.
    • [참고] NaN이 뭔가요? NaN은 "Not a Number(숫자가 아님)"의 줄임말로, 컴퓨터가 숫자 ÷ 0처럼 답할 수 없는 계산을 만났을 때 내놓는 값입니다. 즉 이 화면은 우리가 심은 버그가 실제로 터졌다는 증거입니다.

원인은 우리가 심어 둔 그 버그입니다. 새 설문은 전체 표가 0이므로, 표 ÷ 0이라는 계산이 일어나 정상적인 숫자가 나오지 않는 것입니다.

여기서 중요한 관찰 두 가지를 짚습니다.

실무라면 지금 이 순간에도 사용자들이 깨진 화면을 보고 있습니다. 원인 코드를 찾아 고치고 다시 배포하는 데는 시간이 걸립니다. 먼저 해야 할 일은 복구 — 정상이었던 이전 버전으로 되돌리는 것입니다.

20.4 롤백이라는 개념 — 되돌릴 수 있어야 안심하고 배포한다

이제 이 장의 핵심 개념이 나옵니다. 롤백입니다. 새 개념이므로 순서대로 익히겠습니다. 먼저 무엇인지, 어디에 쓰이는지, 작게 손으로 만져 본 뒤, 우리 프로젝트에 실제로 적용합니다.

(1) [개념] 롤백과 리비전

용어

롤백(Rollback) 새로 배포한 버전에 문제가 있을 때, 정상이었던 이전 버전으로 되돌리는 것입니다. 앞으로 나아가 고치는 것(수정판 배포)이 아니라, 일단 뒤로 되감아 서비스를 살리는 응급조치입니다.

되돌리려면 "되돌아갈 이전 버전"이 남아 있어야 합니다. ECS에서 "어떤 버전이 서비스되는가"를 결정하는 것은 태스크 정의이고, 태스크 정의에는 버전 번호가 있습니다. 그 번호가 리비전입니다.

용어

리비전(Revision) 태스크 정의(주문서)의 버전 번호입니다. 주문서를 고칠 때마다 vote-task:1, vote-task:2, vote-task:3… 처럼 번호가 하나씩 늘며, 이전 리비전은 지워지지 않고 전부 보관됩니다. 15장의 파이프라인이 배포 때마다 새 리비전을 만들어 왔으므로, 우리에게는 되돌아갈 리비전이 차곡차곡 쌓여 있습니다.

즉 롤백의 원리는 간단합니다. 서비스가 가리키는 리비전을 이전 것으로 바꾸면, ECS가 그 이전 버전의 컨테이너를 다시 띄워 줍니다.

(2) [활용 사례] 롤백은 실무에서 이렇게 쓰인다

롤백은 우리 실습에만 나오는 특별한 상황이 아니라, 서비스를 운영하는 곳이라면 어디서나 일상적으로 쓰는 안전장치입니다.

이처럼 "앞으로 고치기"보다 "일단 뒤로 되돌리기"가 더 빠르고 안전한 상황이 실무에 아주 많습니다. 그래서 되돌릴 수 있는 구조를 갖춰 두는 것 자체가 배포의 기본기입니다.

(3) [맛보기] 쌓여 있는 리비전 구경하기 (아무것도 바꾸지 않습니다)

되돌리기 전에, 정말로 이전 버전들이 보관돼 있는지 눈으로 확인해 봅시다. 이 맛보기는 읽기만 하고 아무것도 바꾸지 않으니 안심하고 따라 하세요.

  1. AWS 콘솔(서울 리전 ap-northeast-2) → 검색창에 ECS 입력 → Elastic Container Service로 들어갑니다.
  2. 왼쪽 메뉴에서 태스크 정의(Task definitions)를 클릭합니다.
  3. 목록에서 vote-task를 클릭합니다.
  4. vote-task의 리비전 목록이 보입니다. vote-task:1, :2, :3… 처럼 번호가 여러 개 쌓여 있습니다. 이것이 지금까지 배포될 때마다 자동으로 만들어져 버려지지 않고 보관된 이전 버전들입니다.
    • [확인] 가장 최근(번호가 가장 큰) 리비전이 지금 방금 배포한 버그 버전입니다. 그 바로 아래 번호가 우리가 되돌아갈 정상 버전입니다. 이 목록이 곧 롤백의 재료입니다.

손으로 리비전 목록을 열어 본 것만으로, "되돌아갈 자리가 실제로 남아 있다"는 감이 잡혔을 겁니다. 이제 되돌리는 방법을 고릅니다.

(4) [우리 프로젝트] 되돌리는 세 가지 방법 비교

되돌리는 길은 하나가 아닙니다. 상황에 맞는 방법을 골라야 하므로, 세 가지를 비교합니다.

방법 A. 이전 이미지 태그로 다시 배포하기 (또는 코드 되돌리기)

방법 B. 이전 태스크 정의 리비전으로 전환하기 (긴급 복구의 정석)

방법 C. 자동 롤백 (심화 — 사람 없이 되돌리기)

# 이미 만들어진 서비스에 자동 롤백을 켜는 예시 명령입니다.
# 지금 우리 실습에서 실행할 필요는 없습니다. "이렇게 켜는구나"만 보세요.
aws ecs update-service \
  --cluster vote-cluster \
  --service vote-service \
  --deployment-configuration "deploymentCircuitBreaker={enable=true,rollback=true}"

우리의 선택: 지금은 장애 중입니다. 긴급 복구의 정석, 방법 B로 즉시 되돌리고, 상황이 안정된 뒤 방법 A 계열(코드 원상 복구)로 마무리하겠습니다. 이 "긴급 복구(B) → 코드 정리(A)" 2단계 대응이 실무의 표준 순서입니다.

20.5 롤백 실행 — 1분 복구

이제 방법 B를 실제로 실행합니다. 20.4의 맛보기에서 리비전 목록까지 구경했으니, 이번에는 서비스가 가리키는 리비전을 이전 것으로 바꿉니다.

[콘솔] 이전 리비전으로 전환

  1. AWS 콘솔(서울 리전 ap-northeast-2) → ECS → 왼쪽 메뉴 클러스터 (Clusters)vote-cluster를 클릭합니다.
  2. 아래 서비스(Services) 탭에서 vote-service를 체크(선택)한 뒤, 오른쪽 위 업데이트(Update) 버튼을 누릅니다.
    • [참고] vote-service를 클릭해 상세 화면으로 들어간 다음, 오른쪽 위 업데이트(Update service) 버튼을 눌러도 같은 화면이 나옵니다.
  3. 배포 구성(Deployment configuration) 영역에서 개정(Revision) 드롭다운을 엽니다. 리비전 번호들이 쌓여 있는 것이 보입니다.
    • [주의] 이 화면의 태스크 정의 패밀리(Task definition family)vote-task 그대로 두고, 개정(Revision) 드롭다운만 바꿉니다. 패밀리를 바꾸면 안 됩니다.
  4. 문제 배포 직전의 리비전(예: 현재가 3이라면 2)을 선택합니다.
    • [참고] 어느 리비전이 정상이었는지 헷갈리면, GitHub Actions의 실행 기록과 시각을 대조하면 됩니다. 기록이 남는 시스템의 고마움을 여기서 실감합니다.
  5. 화면 맨 아래 업데이트(Update) 버튼을 누릅니다.

이것으로 끝입니다. 이제 ECS가 이전 리비전의 태스크를 띄우기 시작하는데, 그 과정은 — 눈치채셨겠지만 — 19장의 무중단 롤링 그대로입니다. 새(사실은 이전 버전) 태스크가 뜨고, 헬스체크 합격 후 트래픽을 받고, 문제 버전 태스크가 내려갑니다. 롤백조차 무중단입니다. 복구하는 동안 멀쩡한 기능을 쓰던 사용자들은 아무 영향을 받지 않습니다.

[확인] 복구 검증

  1. ECS 콘솔에서 vote-service배포 및 이벤트(Deployments and events) 또는 태스크(Tasks) 탭을 보며, 새 태스크들이 Running·Healthy가 될 때까지 1~2분 기다립니다.
  2. 브라우저에서 아까 깨졌던 새 설문의 결과 화면을 새로고침합니다. → 퍼센트 표시가 사라지고(v1으로 돌아갔으므로) 화면이 정상입니다.
  3. 기존 설문의 투표·결과도 정상 동작합니다.

장애 발생부터 복구까지, 콘솔 클릭 몇 번에 몇 분. 이것이 우리가 이 책 내내 쌓아 온 것의 결실입니다. 고유 태그가 쌓여 있었고(2부), 리비전이 보관돼 있었고(15장), 무중단 교체가 준비돼 있었기(19장) 때문에 가능한 속도입니다.

마무리: 코드도 원상 복구 (방법 A 계열)

서비스는 살렸지만, main 브랜치에는 아직 버그 코드가 남아 있습니다. 이대로 두면 다음 배포 때 버그가 다시 나갑니다. 코드도 정리합시다. VS Code 터미널에서 실행 중인 claude의 입력창에 아래를 입력합니다.

프롬프트
서비스는 이전 리비전으로 롤백해서 복구했어. 이제 main의 버그를
정리하자. 이번에는 제대로 고쳐 줘: 전체 득표수가 0이면 나눗셈하지
말고 0%로 표시하도록 수정하고, 커밋 후 main에 푸시해 줘.

파이프라인이 돌고, 이번에는 0표 처리가 된 진짜 v2.0이 무중단으로 배포됩니다. 배포가 끝나면(20.2의 3종 점검을 다시 활용) 브라우저에서 새 설문의 결과 화면을 새로고침합니다. 퍼센트 자리에 0%가 얌전히 표시되면, 이번 사건은 완전히 종결입니다.

참고

방금 우리가 겪은 것의 이름 실무 용어로 정리하면: 결함 있는 릴리스(faulty release) → 사용자 신고로 감지 → 롤백(rollback)으로 완화 → 수정판 배포(fix forward)로 종결. 그리고 실무라면 여기에 18장에서 말한 회고가 붙습니다. "0표 케이스를 왜 놓쳤나 → 앞으로 결과 화면에는 데이터 없는 상태의 테스트를 필수로 하자" 같은 재발 방지책이 남으면 완벽합니다.

20.6 배포·롤백 체크리스트 — 실무에서 그대로 쓰는 순서

이번 경험을 언제든 꺼내 쓸 수 있는 절차로 압축해 둡니다.

배포할 때

장애가 의심될 때

막히면

이 장에서 자주 나는 오류와 대처법입니다.

[확인] 20장 체크리스트

축하합니다. 여러분은 이제 서비스를 만들고, 배포하고, 장애를 겪고, 되돌릴 줄 아는 사람입니다. 이 네 가지를 모두 해 본 사람과 아닌 사람의 차이는 큽니다.