CI/CD는 Continuous Integration (지속적 통합)과 Continuous Deployment (지속적 배포)의 줄임말이다. CI/CD는 초기 코드 커밋부터 배포까지 소프트웨어 개발 프로세스를 자동화하는 데 도움을 준다. 이를 통해 코드를 프로덕션 환경에 배포하는 데 전통적으로 필요했던 수많은 수동적인 사람의 개입을 제거함으로써 더 간편한 개발 환경을 제공한다.

CI/CD의 목적은 소프트웨어 팀이 더 나은 품질의 소프트웨어를 더 빠르게 배포할 수 있도록 하는 것이다. 하지만 이것이 실제 환경에서 잘 작동하는지는 상황에 따라 다르다.

Continuous Integration(CI)

Merge Hell

CI가 등장하기 전에는 개발 팀원들이 각자 독립적으로 기능을 개발하다가 나중에 코드를 통합하여 “병합 지옥(merge hell)” 과 같은 어려움을 겪는 경우가 많았다고 한다. 수백 개의 파일과 컨트리뷰터들이 관련된 환경에서는 병합 충돌 및 호환성 문제가 흔히 발생했으며, 이로 인해 계속해서 여러명이 코드를 작업하는 과정에서 지속적으로 문제가 발생했다.

자동화하기

이를 해결하기 위해서는 지속적으로 코드를 모니터링하고, 변동이 발견됐을 때 테스트를 돌리면서 문제가 있을 경우 알림을 받는 식으로 자동화를 시키는 방법이 있다.

정의

CI의 주요 목표는 소스 코드를 공유 저장소에 효율적으로 통합하는 것이다. 변경 사항이 버전 관리 시스템에 커밋되면 자동화된 빌드 및 테스트 케이스가 실행되어 코드의 기능성과 유효성을 검증한다. 이 때 소스 코드가 제대로 컴파일되고 테스트 케이스가 올바르게 실행되는지 확인하는 과정을 거치고, 이 과정에서 문제가 발생하면 팀 전체에 즉시 알림이 전달되어 모두가 상황을 인지할 수 있도록 한다.

도구

CI의 기본적인 도구로는 깃허브같이 소스코드 관리 시스템이 필수적이며, CI 프로세스 자체를 관리하는 도구로는 GitHub Actions, Buildkite, Jenkins, CircleCI, TravisCI 등이 널리 사용된다. 또한, 각 프로그래밍 언어나 생태계에 맞는 다양한 테스트 도구(Jest, Playwright, Cypress, TestNG, JUnit 등)와 빌드 도구(Gradle, Webpack 등)를 함께 활용한다.

CI의 이점

구분세부 설명
”Merge hell” 방지 및 해결 간소화코드를 한꺼번에 병합할 때 발생하는 복잡한 충돌을 피할 수 있다.
충돌이 발생하더라도 비교적 최근 변경 사항에 대한 것이므로 해결하기 쉽다
코드리뷰 효율화통합하는 과정에서 필요한 코드리뷰에 대해서 해야 하는 일이 줄어든다
개발 속도개발자간 효율적인 개발 속도로 각자 개발할 수 있도록 해준다
개발 프로세스 자체를 줄여준다
빌드 안정성빌드가 된 것들만 테스트하면 된다.
프로젝트 백로그 관리프로젝트 백로그의 업무가 줄어든다

CI는 이러한 이점들을 통해 소프트웨어 개발 팀이 더 빠르고 안정적으로 코드를 작성할 수 있도록 해준다.

프론트엔드에서 CI는 어떻게 나눠야 좋을까?

Github actions 기준으로 프론트엔드에서의 CI는 어떻게 나눠야 좋을까? 일단 크게 나눠보면

모든 브랜치에서 린팅과 유닛테스트

린팅의 경우 정적 코드 분석을 통해 다양한 코드 스타일을 하나로 통일할 수 있도록 해주고, 잠재적인 오류에 대해서도 warning을 띄워주어 기존의 부족한 로직을 보완할 수 있다. 그렇기 때문에 웬만해서는 린팅과 유닛테스트와 같이 배포까지 가기 전 단계에서 한번 검증을 돌린 다음에, 규칙에 어긋나는 코드를 미리 거르는 것이 좋을 것 같다.

name: Validate Code
 
on: [push, pull_request]
 
jobs:
  lint-and-unit-test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
 
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm' # npm 의존성 캐싱
 
      - name: Install dependencies
        run: npm ci # package-lock.json 기반으로 설치하여 일관성 유지
 
      - name: Run Lint
        run: npm run lint
 
      - name: Run Unit Tests
        run: npm run test:unit

Continuous Deployment(CD)

CD는 CI/CD 파이프라인에서 CI 이후에 이루어지는 단계이다. CI로 자동화된 테스트가 끝났다면 모든 코드 변경 사항을 자동으로 프로덕션 환경에 배포해주는 것이다.

사실 더 잘 알려진 거로는 Continuous delivery가 있다.

Continuous Delivery

Continuous Delivery는 배포 환경으로의 빠른 배포에 포커스를 두기 때문에 효율적인 배포코드로 전환하기 위해 사용된다. 각 단계는 코드 빌드 후 배포가능한 코드로 변환하는 과정을 거친다. Continuous Delivery(지속적 전달)이라는 이름에 걸맞게 언제든 마치 전달은 되었고, 언제든지 이 전달된 배포버전을 그대로 배포로 올려놓기 직전까지 해주는 것이다.

소프트웨어가 되었다면 다음 논리적 단계는 그걸 프로덕션 환경에 바로 배포하는 것인데, 그 전에 프로덕션 환경에서 제대로 돌아가는지 많은 테스트를 거친다. 주로 QA(Quality Assurance), Performance, Staging 이라고 하는데, 배포 전 환경을 의미한다. 이 단계에서 배포에 준비됐는지 확인하기 위한 여러 테스트를 거치는 것이다.

continuous delivery에서 가장 중요한 관점은 언제라도 배포가 가능할 수 있음을 보장하는 것이다. 그렇기에 한번 delivery 프로세스가 끝나고 나면, 코드는 어떤 필요한 환경에서도 배포가 가능하게끔 되어 있어야 한다. 이를 위해 빌드부터 테스트, 패키징, 배포까지 end-to-end의 프로세스를 포함하는 것이다.

테스트는 CI에서 하는게 아닌가?

CI 단계에서 테스트를 하는건 맞다. 하지만 CI단계와 CD 단계에서의 테스트가 지향하는 목적이 다르다. CI단계에서의 목적은 주로 코드쪽에서 나오는 문제들을 검출하는 단계이다. 새로운 코드가 기존의 코드와 잘 머지되고 코드 자체에 결함이 없는지 빠르게 검증하기 위해 CI라는 단계를 둔 것이다. 반면 CD단계에서 하는 테스트의 경우, 이 서비스가 제대로 동작하는가? 를 검증하기 위한 목적의 테스트이다. 즉, CI단계와 다르게 코드 단위로 보기보다는 하나의 서비스 단위로 각 서비스의 기능들에 대해서 다시금 전체적인 테스트를 통해 서비스가 원활하게 돌아가는지 검증하는 단계인 것이다.

이 단계에서는 주로 테스트하는 것은

  • 통합 테스트 (Integration Tests): 여러 개의 유닛(모듈, 컴포넌트)을 결합했을 때 서로 상호작용이 잘 되는지 검증
  • E2E 테스트 (End-to-End Tests, E2E): 실제 사용자 시나리오를 처음부터 끝까지 시뮬레이션
  • 스모크 테스트 (Smoke Tests): 프로덕션 환경에 배포된 직후, 서버가 정상적으로 켜졌는지, 가장 핵심적인 기능(로그인, 메인 페이지 로딩 등)이 동작하는지 가볍게 확인하는 테스트이다. “불이 나서 연기가 나는 곳은 없는지” 빠르게 훑어보는 개념이다 이 있다.

이를 workflow 파일로 보면 다음과 같이 구성할 수 있다

# .github/workflows/deploy-dev.yml
name: Deploy to Development
 
on:
  push:
    branches:
      - development # development 브랜치에 push될 때만 실행
 
jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: dev # GitHub Environment 'dev'를 사용
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
 
      # ... (Node.js 설치, 의존성 설치, 빌드 과정) ...
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      - name: Install dependencies
        run: npm ci
      - name: Build
        run: npm run build
 
      - name: Deploy to Dev Server
        run: |
          echo "🚀 Deploying latest changes to the dev server..."
          # 개발 서버 배포 스크립트 실행 (예: AWS S3, Vercel 등)
          # ./scripts/deploy-dev.sh

주로 여기에서 dev용 서버를 하나 두고, 이 서버에서 실제로 qa를 하면서 서비스를 직접 열어서 스모크테스트도 해보고 테스트도 더 큰 단위로 돌려본다고 생각하면 된다.

배포 자동화

Continuous Deployment의 경우에는 모든 빌드, 테스트등을 거친 뒤 마지막 단계에서 인간의 개입 없이 배포까지 혼자 돌아갈 수 있도록 자동화해놓은 것을 의미한다.

Contunuous Deployment vs. Continuous Delivery

언뜻 보면 둘이 조금 헷갈릴 수 있다. 하지만 이 두 개의 구분을 명확히 알면 보다 이해를 잘 할 수 있다.

구분Continuous DeliveryContinuous Deployment
정의코드 변경들이 자동으로 병합되고 빌드, 테스트, 프로덕션 환경으로 배포 전 준비의 모든 단계자동화된 테스트를 통과한 후 인간의 개입 없이 프로덕션 환경에 배포
목표코드를 언제든 프로덕션에 배포할 수 있게끔 보장하지만 배포까지 이루어지진 않음모든 배포 프로세스 자체를 자동화해서 시간을 절약하고 프로덕션 환겨에서 바로 사용
프로덕션 환경 배포코드는 스테이징 환경에서 승인을 대기한 채로 기다리기 떄문에 인간이 직접 해야 하는 부분이 있음변동사항이 인간의 개입 없이 바로 프로덕션 환경으로 배포
자동화 및 테스트배포를 위한 코드를 준비하기 위해 자동화와 테스트에 중점을 둠배포 환경에 좋지 않은 영향을 끼치지 않을 정도의 고수준 자동화, 테스트, 배포 모니터링 보장을 필요로 함

Continuous deployment의 장점

  • 더 빠른 배포
    • 더 빠른 프로덕트 개발 사이클이 될 수 있음
  • 덜 위험한 배포와 쉬운 문제 해결
    • 배포 과정에서 직접 하다가 오류가 날 확률을 줄여줌
    • 에러가 날 가능성이 없게끔 매끄럽게 배포 프로세스 구축
    • 문제가 생겨도 더 빨리 알아채고 해결할 수 있음
  • 지속적인 품질 개선
    • 프로덕트 품질 측면에서 계속해서 개선될 수 있음
    • 자주 점진적인 개선을 이룰 수 있음.

배포 전략

실제 사용자가 사용하고 있는 서비스에 대해서 배포를 할 때, 배포 과정에서 업데이트가 불가능하다면 그거 나름대로 사용자 경험을 저해할 수 있는 요인이 된다. 그렇기에 이를 해결하기 위한 여러 배포 전략들이 있다.

Big Band Deployment

빅뱅 배포 전략은 프로덕션 환경에 변경 사항을 배포 하는 초기 방법 중 하나이다. 해당 배포 전략은 모든 변경사항을 한 번에 푸시해버리는데, 이 때 애플리케이션의 전체 혹은 대부분이 업데이트 될 정도의 스케일이다.

기존의 시스템을 완전히 바꿔버리는 큰 업데이트가 있기 때문에 기존의 시스템을 셧다운하고 새로운 버전으로 바꿔버리면 다운타임이 나올 수밖에 없다. 게다가 이 중간에 갑자기 문제가 생기거나 해서 다시 돌아가야 한다면, 문제는 더 커질 수 있으며 사용자의 불편 또한 더 커질 수 있다. 그렇기에 릴리즈 전 광범위한 개발과 테스트가 필요하다. 또한 롤백이 매우 어려우므로 실패를 최소로 가정해야 한다.

Rolling Deployment

롤링 배포의 경우에는 시간이 지나면서 시스템의 일부분을 점진적으로 업데이트 하는 방식이다.

만약 애플리케이션 서버 10대가 있을 때, 롤링 배포는 첫 번째 서버 중단 -> 배포 -> 다시 작동 -> 정상적으로 작동하면 다음 서버로 가서 반복 의 과정을 반복하면서 점진적으로 새로운 버전으로 대체하는 것이다.

이러한 방식의 장점으로는 점진적으로 서버를 업데이트 시키기 때문에, 다른 서버는 계속 가동되고 있는 상태이고 이에 사용자에게 서비스를 제공할 수 있다.또한 롤아웃 초기 단계에서 문제를 발견하고 해결할 수 있어 광범위한 문제로 가기 전에 예방할 수있다.

하지만 단점으로는

  • 점진적인 만큼 느림
  • 시스템 전체 문제의 위험을 완전히 제거하지 못함
  • 타겟 배포를 지원하지 않아 랜덤으로 새 버전을 제공함 등이 있다.

이러한 롤링 배포의 경우 제어되고 체계적인 방식으로 위험과 사용자 영향의 균형을 맞춘다는 점에서 인기가 있다.

Blue Green Deployment

블루그린 전략은 ‘블루’와 ‘그린’이라는 두 개의 동일한 프로덕션 환경을 유지하는 방식이다.

이 방식에서는 어느 시점이든 한 환경만 활성화시키고 해당 버전의 애플리케이션을 제공한다. 다른 한 환경의 경우에는 애플리케이션을 안전하게 배포하고 테스트할 수 있는 idle 상태의 환경으로 사용된다.

만약 새로운 버전이 나왔을 경우, 지금 배포되고 있는 서버가 블루라고 한다면 현재 idle 상태인 버전을 그린 환경에 배포하고 테스트한다. 이 상태에서도 서버는 여전히 블루에서 제공하고 있다. 만약에 그린환경에서 qa가 모두 끝났다면, 로드 밸런서를 통해서 블루가 아닌 그린을 가리키도록 하면서 바로 트래픽이 향하는 방향을 바꿀 수 있다.

전환이 완료된 이후에는 기존의 서비스하던 블루가 idle 상태가 된다. 만약 새로운 버전에서 문제가 발생하면 빠르게 블루 환경으로 전환해서 이전 버전으로 효과적으로 롤백할 수도 있다.

이러한 블루그린 배포의 장점은

  • 원활한 전환과 롤백
  • 높은 수준의 제어
  • 위험 초소화
  • 원활한 사용자 경험과 안정적인 롤백 이 있다.

하지만 단점으로는

  • 타겟 배포(특정 사용자에게 새로운 버전을 대상으로 배포)가 안되고 모든 사람들에게 동시에 발생
  • 동일한 프로덕션 환경 유지 때문에 리소스 집약적이고, 인프라나 리소스 요구 사항이 무조건 두 배가 되어야 한다.
  • 두 개의 병렬 프로덕션 환경을 관리하고 데이터 동기화를 원활하게 보장한는 것은 복잡성이 더 클 수 있으며, 정교한 조정이 필요하다.

등이 있다.

Canary Deployment

카나리 배포같은 경우, 과거 탄광에서 위험한 가스를 감지하기 위해 탄광에서 카나리아를 사용하던 오래된 관행에서 이름을 따온 전략이다. 이러한 카나리아의 의미를 가져와 전체 배포 전에의 ‘공기’를 테스트하는 용으로 카나리아 서버를 만드는 느낌이라고 보면 된다.

새로운 버전의 경우 모든 서버나 사용자에게 배포하는 대신, 작은 하위 집합인 ‘카나리아’ 서버에만 미리 배포한다. 해당 하위 집합은 서버의 일정 비율이 될 수도 있고, 특정 기준에 따라 선택된 사용자 그룹이 될 수 있다. 이렇게 카나리아 서버를 시험삼아 돌려보면서 일부 사용자들에게 사용하도록 하고, 이 과정에서 배포된 버전이 잘 작동하면 나머지 서버를 점진적으로 확대할 수 있다. 하지만 만약 여기에서 문제가 생겼다면, 배포를 중단하고 문제를 고친 다음에 다시 카나리아 서버를 동작시키는 과정을 반복한다. 이런식으로 점진적으로 접근하다보면 안정성과 제어권을 가질 수 있다.

또한 이런 카나리아 배포방식의 경우, 특정한 대사 타켓 배포가 가능하기 떄문에, 지리적 위치나 장치 유형과 같은사용자별 기준에 따라 카나리아를 지정할 수 있다.

하지만 이런 카나리아 배포의 경우, 몇 가지 과제가 있다.

  • 세심한 모니터링과 자동화된 테스트가 필요
  • 필요에 따라 배포 속도를 높이거나 중단하기 위해 다소 복잡한 인프라 도구의 필요성
  • 데이터베이스 스키마 변경이나 API 호환성 문제와 관련하여 구현 및 관리가 복잡함
  • 독립적으로 사용하는 전략이라기보단 롤링 배포와 결합되어 두 전략의 장점을 모두 활용

Feature toggle

Feature Toggle은 이전의 것들과는 약간 다르다. 다른 배포 전략들은 주로 새로운 버전의 배포에 집중해서 얘기를 했다면, Feature toggle의 경우 애플리케이션 내 특정 새로운 기능들을 관리하는데 중점을 둔다.

toggle 이라는 이름 답게, Feature Toggle은 새로운 기능에 대해 코드레벨에서 토글(or 스위치)를 도일한다. 이 토글을 특정 사용자나 상황에 따라 해당 기능을 켜거나 끌 수 있게 함으로써 세밀하게 기능을 제어 할 수 있도록 한다. 그렇기 때문에 이 전략은 다른 어떤 배포 전략과도 함께 사용될 수 있다.

feature toggle의 장점에는

  • 새로운 기능에 대한 제어
  • 특정 사용자를 타겟으로 잡고 테스트가 가능
  • A/B 테스트나 기능 성능 테스트를 위해 점진적 기능 출시에 좋음 가 있다.

하지만 단점으로는

  • 제대로 관리되지 않으면 토글떄문에 코드의 복잡성이 커지며 유지보수가 어려워질 수 있음
  • 토글 부채(toggle debt)를 방지하려면 토글을 정리해야 하는 과정이 필요한데 이 과정에서 비용이 발생 등이 있다.

전형적인 CI/CD 파이프라인

전형적인 CI/CD의 경우, AWS를 포함한 다른 커스텀 툴들을 사용하여 나올 수 있는 파이프라인은 아래와 같다.

소스 코드 관리(Source)

  • 애플리케이션의 모든 패키지와 종속성을 저장하는 곳
  • 프로젝트 변경 사항에 대해 특정 수준의 검토를 의무화하는 메커니즘
    • 코드를 무작위로 병합 X, pull request를 통해 리뷰어들에게 승인을 받고 병합
    • 최소 한 명의 리뷰어가 있으면 좋으며, 두 명 이상의 검토자가 있으면 더 안전함

빌드 프로세스(Build)

  • 소스 코드의 변동 사항이 커밋되었을 때 일어나는 빌드 프로세스
  • 각 언어별 구성 파일(package.json, pom 등)에 지정된 모든 종속성을 포함하여 코드가 컴파일됨
  • 프로젝트와 관련 있는 모든 단위 테스트 실행
    • 단위 테스트 커버리지는 신뢰성있는 CI/CD 파이프라인에 매우 중요
    • 커버리지를 높게 유지하기 위해 점진적으로 개선시켜 나가는 것은 좋지만 무조건 100% 커버리지는 비현실적이고, 오히려 도움이 안 될 수도 있음

테스트 환경(Test environment)

  • 통합 테스트를 실행하는 환경
    • API의 동작을 검증하여 올바르게 작동하고 예상되는 비즈니스 규칙을 준수하는지 확인
    • API 상호작용 및 비즈니스 로직 요구 사항 충족과 같은 시나리오 포함
  • 일정 단계를 활성화하거나 비활성화할 수 있게 하여 변경 사항을 배포 파이프라인의 다음 단계로 진행하는 것을 제어
  • 애플리케이션이 정의된 비즈니스 규칙을 준수하는지도 검증
  • 종속성을 고려하여 테스트(end-to-end 테스트)

1-box 환경

  • 전체 프로덕션 환경에 결함 있는 변경사항을 배포할 위험을 완화하기 위해 프로덕션 트래픽의 작은 부분으로 먼저 변경 사항을 테스트
  • 10개 호스트가 있다면 한개 호스트만에 대해서 10% 이하의 트래픽을 처리하게 함으로써 문제가 있을 경우 그 하나의 호스트에만 영향이 가도록 하는 것
  • 롤백이 쉽고 빨라 효율적

Rollback alarms

  • 배포 중에 오류율, 응답 시간 지연 또는 중요한 지표의 편차와 같은 문제가 발생하는 것을 모니터링 할 수 있는 수단
  • 오류율, 지연 시간 및 주요 비즈니스 지표와 같은 항목을 지속적으로 모니터링하고, 지표가 설정된 한도를 초과할 때 안정 버전으로의 롤백을 트리거해서 신속하게 버전을 되돌림

Bake Period

  • 배포 중 롤백 알람이 트리거되지 않으면 bake period 단계가 됨
  • 전체 배포 전에 변경 사항이 프로덕션 환경에서 안정화될 시간을 허용
  • 즉시 나타나진 않지만 지연된 문제나 비정상적인 동작을 포착하기 위한 시간
  • 이 기간이 지나면 더 큰 안정성을 가진 버전을 배포할 수 있음

배포 환경

  • 위 모든 과정이 지나면 전체 프로덕션 환경으로 나갈 준비가 됨
  • 여기서도 롤백 알람과 같이 안정장치가 있어 이후에도 안정적으로 롤백할 수 있는 수단이 있어 프로덕션으로의 배포를 원활하게 할 수 있음