이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.

  • Build, Deploy, Observe, Recover는 각각 어떤 운영 책임을 가지는가?
  • 네 단계를 끊어서 보지 않으면 어떤 장애 판단이 늦어지는가?
  • Spring 백엔드 서비스에서 각 단계의 최소 증거는 무엇인가?

개요

Build, Deploy, Observe, Recover는 운영을 네 개의 동사로 나누어 보는 방법이다.

Build는 실행할 것을 만들고, Deploy는 실행 환경에 반영하고, Observe는 실제 상태를 보고, Recover는 나쁜 상태에서 빠져나온다.

이 네 단계는 순서가 있지만 한 번만 돌고 끝나는 절차가 아니다.

배포할 때마다 다시 돌고, 장애가 날 때마다 역방향으로 증거를 추적한다.

한눈에 보는 흐름

Build
  source code -> tested jar/image -> version tag
Deploy
  artifact -> runtime config -> server/container -> traffic
Observe
  health -> logs -> metrics -> traces -> alerts
Recover
  rollback -> traffic shift -> mitigation -> restore or forward fix

네 단계의 경계가 분명해야 장애 중 질문도 분명해진다.

  • Build가 문제인가? 같은 commit에서 같은 artifact가 만들어졌는가?
  • Deploy가 문제인가? artifact는 올라갔지만 트래픽이 새 인스턴스로 가지 않는가?
  • Observe가 문제인가? 사용자는 실패하는데 지표와 로그가 비어 있는가?
  • Recover가 문제인가? 문제가 보이지만 되돌릴 artifact나 우회 절차가 없는가?

Build

Build의 목표는 “내 컴퓨터에서 동작하는 코드”를 “운영에서 재현 가능한 실행 단위”로 바꾸는 것이다.

Spring 백엔드에서는 보통 jar 파일이나 Docker image가 build 결과물이다.

좋은 build는 다음 조건을 만족한다.

  • commit hash와 artifact version이 연결된다.
  • 테스트 결과가 남는다.
  • 환경별 설정이 artifact에 섞이지 않는다.
  • 같은 source에서 같은 결과물을 만들 수 있다.
  • 취약한 dependency나 깨진 migration이 배포 전에 드러난다.
./gradlew clean test bootJar
docker build -t registry.example.com/order-api:2026.06.30-3f21a9c .
docker push registry.example.com/order-api:2026.06.30-3f21a9c

latest만 사용하면 장애 중 어떤 코드가 올라갔는지 설명하기 어렵다.

운영 artifact는 사람이 알아볼 수 있는 release version과 Git commit을 함께 가져야 한다.

Deploy

Deploy의 목표는 artifact를 운영 runtime에 반영하고 트래픽을 안전하게 연결하는 것이다.

Deploy는 단순 파일 복사가 아니다.

Spring profile, 환경 변수, Secret, port, DB migration, Nginx upstream, Load Balancer health check가 함께 맞아야 한다.

SPRING_PROFILES_ACTIVE: prod
SERVER_PORT: 8080
MANAGEMENT_ENDPOINT_HEALTH_PROBES_ENABLED: "true"

배포가 실패하는 흔한 이유는 코드 자체보다 경계 값이다.

  • prod profile이 아니라 local profile로 뜬다.
  • Secret 이름은 맞지만 권한이 없어 읽지 못한다.
  • container port와 Nginx upstream port가 다르다.
  • DB migration이 구버전 애플리케이션과 호환되지 않는다.
  • health check 경로는 살아 있지만 핵심 dependency가 죽어 있다.

Observe

Observe의 목표는 서비스의 실제 상태를 운영자가 판단할 수 있게 만드는 것이다.

좋은 관측은 “로그가 많다”가 아니라 “사용자 영향과 원인 후보를 좁힐 수 있다”에 가깝다.

Spring 백엔드에서 최소로 연결할 신호는 다음이다.

  • /actuator/health
  • HTTP request count, latency, 4xx, 5xx
  • JVM memory, GC, thread
  • HikariCP active, idle, pending, timeout
  • 주요 외부 API latency와 error
  • structured log와 correlation id
  • trace id 기반 요청 경로
management.endpoints.web.exposure.include=health,info,prometheus
management.metrics.tags.application=order-api
logging.pattern.level=%5p [traceId=%X{traceId:-},spanId=%X{spanId:-}]

관측이 부족하면 장애 중 선택지가 모두 추측이 된다.

Rollback을 해야 하는지, 특정 기능만 차단하면 되는지, DB나 외부 API가 문제인지 판단할 수 없다.

Recover

Recover의 목표는 서비스를 정상 상태로 되돌리거나 사용자 영향을 제한하는 것이다.

복구는 크게 네 가지로 나뉜다.

  • rollback: 이전 artifact나 설정으로 되돌린다.
  • roll forward: 작은 수정으로 현재 방향을 유지한다.
  • mitigation: rate limit, feature flag off, read-only mode로 피해를 줄인다.
  • restore: 데이터나 상태를 백업 지점으로 복구한다.

복구 판단은 배포 전략과 데이터 변경 여부에 따라 달라진다.

코드만 바뀐 배포는 rollback이 비교적 쉽다.

DB schema나 데이터 backfill이 포함된 배포는 이전 코드로 되돌리는 것만으로 해결되지 않을 수 있다.

이때는 forward fix, 호환 코드, restore, 수동 데이터 정정 중 무엇이 안전한지 따져야 한다.

네 단계가 끊어질 때 생기는 문제

Build만 있고 Deploy 기준이 없으면 artifact는 있지만 어디에 어떻게 반영됐는지 모른다.

Deploy만 있고 Observe가 없으면 배포 성공 로그를 보고도 사용자가 실패하는 상황을 놓친다.

Observe만 있고 Recover가 없으면 장애를 정확히 보면서도 피해를 줄이지 못한다.

Recover만 문서화되어 있고 Build version이 없으면 어떤 대상으로 돌아갈지 모른다.

운영 체크리스트

  • Build 결과물에 commit hash와 version tag가 있는가?
  • artifact 안에 prod Secret이 들어가지 않는가?
  • Deploy 전후 smoke test API가 정해져 있는가?
  • health check와 실제 사용자 API 검증을 구분하는가?
  • 로그, metric, trace가 release version과 연결되는가?
  • rollback 대상 version과 명령이 문서에 있는가?
  • DB migration이 포함되면 rollback 가능성 대신 호환성을 검토했는가?

장애 신호

  • 배포는 성공했지만 새 image tag가 서버에 없다.
  • health check는 성공하지만 사용자 API p95가 급증한다.
  • Nginx 502가 늘지만 Spring access log에는 요청이 없다.
  • trace id 없이 에러 로그만 쌓여 사용자 요청을 따라갈 수 없다.
  • rollback하려는데 이전 artifact가 registry에서 사라졌다.
  • rollback 후에도 DB schema 오류가 계속 난다.

하지 말아야 할 대응

  • Build 실패를 무시하고 로컬에서 만든 artifact를 운영에 올리지 않는다.
  • Deploy 실패 중 Secret이나 profile 값을 여러 개 동시에 바꾸지 않는다.
  • Observe가 없는데 “일단 괜찮아 보인다”고 배포를 종료하지 않는다.
  • Recover 절차 없이 DB destructive change를 실행하지 않는다.
  • latest tag를 rollback 기준으로 삼지 않는다.

인프라 담당자와 공유할 자료

Build: order-api 2026.06.30-3f21a9c, Gradle test passed, image digest sha256:...
Deploy: prod, 2 of 4 instances updated, Nginx upstream order-api-v2
Observe: 14:12부터 /api/orders p95 240ms -> 2.9s, 5xx 3.1%
Recover: 이전 tag 2026.06.29-a819e00 존재, DB migration은 additive only
요청: LB target health와 upstream별 5xx 비율 확인

이 형식은 네 단계의 증거를 한 번에 제공한다.

실전 팁

  • Build 단계에서는 재현성과 version을 먼저 본다.
  • Deploy 단계에서는 config와 traffic 연결을 먼저 본다.
  • Observe 단계에서는 사용자 영향과 시스템 지표를 연결한다.
  • Recover 단계에서는 되돌릴 수 있는 변경과 되돌리기 어려운 변경을 구분한다.
  • 네 단계 중 가장 약한 단계가 실제 운영 품질을 결정한다.

위험 신호!

  • CI는 있지만 배포된 artifact version을 운영에서 확인할 방법이 없다.
  • 배포가 끝났다는 메시지는 있지만 smoke test 결과가 없다.
  • metric은 있지만 어떤 release에서 나빠졌는지 알 수 없다.
  • 장애 중 rollback 권한자가 누구인지 모른다.
  • 복구 절차가 DB 변경과 애플리케이션 변경을 구분하지 않는다.

확인 질문

확인 질문

  • Build와 Deploy를 구분해야 하는 이유는 무엇인가?
    • 실행 가능한 artifact를 만드는 문제와 그 artifact를 올바른 환경과 트래픽에 연결하는 문제는 실패 원인과 확인 증거가 다르기 때문이다.
  • Observe 없이 Recover가 어려운 이유는 무엇인가?
    • 어떤 사용자가 얼마나 영향을 받고 어떤 지표가 나빠졌는지 모르면 rollback, traffic shift, 기능 차단 중 무엇을 선택할지 판단할 수 없기 때문이다.
  • DB migration이 포함된 배포에서 rollback을 조심해야 하는 이유는 무엇인가?
    • 애플리케이션 버전만 되돌려도 DB schema나 데이터가 이미 바뀌어 구버전과 호환되지 않을 수 있기 때문이다.

참고 문서