사전 지속적 전달
패턴

지속적 전달

gabury1고친 사람 github-actions[bot]

지속적 전달은 소프트웨어를 언제든 내보낼 수 있는 상태로 붙들어 두는 방식입니다. 코드가 바뀔 때마다 빌드와 테스트와 배포 준비를 자동으로 거칩니다. 실제로 사용자에게 내보낼지는 사람이 버튼 하나로 고릅니다.

쉽고 빠른 이해

지속적 전달은 "지금 당장 내보내도 되나"에 늘 "된다"고 답할 수 있게 해 줍니다. 커밋 하나가 들어오면 자동으로 빌드되고 테스트를 거쳐 배포 직전까지 갑니다.

이게 없으면 몇 달 치 변경을 모아 한 번에 내보내게 됩니다. 내보내는 날마다 수작업 절차를 따라 하느라 긴장합니다. 무언가 깨지면 수백 개 변경 중 무엇이 원인인지 찾아야 합니다.

어떻게 도는가:

  1. 개발자가 변경을 공용 브랜치에 합칩니다
  2. 자동화된 단계가 빌드하고 테스트하고 테스트용 서버에 올려 봅니다
  3. 끝까지 통과한 버전은 언제든 버튼 하나로 운영 서버에 나갈 수 있습니다

대가는 자동 테스트와 배포 자동화를 먼저 만들어 두고 계속 손봐야 한다는 것입니다. 테스트가 느리거나 자주 헛돌면 흐름 전체가 막힙니다.

팀이 배포를 직접 쥔 서버 서비스에 잘 맞습니다. 사용자가 설치하는 소프트웨어나 앱 마켓 심사를 거치는 앱은 효과가 줄어듭니다.

상세

이 절은 먼저 드물게 내보내는 팀이 무엇에 막히는지 봅니다. 그다음 지속적 전달이 그 문제를 풀려고 거는 약속을 봅니다.

뒤로 가면서 배포 파이프라인의 단계, 한 번 만든 결과물을 끝까지 들고 가는 원칙, 이웃한 두 실천과의 차이, 이 방식이 요구하는 것과 대가를 차례로 다룹니다.

드물게 내보내는 팀의 문제

석 달에 한 번 릴리스하는 팀을 떠올려 봅시다. 릴리스는 새 버전을 사용자에게 내놓는 일입니다. 석 달 동안 쌓인 변경은 수백 개입니다. 릴리스 주에야 전부 모아 처음으로 함께 돌려 봅니다.

릴리스는 보통 배포와 함께 일어납니다. 배포는 코드를 운영 서버에 올리는 일입니다. 뒤 절 「배포와 릴리스를 떼어 놓는다」 전까지는 둘이 한꺼번에 일어난다고 봅니다. 그래서 둘을 묶어 「내보낸다」고 부릅니다.

그러면 세 가지가 한꺼번에 터집니다. 서로 모르고 고친 코드가 부딪힙니다. 배포 절차는 석 달 전에 한 번 해 본 수작업이라 누군가의 메모에만 남아 있습니다. 운영에서 장애가 나면 수백 개 변경 가운데 범인을 찾아야 합니다.

위험이 크니 릴리스를 더 미룹니다. 미룰수록 쌓이는 변경이 늘어 위험이 더 커집니다. 지속적 전달은 이 고리를 거꾸로 돌립니다. 내보내는 일이 아프다면 더 자주 해서 작게 만들자는 것입니다.

언제든 내보낼 수 있는 상태

지속적 전달의 중심 약속은 하나입니다. 공용 브랜치의 최신 버전은 언제나 운영에 내보낼 수 있어야 합니다. 여기서 공용 브랜치는 팀 전체가 변경을 합치는 버전 관리 브랜치입니다.

이 약속을 지키려면 변경 하나하나가 곧 릴리스 후보가 됩니다. 릴리스 후보는 검사를 다 통과하면 그대로 내보낼 수 있는 버전입니다. "다음 릴리스 때 정리하자"는 미룰 곳이 사라집니다.

그러면 "언제 내보내나"는 기술 문제가 아니게 됩니다. 영업 일정이나 공지 시점에 맞춰 고르는 사업상 결정이 됩니다. 기술 쪽은 어느 순간에 물어도 준비돼 있다는 것만 보장합니다.

배포 파이프라인

약속을 사람이 매번 확인할 수는 없습니다. 그래서 커밋이 운영 직전까지 가는 길을 자동화된 단계로 이어 둡니다. 이 길을 배포 파이프라인이라고 부릅니다.

아래 그림은 커밋 하나가 지나가는 단계입니다. 앞 단계를 통과해야 다음 단계로 갑니다. 어느 단계든 실패하면 그 버전은 거기서 멈춥니다.

그림의 아티팩트는 빌드가 만든 실행 파일이나 컨테이너 이미지입니다. 내보낼 물건 그 자체라서 한 번 만들어 저장해 둡니다. 뒤 단계는 그것을 꺼내 씁니다.

flowchart TD
    A["커밋"] --> B["빌드 · 단위 테스트"]
    B --> C["아티팩트 저장"]
    C --> D["인수 테스트"]
    D --> E["스테이징 배포 · 확인"]
    E --> F(["릴리스 후보"])
    F -->|사람이 버튼을 누른다| G["운영 배포"]

그림 끝의 둥근 칸 「릴리스 후보」는 단계가 아닙니다. 앞 단계를 모두 통과한 버전이 이른 상태입니다.

앞쪽 단계는 빠르고 싸게 둡니다. 단위 테스트는 함수 하나, 클래스 하나를 떼어 보는 테스트라 몇 분 안에 끝납니다. 개발자가 자기 변경이 무엇을 깼는지 바로 알게 하려는 것입니다.

뒤쪽 단계는 느리지만 실제에 가깝습니다. 인수 테스트는 사용자가 기대하는 동작을 시스템 전체에 대고 확인하는 테스트입니다. 단위 테스트가 부품을 본다면 인수 테스트는 조립된 제품을 봅니다.

스테이징 환경은 운영과 최대한 같게 꾸린 연습용 서버입니다. 여기까지 통과하면 운영에서도 돌 것이라는 믿음이 쌓입니다.

마지막 화살표만 사람이 고릅니다. 운영 서버로 나가는 순간입니다. 운영 서버는 흔히 프로덕션이라고도 부릅니다. 이 문서에서는 운영이라고 적습니다.

이 버튼을 없애고 통과한 버전을 전부 자동으로 내보내면 지속적 배포가 됩니다. 사람이 고르는 순간이 사라지는 것입니다.

한 번 만든 결과물을 끝까지 들고 간다

앞에서 본 아티팩트는 빌드가 만든 실행 파일이나 컨테이너 이미지입니다. 파이프라인은 첫 단계에서 아티팩트를 한 번만 만듭니다. 뒤 단계는 전부 그것을 받아 씁니다.

단계마다 다시 빌드하면 테스트한 것과 내보내는 것이 달라질 수 있습니다. 그사이 라이브러리가 바뀌었거나 빌드 서버 설정이 달랐을 수 있기 때문입니다. 같은 아티팩트를 들고 가야 "테스트를 통과한 바로 그 버전"을 내보낸다고 말할 수 있습니다.

환경마다 달라야 하는 값은 아티팩트 밖에 둡니다. 데이터베이스 주소나 접속 비밀번호 같은 설정은 배포할 때 환경이 넣어 줍니다.

배포 절차와 환경도 자동화한다

아티팩트가 같아도 올리는 방법이 환경마다 다르면 운영에서 처음 보는 실패가 납니다. 그래서 스테이징과 운영에 같은 배포 스크립트를 씁니다. 스테이징에 배포할 때마다 운영 배포를 미리 연습하는 셈입니다.

서버와 네트워크 설정도 사람이 콘솔에서 만지지 않고 코드로 적어 둡니다. 이것을 코드형 인프라라고 부릅니다. 환경을 코드로 적으면 스테이징과 운영이 어긋났을 때 차이를 비교할 수 있습니다. 망가진 환경을 다시 만들 수도 있습니다.

지속적 통합·지속적 배포와의 차이

이름이 비슷한 실천이 둘 있습니다. 셋은 한 줄로 이어집니다. 지속적 통합은 변경을 자주 공용 브랜치에 합치고 합칠 때마다 빌드와 테스트를 돌리는 실천입니다. 지속적 전달은 그 위에 배포 준비까지 얹습니다. 지속적 배포는 마지막 버튼까지 자동으로 누릅니다.

자동으로 하는 것 사람이 고르는 것
지속적 통합 합치기마다 빌드 · 테스트 무엇을 언제 배포할지 전부
지속적 전달 운영 직전까지 전부 운영에 내보내는 시점
지속적 배포 운영 배포까지 전부 없음

아래로 갈수록 사람이 고르는 몫이 줄어듭니다. 지속적 배포를 하는 팀은 지속적 전달도 하고 있습니다. 거꾸로는 성립하지 않습니다. 줄임말 CD 는 Continuous Delivery 와 Continuous Deployment 를 둘 다 줄인 꼴입니다. 그래서 어느 실천을 말하는지 문맥으로 가려 읽어야 합니다.

배포와 릴리스를 떼어 놓는다

앞 절들은 배포와 릴리스를 묶어 「내보낸다」고 불렀습니다. 지속적 전달을 하면 코드가 자주 운영에 올라갑니다. 그런데 기능은 아직 사용자에게 보이면 안 될 때가 있습니다.

이때 코드를 운영에 올리는 배포와 기능을 사용자에게 여는 릴리스를 따로 다룹니다. 코드는 운영에 올라가 있어도 사용자는 아직 새 기능을 못 봅니다.

흔한 방법은 기능 플래그입니다. 기능 플래그는 코드 안에서 기능을 켜고 끄는 스위치입니다. 새 기능을 꺼 둔 채 배포해 두었다가 준비가 되면 스위치만 켭니다.

이 방식이 요구하는 것

파이프라인은 테스트가 믿을 만할 때만 뜻이 있습니다. 자동 테스트가 빈약하면 통과해도 내보낼 수 있다는 보장이 안 됩니다. 반대로 테스트가 가끔 이유 없이 실패하면 팀이 실패를 무시하는 습관이 생깁니다.

변경을 작게 자주 합쳐야 합니다. 긴 브랜치에서 몇 주씩 작업하면 공용 브랜치가 늘 내보낼 수 있는 상태라는 약속이 그 브랜치에는 닿지 않습니다. 그래서 짧게 쪼개 자주 합치는 트렁크 기반 개발과 함께 쓰는 일이 많습니다.

파이프라인이 빨갛게 멈추면 다른 일보다 먼저 고칩니다. 멈춘 채 변경이 계속 쌓이면 어느 변경이 깨뜨렸는지 다시 찾기 어려워집니다.

대가

자동 테스트와 배포 자동화를 먼저 만들어야 합니다. 기능 개발에 쓸 시간을 파이프라인에 들이는 셈입니다. 파이프라인도 코드라서 계속 손봐야 합니다.

테스트가 느리면 파이프라인 전체가 느려집니다. 커밋마다 결과를 오래 기다리면 개발자는 변경을 모아서 합치기 시작합니다. 그러면 작게 자주 합친다는 전제가 무너집니다.

데이터베이스 스키마 마이그레이션은 따로 신경 써야 합니다. 스키마 마이그레이션은 테이블 구조를 바꾸는 작업입니다. 코드는 옛 버전으로 쉽게 되돌립니다. 바뀐 테이블 구조는 그렇게 쉽게 되돌아가지 않습니다.

새 버전에 문제가 생기면 직전 버전 코드로 롤백합니다. 이때 되돌린 옛 코드는 이미 바뀐 스키마를 만납니다. 그래서 스키마 변경은 새 코드뿐 아니라 직전 버전 코드와도 함께 돌 수 있어야 합니다.

잘 맞는 경우와 덜 맞는 경우

서버에서 도는 웹 서비스나 API(Application Programming Interface, 애플리케이션 프로그래밍 인터페이스)는 잘 맞습니다. 배포를 팀이 직접 쥐고 있어서 파이프라인 끝의 버튼이 곧 사용자에게 닿는 길이기 때문입니다.

사용자가 직접 설치하는 소프트웨어나 앱 마켓 심사를 거치는 모바일 앱은 버튼 뒤에 팀이 못 쥐는 단계가 더 있습니다. 이런 경우에도 언제든 내보낼 수 있는 상태를 유지하는 원칙은 쓸 수 있습니다. 다만 사용자에게 닿는 속도는 그 바깥 단계가 정합니다.

관련 항목

지속적 전달과 한 줄로 이어지는 실천

지속적 통합 · 지속적 배포 · 트렁크 기반 개발 · 데브옵스 · 릴리스 엔지니어링

지속적 전달을 이루는 구성 요소

배포 파이프라인 · 파이프라인 · 아티팩트 · 빌드 · 자동 테스트 · 단위 테스트 · 인수 테스트 · 스테이징 환경 · 프로덕션 · 코드형 인프라 · 버전관리

지속적 전달 위에서 위험을 줄이는 배포 기법

기능 플래그 · 카나리 배포 · 블루-그린 배포 · 점진적 롤아웃 · 롤백 · 스키마 마이그레이션

지속적 전달의 효과를 재는 지표

배포 빈도 · 변경 리드 타임 · 변경 실패율 · 평균 복구 시간 · DORA 지표

지속적 전달이 거치는 처리 단계

커밋에서 배포까지 · 배포 · 릴리스 · 릴리스 후보 · 배치 크기

다른 이름: Continuous Delivery · CD