
CI를 처음 실행했을 때 발견한 문제: 4개 레포지토리에서 실제 버그 사례
요약
CI(지속적 통합)를 처음 도입했을 때 로컬 환경에서는 발견되지 않았던 환경 설정 및 의존성 문제를 발견한 사례를 다룹니다. CI의 '깨끗한 상태(freshness)'가 가진 가치와 이를 통해 드러난 실제 버그 유형들을 설명합니다.
핵심 포인트
- CI는 로컬의 캐시나 설정 없이 원본 상태에서 시작하여 숨겨진 문제를 드러냄
- .env.example 파일의 잘못된 형식이 CI 부팅 실패를 유발할 수 있음
- 의존성 감사(npm audit)를 통해 로컬에서 간과한 보안 취약점을 포착 가능
- CI는 로직 버그가 아닌 환경 및 설정 오류를 잡아내는 데 매우 효과적임
저는 CI(Continuous Integration)를 추가하지 않았던 네 개의 작은 애플리케이션에 적용했습니다. 첫 번째 실행은 단 하나의 테스트도 실행되기도 전에 빨간색으로 실패했는데, 그것이 제 코드가 망가졌기 때문은 아니었습니다. 제가 실제로 테스트해 본 적 없는 파일 때문에 발생한 문제였습니다.
첫 CI 실행에서 발견할 수 있는, 여러분의 로컬 환경이 조용히 숨기고 있던 것들이 있습니다.
파이프라인 자체는 특별하지 않았다
네 개의 레포지토리 모두 동일한 형태였습니다. 새롭게 코드를 체크아웃하고, .env.example을 .env로 복사하며, 의존성을 설치하고, 검사를 실행하고, 의존성 감사를 수행하는 식입니다. 10분 안에 템플릿에서 복사해 붙여넣기 할 수 있는 종류의 작업이었습니다.
가치가 높았던 것은 정교함 자체가 아니었습니다. 바로 **새로움(fresh)**이라는 단어였습니다. 제 노트북에는 실제 .env 파일, 설치된 node_modules, 따뜻한 캐시, 그리고 수년간 쌓인 상태들이 있습니다. CI는 그런 것들을 전혀 가지고 있지 않습니다. CI는 실제로 커밋된 것, 즉 원본 상태에서 시작합니다. 그리고 그것이 매우 다른 것이라는 점을 알게 되었습니다.
버그 유형 1: .env.example 파일이 클린 부팅을 방해했다
네 개의 레포지토리 중 두 곳은 자체 예시 환경 파일로부터 부팅할 수 없었습니다.
값들에 따옴표로 감싸지지 않은 공백(예: APP_NAME=My App)이 포함되어 있었는데, dotenv 파서가 이를 전면적으로 거부했습니다. 제 로컬 머신에서는 전혀 문제가 되지 않았습니다. 왜냐하면 저는 몇 달 전에 만든 실제 .env 파일을 가지고 있고 예시 파일을 다시 복사할 일이 없었기 때문입니다. CI는 매번 실행 시 예시 파일을 복사하기 때문에, 가장 먼저 한 일은 이를 파싱하는 데 실패했고, 이는 앱 부팅에 실패하게 했으며, 심지어 실제 테스트가 실행되기도 전에 테스트 스위트의 일부를 무너뜨렸습니다.
이것이 바로
CI는 누락된 값을 명시적으로 드러내는 유일한 방법, 즉 아무것도 없는 상태에서 시작하는 기계로서의 역할을 수행했습니다.
버그 유형 3: 감사 게이트(audit gate)가 실제 알려진 취약점을 포착함
파이프라인이 의존성 감사(dependency audit)를 실행했고, 심각도가 높은(high-severity) 발견 사항으로 인해 실패했습니다. 이는 모든 곳에서 일관되게 나타난 현상은 아니었으며, 제가 여전히 재현할 수 있는 범위의 한계에 대해서도 정확히 말씀드리고 싶습니다:
- 한 레포지토리의 감사 결과 빌드 도구 체인에서 높음(high) 등급이 표시되었습니다. 이는
npm audit fix로 해결할 수 있는vite권고 사항(advisory)이었습니다. 이 사례는 바로 지목할 수 있습니다. 해당 레포지토리의 CI 설정 PR(Pull Request)에 문서화되어 있습니다. - 같은 주에 작성한 메모에 따르면 두 번째 레포지토리의 테스트 러너(test-runner) /
vite체인에서도 권고 사항이 발견되었으나, 오늘 제가 다시 감사할 수 있는 고정된 락파일(pinned lockfile)은 깨끗한 상태로 나옵니다. 따라서 저는 PR 기록에 남아 있는 내용만을 주장하며, 해당 사례는 "당시에는 표시되었으나 현재는 재현되지 않음"으로 남겨두겠습니다. - 나머지 레포지토리들은 기껏해야 중간/낮음(moderate/low) 수준이었습니다.
따라서 솔직한 버전은 "모든 레포지토리가 심각한 문제를 숨기고 있었다"는 것보다는 규모가 작습니다. 적어도 한 개의 레포지토리에는 첫 번째 게이트형 npm audit이 드러낸 실제 높은 심각도의 권고 사항이 있었습니다. 실패를 유발할 수 있는 게이트에서 아무도 npm audit을 실행한 적이 없었기에 그저 조용히 자리 잡고 있었을 뿐입니다.
점수판이 아닌 패턴
이 중 어느 것도 로직 버그(logic bugs)는 아니었습니다. 모든 버그는 "내 컴퓨터에서는 돌아가지만"과 "깨끗한 체크아웃(clean checkout) 상태에서는 돌아가지 않음" 사이의 간극에 존재했습니다. 즉, 망가진 예시 환경(example env), 문서화되지 않은 필수 변수, 아무도 다시 감사하지 않은 오래된 의존성(stale dependency) 등이었습니다. 제 컴퓨터는 이 세 가지를 모두 숨기기에 딱 적합한 상태를 갖추고 있었습니다.
숫자를 세는 것에 대해서도 솔직해지겠습니다. 저는 여러분에게 "이제 13개의 제품에 CI가 적용되었습니다"와 같은 깔끔한 숫자를 제시하지 않을 것입니다. 정확한 수치는 무엇을 제품이라고 부르느냐에 따라 달라지며, 이는 측정이 아니라 그 주가 끝날 때 스스로 집계한 결과이기 때문입니다. 제가 확신을 가지고 말씀드릴 수 있는 것은 더 작지만 더 유용한 사실입니다: CI가 없었던 4개의 레포지토리, 가장 지루할 수 있는 파이프라인을 추가하는 데 걸린 한 오후의 시간, 그리고 각 파이프라인의 첫 실행에서 발견된 실제 결함들입니다.
그것이 제가 실제로 믿는 CI(지속적 통합, Continuous Integration)의 논거입니다. "언젠가 회귀(regression)를 잡아낼 것이다"라는 말은 느리고 추상적인 보상일 뿐입니다. 즉각적인 보상은 CI가 아무것도 없는 상태에서 시작하는 유일한 환경이라는 점입니다. 그리고 아무것도 없는 상태에서 시작할 때, 여러분의 .env.example, 문서화되지 않은 변수들, 그리고 검증되지 않은 의존성(dependencies)들이 한꺼번에 문제를 일으키며 대가를 요구하게 됩니다.
이것은 제가 문서화에 걸었던 것과 동일한 내기이며, 그 이유 또한 같습니다. 한 명의 관리자가 6개 언어로 작성한 261개의 문서의 경우, 인덱스가 생성되며 CI가 어긋날 때 빨간불(fail)이 들어옵니다. 문제는 다르지만 논리는 동일합니다. 매번 확인하는 기계는 컨디션이 나쁜 주를 보내지 않지만, 사람은 그렇기 때문입니다.
만약 현재 CI 없이 운영 중인 레포지토리가 있다면, 여러분이 작성할 수 있는 가장 저렴한 파이프라인이 아마 첫 번째 실행(run #1)에서 그 비용을 충분히 뽑아낼 것입니다.
여러분의 첫 CI 실행은 여러분의 로컬 환경이 숨기고 있던 어떤 문제를 잡아냈나요?
── Hideyuki Mori (Ayane International) 🔗 hideyuki-mori.com
[

](/hideyukimori)
HideyukiMORI팔로우
셀프 호스팅 비즈니스 워크플로우를 위한 API 우선(API-first) PHP 도구 구축 중. NENE2 및 NeNe OSS 시리즈 제작자.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기