배포를 마치고 헬스체크를 돌렸더니 연속으로 실패합니다. 에러 로그에 ClassNotFoundException이 찍혀있습니다. 무언가 잘못됐습니다. 작업계획서에는 "롤백 기준: 헬스체크 실패"라고 써있었습니다. 롤백을 해야 합니다.
그런데 막상 롤백을 시작하려니 손이 떨립니다. WAR 파일 백업은 어디에 있었지? DB schema도 변경했는데, DB는 어떻게 되돌리지? 설정 파일도 바꿨는데, 그건 어떻게 롤백하지?
롤백은 미리 연습하고 절차를 문서화해둔 사람만 패닉 없이 실행할 수 있습니다. 롤백 체크리스트가 있으면 순서대로 따라가기만 하면 됩니다.
- 1롤백 판단 기준(Go/No-Go)을 정의하고 상황에 따라 결정할 수 있다
- 2WAR 파일과 설정 파일을 세트로 롤백하는 절차를 실행할 수 있다
- 3DB 롤백의 세 가지 방법(UNDO SQL, 트랜잭션 ROLLBACK, 스냅샷)을 구분할 수 있다
- 4인증서 롤백 후 Nginx/Tomcat 재시작까지 완료할 수 있다
- 5롤백 불가 상황(DDL, 데이터 손상)을 인지하고 대안을 설명할 수 있다
롤백 판단 기준
Go/No-Go 기준 정의하기
배포 직후 이상 징후가 보이는 상황에서 "조금 더 기다려볼까"와 "지금 당장 롤백해야 한다" 사이의 판단은 경험이 아니라 사전에 정해둔 기준에서 나와야 합니다. 기준 없이 버티다 보면 5분이 30분이 되고, 영향받는 사용자가 기하급수적으로 늘어납니다. Go/No-Go 기준은 감정이 아닌 숫자로 정의해야 하고, 배포 전에 팀 전체가 합의해야 실제 장애 상황에서 즉시 실행력을 가집니다.
확대
롤백은 감정이 아니라 기준으로 결정해야 합니다. "조금만 더 기다려볼까"를 반복하다가 장애 시간이 길어지는 상황을 방지하기 위해, 배포 전에 No-Go 조건을 명확히 정의해둡니다.
일반적인 No-Go(롤백) 조건:
# 조건 1: 헬스체크 연속 실패
# 배포 후 5분 안에 3회 연속 실패 시 즉시 롤백
for i in 1 2 3; do
STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/myapp/health)
echo "Check $i: HTTP $STATUS"
sleep 30
done
# → 3회 모두 200이 아니면 롤백
# 조건 2: 에러율 급증
TOTAL=$(grep "$(date +'%d/%b/%Y:%H:%M')" /var/log/nginx/access.log | wc -l)
ERRORS=$(awk '$9 ~ /^5/' /var/log/nginx/access.log \
| grep "$(date +'%d/%b/%Y:%H:%M')" | wc -l)
# ERRORS/TOTAL > 0.05 (5%) → 롤백
# 조건 3: 핵심 기능 불가
# OOM 발생 → 즉시 롤백
grep -i "OutOfMemoryError" /opt/tomcat/logs/catalina.out | tail -3
# → 있으면 즉시 롤백
판단 기준 요약:
| No-Go 조건 | 판단 근거 | 즉시 롤백 여부 |
|---|---|---|
| 헬스체크 3회 연속 실패 | 서비스 기본 동작 불가 | 즉시 |
| 500 에러율 5% 초과 | 사용자 경험 심각 훼손 | 즉시 |
| OutOfMemoryError 발생 | 서비스 불안정, 확산 위험 | 즉시 |
| 핵심 기능(결제/로그인) 불가 | 비즈니스 크리티컬 | 즉시 |
| 응답시간 3배 이상 증가 | 성능 저하, 연쇄 장애 가능 | 검토 후 결정 |
배포 실패가 롤백으로 이어지는 전체 흐름 — 이상 감지부터 원인 분석까지 5단계
롤백은 "이전 파일로 되돌린다"는 한 동작처럼 보이지만, 실제로는 이상 감지 → 롤백 결정 → 이전 버전으로 전환 → 상태 검증 → 원인 분석의 연속된 단계입니다. 이 흐름을 미리 알아두면 장애 한복판에서 "지금 어느 단계이고, 다음에 뭘 해야 하는지"를 놓치지 않습니다. 반대로 흐름 없이 손이 가는 대로 되돌리면 "앱만 롤백했는데 같은 오류가 계속" 나거나 "되돌릴 파일 자체가 없는" 상황에 빠집니다. 각 단계가 무엇을 하고 어디서 깨지는지를 나눠 봅니다.
[배포 완료]
│
① 이상 감지 (헬스체크 연속 실패 · 500 에러율 급증 · OOM)
│
② 롤백 결정 (사전 정의한 Go/No-Go 기준 충족 → 즉시)
│
③ 이전 버전으로 전환 (이미지 태그 되돌림 · 심볼릭 링크 스왑 · 트래픽 전환)
│ → WAR·설정·DB·인증서를 '세트'로 되돌림
│
④ 상태 검증 (헬스 200 · 에러율 정상 · 핵심 기능 동작)
│
⑤ 원인 분석 / 포스트모템 (감지·롤백·복구 시각 기록 → 재발 방지)
▼
[이전 안정 버전으로 서비스 복구]
각 단계에서 하는 일과, 막히면 나오는 증상:
| 단계 | 하는 일 | 여기서 막히면 |
|---|---|---|
| ① 이상 감지 | 헬스·에러율·OOM 등 지표로 배포 후 이상을 포착 | 감지 지표가 없음 → 사용자 민원으로 뒤늦게 인지, 영향 범위 확대 |
| ② 롤백 결정 | 사전에 합의한 Go/No-Go 기준으로 즉시 판정 | 기준 없이 "조금 더 지켜보자" → 롤백 적기를 놓쳐 장애 시간이 길어짐 |
| ③ 버전 전환 | 이미지 태그·심볼릭 링크·트래픽으로 이전 버전을 활성화 | 롤백 대상 없음(배포 전 백업 미실시) → 되돌릴 산출물 자체가 없음 |
| ④ 상태 검증 | 앱·설정·DB·인증서까지 함께 되돌아갔는지 확인 | 부분 롤백(앱만, DB는 그대로) → 스키마·데이터 상태 불일치로 오류 지속 |
| ⑤ 원인 분석 | 원인과 시각을 기록하고 재발 방지책을 도출 | DB 마이그레이션이 비가역(DROP 등) → 앱을 롤백해도 스키마 복구 불가, 스냅샷 필요 |
즉 "롤백 성공"은 이전 버전으로 되돌린 것이 아니라, 서비스가 이전 안정 상태로 실제 복구됐다는 뜻입니다. 롤백이 실패하는 지점은 대개 ③(되돌릴 대상 없음)과 ④(부분 롤백)입니다 — "앱만 되돌렸는데 같은 오류"라면 ④의 DB·설정 세트 불일치를 먼저 의심하고, ⑤에서 드러난 비가역 마이그레이션은 애초에 DB를 하위호환(확장-수축)으로 설계해 앱만 롤백해도 안전하게 만드는 것이 근본 해법입니다. 그래서 성숙한 팀은 배포 전에 백업(③)과 Go/No-Go 기준(②)을, 배포 설계 단계에서 하위호환(④⑤)을 미리 확보해 둡니다.
WAR 파일 롤백
파일 롤백 절차
배포 직후 500 에러가 쏟아집니다. Go/No-Go 기준에 따라 롤백을 결정했습니다. 그런데 정작 "어떻게 되돌리지?"를 그 자리에서 처음 생각하면 손이 떨립니다. 명령어를 잘못 치면 백업 파일까지 날릴 수 있습니다. 롤백 절차는 사고 전에 미리 문서화되어 있어야 하고, 복잡하지 않을수록 실수가 줄어듭니다.
WAR 파일 롤백은 가장 빈번하게 쓰이는 롤백 유형입니다. 절차가 단순하고 빠릅니다.
# 롤백 전 현재 상태 기록
echo "롤백 시작: $(date)" >> /opt/backup/rollback_log.txt
systemctl status tomcat >> /opt/backup/rollback_log.txt
# 1. Tomcat 중지
sudo systemctl stop tomcat
# Tomcat이 완전히 멈췄는지 확인
sleep 3
ps aux | grep tomcat | grep -v grep
# → 아무것도 안 나오면 완전 중지
# 2. 현재(실패한) WAR 삭제 — 경로와 백업 체크섬을 먼저 확인하고 승인 후 실행
test "$(readlink -f /opt/tomcat/webapps/myapp.war)" = "/opt/tomcat/webapps/myapp.war"
sudo rm -f -- /opt/tomcat/webapps/myapp.war
# 폭발 디렉터리는 별도 검토 후에만 삭제합니다(경로를 와일드카드로 확장하지 않음).
# sudo rm -rf -- /opt/tomcat/webapps/myapp
# 3. 백업 WAR 복사
BACKUP_WAR="/opt/backup/20260530_2200/myapp_current.war"
sudo cp $BACKUP_WAR /opt/tomcat/webapps/myapp.war
# 복사 확인
ls -lh /opt/tomcat/webapps/myapp.war
# 4. Tomcat 시작
sudo systemctl start tomcat
# 5. 기동 로그 확인 (에러 없는지 30초 관찰)
sudo tail -f /opt/tomcat/logs/catalina.out
설정 파일 롤백
Nginx 설정 롤백
업스트림 주소를 잘못 변경한 Nginx 설정을 reload했더니 모든 요청이 502로 떨어졌습니다. 문제는 설정 파일 자체가 문법 오류 없이 잘못된 값을 담고 있었다는 점입니다. nginx -t는 통과했지만 실제 동작은 실패한 상황입니다. 이런 경우 이전 설정 파일로 즉시 복원하는 것이 가장 빠른 복구 경로입니다. 설정 변경 전 백업을 남기는 습관이 이 한 줄의 복원을 가능하게 합니다.
설정 파일 롤백은 항상 nginx -t 검증 후 적용합니다. 잘못된 설정으로 reload하면 Nginx가 이전 설정으로 계속 동작하지만, restart하면 서비스 중단이 발생할 수 있습니다.
# 현재 설정 파일 백업 (롤백 전 현재 상태 보존)
sudo cp /etc/nginx/conf.d/myapp.conf \
/etc/nginx/conf.d/myapp.conf.failed_$(date +%Y%m%d_%H%M)
# 이전 설정 파일 복원
BACKUP_CONF="/opt/backup/20260530_2200/myapp.conf"
sudo cp $BACKUP_CONF /etc/nginx/conf.d/myapp.conf
# 설정 검증 (반드시 먼저)
sudo nginx -t
# 출력 예시:
# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration file /etc/nginx/nginx.conf test is successful
# 검증 통과 후 무중단 reload
sudo systemctl reload nginx
# 롤백 후 확인
curl -s -o /dev/null -w "HTTP %{http_code}\n" http://localhost
nginx -t 실패 시에는 절대 reload하지 않습니다. 설정 파일을 다시 확인하거나, 직전 백업 파일을 찾아서 대체합니다.
확대
DB 롤백
DB 롤백 세 가지 방법
배포와 함께 ALTER TABLE을 실행했는데 서비스에 문제가 생겼습니다. WAR 파일은 이전 버전으로 되돌렸지만, 스키마 변경은 이미 적용된 상태입니다. 이전 코드가 새 컬럼을 모르니 오류가 계속 납니다. DB 롤백은 단순히 파일을 복사하는 것이 아니라, 데이터 변경 여부에 따라 접근 방법이 완전히 달라집니다. 세 가지 상황별 방법을 미리 알아두어야 실제 장애에서 당황하지 않습니다.
DB 롤백은 파일 롤백보다 훨씬 복잡합니다. 배포 이후 실제 사용자 데이터가 쌓였을 수 있기 때문입니다.
확대
방법 1: UNDO SQL (스키마 변경만 되돌리기)
배포 시 ALTER TABLE이나 ADD COLUMN을 실행했다면, 그 반대 동작을 하는 UNDO SQL을 미리 작성해둡니다.
-- 배포 SQL (예시)
ALTER TABLE users ADD COLUMN last_login TIMESTAMP;
CREATE INDEX idx_users_last_login ON users(last_login);
-- UNDO SQL (미리 작성해둬야 함)
DROP INDEX idx_users_last_login ON users;
ALTER TABLE users DROP COLUMN last_login;
# UNDO SQL 실행
mysql -u dbuser -p myapp_db < /opt/backup/undo_v1.2.0.sql
방법 2: 트랜잭션 ROLLBACK (배포 중 문제)
배포 스크립트 안에서 DB 변경을 트랜잭션으로 감쌌다면, 오류 시 전체를 ROLLBACK할 수 있습니다.
BEGIN;
ALTER TABLE orders ADD COLUMN discount_rate DECIMAL(5,2);
UPDATE orders SET discount_rate = 0;
-- 여기서 오류 발생 시 → ROLLBACK
ROLLBACK; -- 모든 변경 취소
-- 또는
COMMIT; -- 정상이면 커밋
방법 3: 스냅샷 복원 (최후의 수단)
RDS 스냅샷, mysqldump 백업 등을 이용해 전체 DB를 이전 시점으로 복원합니다. 배포 이후 쌓인 실제 데이터가 모두 소실되므로, 사용자에게 공지 후 진행하는 경우에만 사용합니다.
# mysqldump 백업 복원 (예시)
mysql -u root -p myapp_db < /opt/backup/myapp_db_20260530_2100.sql
DB 롤백이 불가한 경우:
| SQL 유형 | 롤백 가능 여부 | 이유 |
|---|---|---|
| INSERT/UPDATE/DELETE | UNDO SQL 가능 | 역연산 작성 가능 |
| ADD COLUMN | UNDO SQL 가능 | DROP COLUMN으로 역연산 |
| DROP TABLE | 불가 (스냅샷 필요) | 데이터 소실, 복구 불가 |
| TRUNCATE | 불가 (스냅샷 필요) | 트랜잭션 밖에서 즉시 반영 |
| DROP COLUMN | 불가 (컬럼 데이터 소실) | 데이터 이미 삭제됨 |
이 때문에 운영 DB에서 DROP TABLE이나 TRUNCATE는 작업계획서에서 별도로 승인을 받아야 하는 위험 작업으로 분류됩니다.
인증서 롤백
SSL 인증서 롤백 절차
인증서 교체 작업을 마쳤는데 일부 브라우저에서 "인증서를 신뢰할 수 없음" 오류가 납니다. 중간 인증서(chain)가 누락됐거나 도메인이 맞지 않는 인증서를 배포한 경우입니다. 사용자에게는 사이트 자체가 먹통처럼 보입니다. 이 상황에서 가장 빠른 복구는 이전 인증서로 즉시 되돌리는 것입니다. 인증서 교체 전에 반드시 이전 파일을 백업해두어야 이 복구가 가능합니다.
인증서를 교체했다가 문제가 생겼을 때, 이전 인증서로 되돌리는 절차입니다.
# 백업 인증서 위치 확인
ls -la /opt/backup/20260530_2200/ssl/
# → server.crt, server.key, server_chain.crt 확인
# 현재 인증서 백업 (롤백 실패 대비)
sudo cp /etc/nginx/ssl/server.crt /etc/nginx/ssl/server.crt.failed
sudo cp /etc/nginx/ssl/server.key /etc/nginx/ssl/server.key.failed
# 이전 인증서 복원
sudo cp /opt/backup/20260530_2200/ssl/server.crt /etc/nginx/ssl/
sudo cp /opt/backup/20260530_2200/ssl/server.key /etc/nginx/ssl/
sudo cp /opt/backup/20260530_2200/ssl/server_chain.crt /etc/nginx/ssl/
# 인증서 유효성 확인
sudo openssl x509 -noout -dates -in /etc/nginx/ssl/server.crt
# 출력 예시:
# notBefore=Jan 1 00:00:00 2026 GMT
# notAfter=Dec 31 23:59:59 2026 GMT
# Nginx 설정 검증
sudo nginx -t
# 검증 통과 후 재시작 (인증서는 reload로도 반영됨)
sudo systemctl reload nginx
# HTTPS 접속 확인
curl -sk https://localhost -o /dev/null -w "HTTP %{http_code}\n"
Tomcat에서 JKS(Java Keystore) 방식으로 SSL을 처리하는 경우:
# JKS 파일 복원
sudo cp /opt/backup/20260530_2200/keystore.jks /opt/tomcat/conf/keystore.jks
# Tomcat 재시작 (인증서 변경은 Tomcat 재시작 필요)
sudo systemctl restart tomcat
롤백 체크리스트
롤백 체크리스트를 한 항목씩 확인하며 진행합니다. 체크박스를 실제로 체크해가며 진행하면 빠진 단계 없이 완료할 수 있습니다.
롤백 체크리스트 (WAR 파일)
□ 롤백 결정 기록 (시각, 이유, 결정자)
□ 현재 실패한 WAR 파일 이름 기록
□ 백업 WAR 파일 위치 확인
→ ls -la /opt/backup/{날짜}/myapp_current.war
□ Tomcat shutdown 완료
→ systemctl stop tomcat
→ ps aux | grep tomcat (프로세스 없음 확인)
□ webapps/myapp 폴더 및 WAR 삭제
→ rm -rf /opt/tomcat/webapps/myapp*
□ 백업 WAR 복사
→ cp /opt/backup/{날짜}/myapp_current.war /opt/tomcat/webapps/myapp.war
□ Tomcat startup
→ systemctl start tomcat
□ 기동 로그 확인 (30초간 ERROR 없음)
→ tail -30 /opt/tomcat/logs/catalina.out
□ 헬스체크 URL 확인
→ curl http://localhost:8080/myapp/health → HTTP 200
□ 주요 기능 동작 확인 (로그인 등)
□ 담당자(팀장) 보고
□ 롤백 완료 기록 (시각, 결과)
sudo systemctl stop tomcat && sleep 3 && ps aux | grep tomcat | grep -v grep- systemctl stop tomcat 후 ps aux | grep tomcat으로 프로세스 완전 종료 확인 — 프로세스가 남아있으면 30초 대기 후 kill -9 필요. 종료 안 된 상태에서 파일 교체하면 롤백 실패
- /opt/backup/ 경로에 타임스탬프 포함 WAR 파일이 있는지 먼저 확인 — 파일이 없으면 백업 없이 배포한 것. ls -lh /opt/backup/*.war로 크기도 함께 확인(1KB 미만이면 빈 파일)
- Tomcat 시작 후 catalina.out에서 "Server startup in" 메시지와 헬스체크 HTTP 200이 모두 확인되면 롤백 성공 — 둘 중 하나라도 실패하면 WAR 외 DB 스키마 불일치 여부 추가 점검
설정 파일 롤백은 반드시 nginx -t 검증 후 진행합니다.
# 이전 설정 복원
sudo cp /opt/backup/$(ls /opt/backup | tail -1)/myapp.conf \
/etc/nginx/conf.d/myapp.conf
# 검증
sudo nginx -t
# 이상 없으면 reload
sudo systemctl reload nginx
# 확인
curl -s -o /dev/null -w "HTTP %{http_code}\n" http://localhost
sudo nginx -t- systemctl status tomcat의 Active 줄을 먼저 본다 — active (running)이면 기동 성공. failed이면 catalina.out 마지막 20줄로 즉시 원인 확인(journalctl -u tomcat -n 20)
- 헬스체크 HTTP 200이 오고 catalina.out에 Exception이 없어도 — 로그인과 주요 화면에서 기능 동작 여부를 수동으로 30초간 확인해야 롤백 완료로 선언 가능
- 롤백 완료 시각 기록 없이 장애 보고서를 작성하면 RTO(복구 목표 시간) 측정이 불가능 — 장애 감지, 롤백 시작, 서비스 복구 세 시각을 모두 기록해야 사후 개선에 활용 가능
트러블슈팅
상황: WAR를 이전 버전으로 롤백했는데도 500 에러가 계속 납니다. catalina.out에 Unknown column 'new_field' in 'field list'가 보입니다.
원인은 버전 불일치입니다. 이전 WAR 코드가 DB에 없는 컬럼을 읽으려는 것이 아니라, 새 WAR가 추가한 컬럼이 DB에 남아있고 이전 WAR가 그것을 처리 못하는 경우도 있습니다. 또는 이전 WAR가 특정 컬럼을 기대하는데 새 배포에서 컬럼명이 바뀐 경우도 있습니다.
# 에러 메시지에서 컬럼명 확인
grep "Unknown column\|Column.*not found\|Table.*doesn't exist" \
/opt/tomcat/logs/catalina.out | tail -5
# DB 현재 스키마 확인
mysql -u dbuser -p myapp_db -e "SHOW COLUMNS FROM orders;"
# 이전 버전 WAR가 기대하는 스키마와 비교
# (UNDO SQL이 준비됐다면 실행)
mysql -u dbuser -p myapp_db < /opt/backup/undo_v1.2.0.sql
배포 시 WAR + DB migration을 함께 진행했다면, 롤백도 WAR + DB migration 되돌리기를 함께 해야 합니다. 작업계획서에 이 연계 관계가 명시돼 있어야 합니다.
상황: 롤백을 시도하려는데 /opt/backup/에 오늘 날짜 파일이 없습니다. 배포 전 백업을 건너뛴 것입니다. 이전 버전 WAR를 구할 수 없는 상황입니다.
# 1. Git 태그에서 이전 버전 빌드 파일 찾기
git log --oneline -10 # 이전 커밋 확인
git show v1.1.0:myapp.war # 가능하면 추출
# 2. CI/CD 시스템에서 이전 빌드 아티팩트 다운로드
# Jenkins: Jobs > myapp > Build History > v1.1.0 > Artifacts
# GitLab: Pipelines > Jobs > download artifacts
# 3. 다른 환경(스테이징)에서 복사
scp staging-server:/opt/tomcat/webapps/myapp.war \
/opt/tomcat/webapps/myapp_rollback.war
# 4. 최후 수단: 스테이징 환경에서 재빌드
이 상황을 겪고 나면 배포 전 백업이 얼마나 중요한지 체감하게 됩니다. 이후부터는 배포 스크립트에 백업 단계를 자동으로 포함시키는 것이 좋습니다.
# 배포 스크립트 첫 줄에 자동 백업 추가 예시
BACKUP_DIR="/opt/backup/$(date +%Y%m%d_%H%M)"
mkdir -p $BACKUP_DIR
cp /opt/tomcat/webapps/myapp.war $BACKUP_DIR/ || {
echo "ERROR: 백업 실패. 배포를 중단합니다."
exit 1
}
echo "백업 완료: $BACKUP_DIR/myapp.war"
심화 — 되돌릴 필요를 없애는 설계, 되돌려도 안전한 설계
심화: 하위호환(확장-수축)과 커넥션 드레이닝 — 롤백이 항상 안전하려면
성숙한 롤백 전략의 목표는 '빠르게 되돌리기'가 아니라 '되돌릴 필요를 줄이고, 되돌려도 안전하게' 만드는 것입니다. 그러려면 배포와 스키마를 애초에 하위호환으로 설계해야 합니다.
- 하위호환(backward-compatible)의 의미: 신버전 스키마가 구버전 코드도 그대로 동작하는 상위집합이어야 앱만 롤백해도 안전합니다. 컬럼 rename·drop, 즉시 NOT NULL 추가, enum 의미 변경 같은 파괴적 변경은 롤백을 깨뜨립니다.
- 확장-수축(expand-contract) 2단계: (1) 확장 — 새 컬럼을 nullable로 추가해 신·구 코드가 둘 다 도는 상태로 배포합니다. (2) 백필·전환 — 데이터를 채우고 앱이 새 컬럼을 쓰게 전환합니다. (3) 수축 — 아무도 옛 컬럼을 안 쓰는 게 확인된 '다음' 릴리스에서야 옛 컬럼을 제거합니다. 이렇게 하면 각 배포의 직전 버전으로 앱만 롤백해도 스키마가 항상 맞습니다.
- 무중단 배포와 커넥션 드레이닝: 롤백·배포로 인스턴스를 교체할 때 진행 중이던 요청을 끊지 않으려면, 로드밸런서에서 먼저 인스턴스를 빼(deregister) 새 요청 유입을 막고, 이미 처리 중인 요청이 끝날 때까지 기다렸다가(drain, 예: 30초) 종료합니다. 드레이닝 없이 바로 죽이면 진행 중 트랜잭션이 잘려 롤백 순간에도 사용자 에러가 튑니다 — 앱의 graceful shutdown(SIGTERM 후 유예)과 짝을 이뤄야 합니다.
- 한계: 하위호환 설계는 릴리스가 2단계로 늘고, '수축'을 잊으면 옛 컬럼이 기술부채로 남습니다. 그럼에도 '언제든 앱만 롤백하면 복구된다'는 안전성이 주는 값이 훨씬 큽니다.
상황: 신버전을 배포했다가 문제가 생겨 앱만 이전 버전으로 롤백했습니다. 스키마는 건드리지 않았고(새 컬럼은 nullable로 추가돼 구버전도 원래는 동작해야 함), 그런데 롤백 후 신버전이 떠 있던 몇 분 사이에 가입·주문한 특정 사용자에게서만 500이 납니다. catalina.out에는 IllegalArgumentException: No enum constant ...가 보입니다.
원인: 신버전이 새 컬럼에 구버전이 모르는 값(예: 새 status enum PENDING_REVIEW, 또는 새 JSON 형식)을 써 넣었는데, 롤백된 구버전 코드가 그 값을 enum으로 매핑하다 실패했습니다. 스키마(구조)는 하위호환이었지만 데이터(내용)가 전방호환(forward-compatible)이 아니었던 것입니다 — 구버전은 신버전이 만든 데이터를 읽지 못합니다.
진단: 에러가 전체가 아니라 '신버전 가동 구간에 생성된 행'에서만 나는지 생성 시각으로 필터해 확인합니다. catalina.out에서 No enum constant·JSON 파싱 에러를 찾고, 문제 행의 새 컬럼 값 분포를 조회해 구버전이 모르는 값이 들어갔는지 봅니다.
해결: 즉시로는 그 몇 분간 생성된 행의 신규 값을 구버전이 이해하는 기본값으로 보정(데이터 패치)하거나, 구버전이 그 값을 관대히 처리하도록 전진 수정(핫픽스)합니다. 근본적으로는 스키마뿐 아니라 데이터도 롤백 창 동안 양방향 호환이 되게 설계합니다 — 새 enum·형식은 '구버전이 무시·기본 처리할 수 있게' 관용적 읽기를 먼저 배포한 뒤에야 쓰기를 시작하는 2단계로, 확장-수축 원칙을 데이터에까지 확장합니다.
실제 업무에서 이 지식이 쓰이는 상황:
현장에서 롤백이 느린 팀과 빠른 팀의 차이는 롤백 절차가 문서화됐는가 여부입니다. 절차가 있는 팀은 No-Go 기준을 충족하는 순간 즉시 체크리스트를 꺼내 따라갑니다. 절차가 없는 팀은 Slack에 "어떻게 하죠?"를 올리는 것부터 시작합니다.
롤백 후 재발 방지 분석:
롤백이 완료된 후, 24시간 안에 짧은 포스트모템을 작성합니다.
롤백 후 재발 방지 분석:
발생 일시: 2026-05-30 22:30
원인: 신규 WAR에서 DB connection pool 설정이 누락됨
(새 버전 context.xml에 maxActive 값이 기본값 8로 초기화됨)
영향: Tomcat 기동 후 10분간 커넥션 고갈, 500 에러 발생
롤백 완료: 22:47 (발견 후 17분)
재발 방지:
1. 배포 체크리스트에 context.xml 설정값 비교 항목 추가
2. 스테이징 환경에서 커넥션 풀 exhaustion 테스트 추가
3. 배포 스크립트에 context.xml diff 출력 단계 추가
롤백 경험은 팀의 소중한 자산입니다. 기록하지 않으면 같은 실수가 반복됩니다.
명령어·단축키 빠른 참조
이 모듈에서 다룬 파일/설정/DB/인증서 롤백 명령을 유형별로 모았습니다. No-Go 판정 후 "예" 열 순서대로 즉시 실행하면 됩니다.
| 명령어/단축키 | 용도 | 자주 쓰는 예 |
|---|---|---|
systemctl stop/start | 롤백 위한 WAS 재기동 | systemctl stop tomcat → WAR 교체 → start |
ps aux | grep tomcat | 완전 종료 확인 | sleep 3 && ps aux | grep tomcat | grep -v grep (없어야 정상) |
cp (백업 복원) | 이전 WAR·설정 되돌리기 | cp /opt/backup/20260530/myapp_current.war …/myapp.war |
rm -rf webapps/앱* | 실패한 배포물 제거 | rm -rf /opt/tomcat/webapps/myapp* |
mysql < undo.sql | 스키마 변경 UNDO(역마이그레이션) | mysql myapp_db < /opt/backup/undo_v1.2.0.sql |
SHOW COLUMNS | 현재 스키마 불일치 확인 | SHOW COLUMNS FROM orders; (Unknown column 진단) |
nginx -t && reload | 설정 롤백 검증 후 반영 | nginx -t && systemctl reload nginx |
openssl x509 -noout -dates | 복원 인증서 유효기간 확인 | openssl x509 -noout -dates -in server.crt |
curl -sk -w '%{http_code}' | 롤백 후 HTTPS 헬스체크 | curl -sk https://localhost -o /dev/null -w '%{http_code}' |
md5sum | 백업 WAR 무결성·버전 대조 | 복원 파일과 원본 체크섬 비교 |
git show / scp | 백업 없을 때 이전 산출물 확보 | scp staging:/opt/tomcat/webapps/myapp.war . |
관련 모듈로 더 깊이:
- WAR/JAR/정적파일 배포와 배포 스크립트 작성 — 롤백이 쉬운 배포 구조(블루-그린, 심볼릭 링크)를 설계하는 법
- 작업계획서 작성과 변경관리 체크리스트 — 롤백 절차를 작업계획서에 사전 명시해두는 법
- 타임라인 분석과 재발 방지 대책 수립 — 롤백 경험을 장애 보고서와 재발 방지 항목으로 남기는 법
다음 모듈에서는 이런 장애 상황에서 HTTP 에러 코드를 해석하고 로그만으로 원인을 찾는 방법을 다룹니다.