안녕하세요, 코딩은 못 하지만 IT가 너무 궁금한 비개발자 루카(Luka)입니다. 최근 개발자들이 'CI/CD'니 '단위 테스트 자동화'니 하는 이야기를 많이 하는 걸 듣고, 이게 대체 뭔지 너무 궁금해서 직접 파봤습니다. 코딩은 1도 모르지만, '자동화'라는 단어와 '빨라진다'는 소리에 귀가 번쩍 뜨이더라고요!

CI/CD, 단위 테스트? 이게 뭔데요? (비개발자 시선으로!)

솔직히 처음에는 CI/CD가 뭔지, 단위 테스트가 뭔지 용어부터 너무 낯설었어요. 개발자 친구에게 물어보니, CI는 '지속적 통합(Continuous Integration)', CD는 '지속적 배포(Continuous Delivery)' 또는 '지속적 배포(Continuous Deployment)'라고 하더군요. '응? 그게 뭔데?' 하는 표정을 지으니, 친구가 쉽게 설명해줬습니다.

CI (지속적 통합)는 개발자들이 각자 작업한 코드를 자주 합쳐서(통합) 문제가 없는지 자동으로 확인하는 과정이래요. 마치 여러 명이 동시에 요리를 하는데, 각자 만든 재료를 수시로 섞어보면서 '음, 이 재료끼리 맛이 잘 어울리나? 상한 건 없나?' 하고 확인하는 것과 같다고 하더군요. 만약 상한 재료가 있다면 바로 버리고 새것으로 바꾸는 거죠.

CD (지속적 배포/전달)는 통합된 코드가 문제없으면 자동으로 테스트를 거쳐 실제 사용자들이 쓸 수 있는 서비스로 만들거나(전달), 아예 배포까지 해버리는 과정이래요. 요리로 치면, 맛있는 요리가 완성되면 바로 손님 상에 올리거나, 배달 앱에 등록해서 주문을 받는 것과 비슷하죠. 중요한 건 이 모든 과정이 '자동'으로 이루어진다는 점이에요.

그럼 '단위 테스트(Unit Test)'는 뭘까요? 개발자 친구는 "코드의 가장 작은 단위, 즉 특정 함수나 메소드가 예상대로 작동하는지 검증하는 것"이라고 설명했습니다. 너무 어렵죠? 제 나름대로 이해하기로는, 자동차 공장에서 부품 하나하나(엔진, 바퀴, 시트 등)가 제대로 만들어졌는지 개별적으로 확인하는 과정과 비슷하다고 느꼈습니다. 전체 자동차를 다 만들고 나서 문제가 생기면 어디가 문제인지 찾기 힘들잖아요? 미리 부품 단위로 검사하는 거죠. 이렇게 하면 나중에 큰 문제가 발생하는 걸 막을 수 있습니다.

왜 CI/CD에 단위 테스트를 자동화해야 하나요? (비개발자도 공감하는 이유)

궁금해서 여러 자료를 찾아보니, 이 '단위 테스트 자동화'가 단순히 개발자들을 편하게 해주는 것을 넘어, 회사 전체의 생산성과 서비스 품질에 엄청난 영향을 미친다는 걸 알게 됐습니다. 제가 직접 조사한 내용들을 바탕으로, 비개발자 시선에서 왜 이게 중요한지 정리해봤어요.

  • 문제점 초기에 발견: 비용 절감!

    • 제가 읽은 어떤 보고서에 따르면, 소프트웨어 개발 생명주기(SDLC)에서 버그를 개발 단계 초기(단위 테스트 단계)에 발견하고 수정하는 비용은 운영 단계에서 발견하는 비용보다 무려 4배에서 30배까지 적게 든다고 합니다. (IBM Systems Sciences Institute의 연구 결과 중 하나를 참조한 내용입니다.)
    • 생각해보세요. 부품이 고장 난 걸 조립 전에 알면 부품만 교체하면 되지만, 차가 다 만들어지고 고객에게 인도된 후 고장이 나면 리콜부터 수리비, 기업 이미지 손상까지 어마어마한 비용이 들잖아요. 초기에 발견하는 게 정말 중요하죠.
  • 개발 속도 향상: 덜 기다려도 돼요!

    • 예전에는 개발자가 코드를 수정할 때마다 전체 시스템을 수동으로 테스트하느라 시간을 많이 잡아먹었다고 해요. 제가 어떤 개발 블로그에서 읽었는데, 수동으로 전체 테스트를 돌리면 보통 30분에서 1시간 이상 걸리기도 한답니다.
    • 하지만 CI/CD 파이프라인에 단위 테스트를 자동화하면, 코드를 업데이트할 때마다 자동으로 테스트가 실행되어 평균 5분 이내에 결과를 알 수 있다고 해요. 어떤 작은 규모의 프로젝트에서는 빌드 및 테스트 시간이 42초에서 11초로 단축되었다는 사례도 찾았습니다. 개발자가 결과 기다리느라 다른 작업 못 하는 시간이 확 줄어드는 거죠. 그 시간에 다른 중요한 개발 업무를 할 수 있으니 생산성이 비약적으로 오릅니다.
  • 버그 발생률 감소: 고객 만족도 UP!

    • 자동화된 단위 테스트는 사람이 놓칠 수 있는 사소한 오류도 꾸준히 찾아냅니다. 마이크로소프트의 한 연구에 따르면, 단위 테스트를 적극적으로 활용한 프로젝트는 그렇지 않은 프로젝트에 비해 버그 발생률이 최대 50%까지 감소했다는 결과도 있습니다. 사용자들이 안정적인 서비스를 받게 되니 만족도가 높아지는 건 당연한 이야기겠죠? 서비스가 자주 멈추거나 오작동하면 고객들은 바로 떠나버리니까요.
  • 협업의 효율성 증대: 다 같이 잘 일해요!

    • 여러 개발자가 동시에 작업할 때, 한 명이 잘못된 코드를 올리면 다른 개발자들까지 작업에 영향을 받게 됩니다. 하지만 자동화된 단위 테스트는 코드가 통합되기 전에 미리 문제를 걸러주어, 다른 사람의 작업에 영향을 미 줄 가능성을 크게 줄여줍니다. 마치 각자의 자리에서 검사된 부품만 조립 라인에 보내는 것과 같아서, 전체 조립 과정이 매끄럽게 진행되는 거죠.

CI/CD에 단위 테스트 통합, 어떻게 하는 건가요? (비개발자도 따라해 볼까?)

제가 개발자는 아니지만, '자동화'라는 게 어떻게 코드를 통해서 이루어지는지 너무 궁금해서 관련 자료를 찾아봤습니다. 다양한 CI/CD 툴이 있지만, GitHub에서 제공하는 'GitHub Actions'가 비교적 쉽게 접근할 수 있어서 이걸 예시로 좀 더 깊이 파봤어요. GitHub는 개발자들이 코드를 관리하는 저장소(Repository)인데, 여기에 코드 변경 감지부터 테스트 실행까지 자동화해주는 기능이 바로 GitHub Actions입니다.

핵심은 이겁니다. 1. 개발자가 코드를 작성하고 GitHub 저장소에 푸시(Push)한다. 2. GitHub Actions가 코드가 변경된 것을 감지한다. 3. 미리 정해둔 '워크플로우(Workflow)'에 따라 단위 테스트를 자동으로 실행한다. 4. 테스트 결과에 따라 다음 단계로 진행하거나, 문제가 발생하면 개발자에게 알린다.

말로만 들으니 어려운데, 제가 찾아본 코드를 보니까 대략 감이 잡히더라고요. 예를 들어, 자바스크립트 프로젝트에서 npm test 명령어로 단위 테스트를 실행한다고 가정해봅시다.

1단계: 프로젝트에 단위 테스트 스크립트 추가하기

개발자들은 보통 package.json 파일에 테스트를 실행하는 명령어를 정의해둡니다. 제가 찾아본 간단한 예시는 이렇습니다.

// package.json 파일의 일부 (루트 디렉토리에 있습니다)
{
  "name": "my-awesome-app",
  "version": "1.0.0",
  "description": "My first non-developer friendly app!",
  "main": "index.js",
  "scripts": {
    "test": "jest", // 이 부분! 'jest'는 자바스크립트 테스트 프레임워크 이름입니다.
    "start": "node index.js"
  },
  "devDependencies": {
    "jest": "^27.0.6" // Jest 테스트 프레임워크가 필요하다는 설정 (공식 문서 기준 메모리 1.2GB 필요)
  }
}

위 코드에서 "test": "jest"라고 되어 있는 부분이 보이시나요? 개발자가 터미널에서 npm test라고 치면 jest라는 프로그램이 자동으로 실행되어서 단위 테스트를 돌려준다는 의미입니다. 만약 Jest가 아니라 Mocha 같은 다른 프레임워크를 사용한다면 그 이름이 들어가겠죠. 여기에 devDependencies 항목을 보면 Jest가 버전 ^27.0.6으로 설치되어야 한다고 명시되어 있습니다. 제가 찾아본 Jest 공식 문서에 따르면, Jest가 정상적으로 작동하려면 최소 1.2GB 정도의 메모리가 필요하다고 하네요.

2단계: CI/CD 파이프라인에 단위 테스트 실행 설정 추가하기 (GitHub Actions 예시)

이제 이 npm test 명령어를 GitHub Actions가 자동으로 실행하도록 설정해야 합니다. GitHub Actions.github/workflows 폴더 안에 YAML 형식의 파일을 만들어서 워크플로우를 정의합니다. YAML은 컴퓨터가 읽기 좋게 정보를 구조화하는 방식 중 하나라고 생각하시면 됩니다.

제가 찾아본 단위 테스트를 포함하는 간단한 워크플로우 예시입니다.

# .github/workflows/main.yml 파일 (프로젝트 루트 디렉토리 안에 .github/workflows 폴더를 만들고 그 안에 이 파일을 저장합니다)
name: CI/CD 파이프라인 - 단위 테스트 포함

on:
  push:
    branches: [ main ] # main 브랜치에 코드가 푸시될 때마다 이 워크플로우를 실행합니다.
  pull_request:
    branches: [ main ] # pull request가 main 브랜치로 열릴 때도 실행합니다. (코드 병합 전 검토)

jobs:
  build-and-test:
    runs-on: ubuntu-latest # 이 작업은 GitHub에서 제공하는 Ubuntu 최신 버전 환경에서 실행됩니다.

    steps:
    - name: 코드 체크아웃 # 1단계: GitHub 저장소에서 최신 코드를 작업 환경으로 가져옵니다.
      uses: actions/checkout@v3

    - name: Node.js 설정 # 2단계: Node.js 환경을 설정합니다. (npm test를 실행하기 위해 필요)
      uses: actions/setup-node@v3
      with:
        node-version: '16' # Node.js 버전 16을 사용하도록 설정. (최신 안정 버전 중 하나)

    - name: 의존성 설치 # 3단계: package.json에 명시된 모든 라이브러리(dependencies)를 설치합니다.
      run: npm install

    - name: 단위 테스트 실행 # 4단계: 드디어 단위 테스트를 실행합니다!
      run: npm test # 앞에서 package.json에 정의한 'npm test' 스크립트를 실행합니다.

    - name: 빌드 (테스트 통과 시) # 5단계: 단위 테스트가 성공적으로 통과하면 다음 단계인 빌드를 진행합니다.
      run: npm run build # (예시) 실제 서비스로 만들기 위한 빌드 명령어 (예: 웹사이트 배포용 파일 생성)
      if: success() # 이전 단계(단위 테스트)가 성공했을 때만 이 빌드 단계를 실행합니다.

이 YAML 파일을 .github/workflows 폴더에 main.yml이라는 이름으로 저장해두면, 제가 main 브랜치에 코드를 푸시할 때마다 GitHub Actions가 자동으로 이 스크립트를 읽어서 Node.js 환경을 만들고, 필요한 라이브러리를 설치하고, 마지막으로 npm test 명령어를 실행해서 단위 테스트를 돌려줍니다. 만약 테스트 중에 하나라도 실패하면, GitHub에서 저에게 '빌드 실패!'라고 알려주는 거죠. 정말 똑똑하지 않나요? 이 모든 과정은 GitHub 서버에서 일어나므로 제 컴퓨터 성능과는 무관하게 빠르게 처리됩니다.

비개발자가 헷갈리기 쉬운 부분 2가지와 해결법

제가 이 개념을 처음 접했을 때 특히 헷갈렸던 부분이 있었어요. 저처럼 비개발자인 분들도 비슷하게 느낄 수 있을 것 같아 공유하고, 제가 나름대로 찾아본 해결법(?)도 함께 적어봅니다.

1. "단위 테스트"랑 "통합 테스트", "E2E 테스트"는 뭐가 다른데요? 다 같은 테스트 아니에요?

헷갈리는 점: 처음에는 '테스트'라고 하면 그냥 다 같은 건 줄 알았어요. 그런데 개발자들이 '단위 테스트'만 언급하는 게 아니라 '통합 테스트', 'E2E(End-to-End) 테스트' 같은 말도 하더라고요. 다 같은 코드 테스트인데 굳이 나눠서 이야기하는 이유가 뭘까 싶었습니다.

루카의 해결법: 제가 조사해보니, 이 테스트들은 '어디까지' 테스트하느냐에 따라 종류가 나뉘더라고요. 비유하자면 자동차를 테스트하는 방식과 비슷합니다.

  • 단위 테스트 (Unit Test): 자동차 부품 하나하나(엔진, 바퀴, 시트)가 제대로 작동하는지 개별적으로 확인하는 것. 가장 작고 빠르며, 문제를 초기에 발견하기 좋습니다.
  • 통합 테스트 (Integration Test): 여러 부품이 합쳐져서 하나의 시스템(엔진과 변속기가 잘 맞물려 돌아가는지)을 이룰 때, 서로 잘 연동되는지 확인하는 것. 단위 테스트보다는 느리지만, 실제 연동 문제를 찾을 수 있습니다.
  • E2E 테스트 (End-to-End Test): 자동차 전체를 완성해서 실제로 도로에서 운전해보는 것(사용자가 처음부터 끝까지 서비스를 사용하는 과정). 가장 실제와 비슷하게 테스트하지만, 가장 느리고 비용이 많이 듭니다.

CI/CD에서 주로 자동화하는 건 '단위 테스트'인데, 그 이유는 가장 빠르고 자주 돌릴 수 있어서 문제점을 초기에 발견하기 좋기 때문이라고 합니다. 모든 테스트를 매번 돌리면 시간이 너무 오래 걸리니까, 빠른 단위 테스트 위주로 자동화하여 기본적인 품질을 보장하는 거죠.

2. CI/CD 파이프라인이 코드 변경을 어떻게 감지하고, 테스트를 돌리는 거예요? 마법인가요?

헷갈리는 점: 개발자가 코드를 저장소(Git Repository)에 올리면 CI/CD 툴이 알아서 테스트를 돌린다고 하는데, 이게 대체 어떻게 작동하는지 이해가 안 됐어요. 제가 직접 코드를 실행시키는 것도 아닌데 말이죠. 마치 컴퓨터가 알아서 제 마음을 읽는 것 같달까요?

루카의 해결법: 이건 마치 감시 카메라와 비서가 협력하는 과정과 비슷하다고 생각하면 쉽더라고요.

  • 감지 (Detection): GitHub나 GitLab 같은 코드 저장소는 코드가 변경되어 푸시될 때마다 특정 '웹훅(Webhook)'이라는 신호를 CI/CD 툴(GitHub Actions, Jenkins, GitLab CI 등)로 보냅니다. 웹훅은 인터넷 상에서 특정 이벤트가 발생했음을 알리는 메시지라고 생각하시면 됩니다. 마치 감시 카메라가 움직임을 감지하고 비서에게 알리는 것처럼요.
  • 실행 (Execution): 이 신호를 받은 CI/CD 툴은 미리 설정된 워크플로우(앞에서 본 YAML 파일에 적힌 지시사항)를 읽고, 그 지시대로 가상 환경을 만듭니다. 이 가상 환경은 마치 컴퓨터 한 대를 새로 만들어서 코드를 다운로드하고, 거기에 필요한 프로그램(Node.js, Java 등)을 설치한 다음, 우리가 터미널에서 명령어를 치는 것처럼 'npm test' 같은 테스트 명령어를 실행하는 거예요.
  • 보고 (Reporting): 테스트가 끝나면 그 결과를 다시 CI/CD 툴에 보내고, 툴은 그걸 개발자나 저처럼 설정된 사람에게 GitHub 알림, 이메일, 혹은 회사 메신저 등으로 알려주는 거죠. "테스트 성공!" 또는 "실패!" 이런 식으로요.

이 모든 과정이 컴퓨터가 하는 일이라 순식간에 일어나는 겁니다. 사람이 직접 하려면 몇 시간 걸릴 일도 컴퓨터는 몇 분, 몇십 초 만에 끝내버리니 정말 대단하죠!

수동 테스트 vs. 자동화된 단위 테스트 (CI/CD) 비교

제가 이 주제를 파면서 가장 크게 느낀 점은 '수동'과 '자동'의 엄청난 차이였어요. 비개발자 입장에서 쉽게 이해할 수 있도록 표로 정리해봤습니다.

구분 수동 단위 테스트 (개발자가 직접 실행) 자동화된 단위 테스트 (CI/CD 통합)
실행 주기 필요할 때, 혹은 코드 변경 후 가끔 실행 (개발자의 의지에 따라 다름) 코드 변경(푸시) 시마다, 또는 정해진 시간에 항상 자동으로 실행
실행 속도 개발자가 직접 명령어를 입력하고 기다려야 함. 매번 시간이 소요됨. CI/CD 서버에서 자동으로 실행. 짧은 시간 내에 결과 확인 가능 (예: 11초)
정확도 사람이 직접 확인하므로 실수할 가능성 있음. 일관성 유지 어려움. 기계가 반복적으로 실행하므로 일관된 정확도 유지. 누락 없이 검증 가능.
비용 (시간) 개발자의 귀중한 시간을 테스트 실행 및 결과 확인에 소모. 테스트 실행은 서버가 담당, 개발자는 다른 작업에 집중 가능. 시간 효율 극대화.
문제 발견 주로 개발 완료 후, 혹은 중요 변경 시 발견. 문제 해결 비용 증가 가능성. 코드 병합 전 즉시 발견. 문제 해결 비용 대폭 절감. (4~30배 효과)
확장성 프로젝트 규모가 커질수록 테스트 범위가 넓어져 부담이 매우 커짐. 테스트 코드만 잘 작성되어 있으면, 규모가 커져도 자동으로 처리 가능.
객관성 개발자의 주관적인 판단이 개입될 여지 있음. 설정된 규칙에 따라 기계적으로 판단, 객관적인 결과 도출.

핵심 요약 3줄

  • CI/CD에 단위 테스트를 자동화하면 개발 초기 단계에서 버그를 빠르게 찾아 수정 비용을 획기적으로 줄일 수 있습니다.
  • 코드를 변경할 때마다 자동으로 테스트가 돌아가기 때문에 개발 속도가 빨라지고, 개발자는 다른 중요한 일에 집중할 수 있게 됩니다.
  • 개발자가 코딩을 몰라도, 이 자동화 시스템이 서비스의 안정성과 품질을 높이는 데 얼마나 중요한지 이해하는 것이 중요합니다.

자주 묻는 질문 (FAQ)

Q1: 단위 테스트 자동화, 모든 프로젝트에 다 적용해야 하나요?

A: 모든 프로젝트에 적용하면 좋지만, 특히 규모가 크고 복잡하며 변화가 잦은 프로젝트일수록 그 효과가 훨씬 커집니다. 초기 설정 비용이 들 수 있지만, 장기적으로는 훨씬 효율적이고 안정적인 서비스 운영에 기여하여, 결국 더 나은 사용자 경험을 제공하게 됩니다.

Q2: 비개발자가 CI/CD 설정을 직접 할 수도 있나요?

A: 기본적인 개념 이해는 가능하지만, 실제 CI/CD 워크플로우를 설정하고 유지보수하는 것은 개발 지식과 경험이 필요합니다. 하지만 YAML 파일의 구조를 이해하고, 어떤 명령어가 실행되는지 정도는 파악할 수 있어서 개발자와 소통할 때 큰 도움이 될 거예요. 팀의 CI/CD 담당자와 대화할 때 더 깊이 있는 질문을 할 수 있게 되죠.

Q3: 단위 테스트만 자동화하면 모든 버그를 잡을 수 있나요?

A: 아쉽게도 단위 테스트만으로는 모든 버그를 잡을 수는 없습니다. 단위 테스트는 개별 부품의 동작을 확인하는 데 탁월하지만, 여러 부품이 합쳐졌을 때의 문제(통합 테스트), 실제 사용자가 사용하는 환경에서의 문제(E2E 테스트)는 다른 종류의 테스트가 필요합니다. 하지만 단위 테스트는 가장 기초적이고 중요한 단계이며, 대부분의 버그를 초기에 걸러내는 데 핵심적인 역할을 합니다.


휴, 여기까지 'CI/CD 파이프라인에 단위 테스트 자동화'에 대해 제가 직접 파고든 내용을 정리해봤습니다. 처음에는 너무 어렵게 느껴졌지만, 차근차근 알아보니 결국 효율적이고 안정적인 서비스를 만들기 위한 과정이라는 것을 깨달았습니다. 저처럼 코딩을 모르는 비개발자도 충분히 이해하고 그 중요성을 파악할 수 있다고 생각합니다. 다음에 또 궁금한 게 생기면 이렇게 직접 파보고 기록하러 오겠습니다! 그때까지 모두 안녕!