DX 하네스 v2 설계기: Jenkins를 없애는 것보다 어려웠던 일
v1 하네스는 혼자 테스트하며 구조와 인프라 선택지를 검증한 PoC에 가까웠습니다. 이 과정에서 공통 도구를 레포마다 복사해 넣는 방식의 한계, skill 버전 드리프트, 프로젝트별 업데이트 전파 비용을 확인했습니다. v2는 이 경험을 바탕으로 실제 사내 프로젝트에서 사용하고 피드백을 받을 수 있는 운영 도구로 다시 설계한 버전입니다.
Jenkins를 GitHub Actions로 전환하는 작업을 시작했을 때, 처음 문제는 단순해 보였습니다. Jenkins 서버 2대를 운영하며 월 20만 원의 고정 비용이 발생하고 있었고, 현재 회사의 배포 수준에서는 GitHub Actions로도 충분히 대체할 수 있다고 판단했습니다.
하지만 실제로 들여다보니 문제는 "Jenkins를 없앨 것인가"가 아니라 "팀이 앞으로 프로젝트를 어떻게 시작하고, 검증하고, 배포하고, AI 에이전트와 협업할 것인가"에 가까웠습니다. 이 글은 그 고민을 바탕으로 사내 DX 하네스 v2를 설계하며 얻은 인사이트를 정리한 기록입니다.
The Problem: 도구 교체만으로는 DX가 바뀌지 않는다
기존 Jenkins 환경에는 몇 가지 문제가 동시에 있었습니다.
첫째, 고정 비용이 있었습니다. Jenkins 서버 2대에 각각 월 10만 원씩, 총 월 20만 원이 발생했습니다. 비용 자체가 엄청나게 큰 것은 아니지만, Jenkins를 깊게 활용하지 못하는 상황에서는 불필요한 고정비에 가까웠습니다.
둘째, 운영 책임이 명확하지 않았습니다. 별도 Jenkins 관리 담당자가 있는 구조가 아니었고, 최초 설정 이후 플러그인과 버전 관리가 거의 이루어지지 않았습니다. 배포 실패가 발생했을 때도 원인이 배포 스크립트인지 Jenkins 서버 자체 장애인지 분리해 추적해야 했습니다. 실제로 Jenkins 서버가 죽어 배포가 지연되는 경우도 있었습니다.
셋째, 배포 병목이 있었습니다. 여러 프로젝트가 같은 Jenkins 서버에 묶여 있었고, 여러 프로젝트를 동시에 업데이트해도 하나의 프로젝트 배포가 완료된 뒤 다음 프로젝트가 배포되는 방식으로 동작했습니다. 서버 스펙이 작아 slave를 늘리기도 어려웠습니다.
넷째, 시크릿 관리가 불투명했습니다. Jenkins UI에 env와 외부 서비스 토큰을 직접 넣어 관리하다 보니, 어떤 값이 어떤 프로젝트와 환경에 정확히 반영되어 있는지 확인하기 어려웠습니다. 값 누락이나 오입력 같은 휴먼 오류도 생길 수 있었습니다.
여기까지만 보면 GitHub Actions workflow를 잘 만들면 해결될 것처럼 보입니다. 하지만 저는 이 지점에서 한 가지 더 고민했습니다.
workflow 파일을 프로젝트마다 복사하는 방식은 정말 DX 개선인가?
신규 프로젝트가 생길 때마다 GitHub repo 생성, 팀 권한 설정, front/back 구조 선택, Infisical 연결, workflow placeholder 치환, GitHub Secrets 등록, 브랜치와 환경 매핑을 사람이 다시 처리해야 한다면 Jenkins만 사라졌을 뿐 운영 복잡도는 그대로 남습니다.
Insight 1: 재사용성은 복사가 아니라 업데이트 전파다
처음 하네스를 만들었을 때는 공통 도구와 skill을 각 프로젝트 모노레포 안에 포함하는 방식이었습니다. 초기 적용은 쉬웠습니다. 레포 안에 필요한 것이 다 있으니 당장 사용할 수 있었기 때문입니다.
하지만 시간이 지나자 문제가 선명해졌습니다.
- 프로젝트마다 GStack과 skill 버전이 달라졌습니다.
- 어떤 프로젝트는 최신 skill을 쓰고, 어떤 프로젝트는 예전 skill에 머물렀습니다.
- 공통 변경이 생길 때마다 각 레포에 변경분을 복사하고 커밋해야 했습니다.
문제의 핵심은 레포 용량이나 clone 시간보다 버전 드리프트였습니다. 공통 도구를 복사해 넣는 방식은 "처음 설치"는 쉽게 만들지만, "계속 같은 기준으로 유지"하는 데 실패합니다.
그래서 v2 하네스에서는 shared와 project-owned 경계를 나눴습니다. 공통 정책, 스크립트, shared skill은 upstream에서 업데이트하고, apps/ **, .github/** , README.md, 패키지 파일, .planning/ **, .harness/skills-local/** 같은 프로젝트 소유 파일은 업데이트가 덮어쓰지 않게 했습니다.
shared 파일도 로컬 변경이 있으면 자동으로 덮어쓰지 않도록 막았습니다. 누락된 shared 파일은 manifest 기준으로 복구하고, upstream에서 제거된 stale 파일은 shared root 안에서만 정리했습니다.
이 경험을 통해 재사용성에 대한 관점이 바뀌었습니다. 재사용성은 같은 파일을 여러 곳에 복사하는 것이 아니라, 공통 변경이 생겼을 때 프로젝트별 소유권을 침범하지 않고 안전하게 전파되는 구조입니다.
Insight 2: CI/CD의 목적은 빠른 배포만이 아니다
GitHub Actions로 전환하면서 pipeline.yml을 단일 진입점으로 설계했습니다. 변경 파일을 감지해 백엔드와 프론트엔드 배포 여부를 나누고, PM2, Docker, Vercel 배포는 reusable workflow로 분리했습니다.
이 구조는 불필요한 배포를 줄이기 위한 것이기도 했지만, 더 중요한 목적은 검증 기준을 한곳에 모으는 것이었습니다. 배포 전에는 Node 검증과 dependency security 검사를 공통 gate로 두었습니다.
흥미로운 점은 CI/CD 시간이 단순히 줄지 않았다는 것입니다. 오히려 패키지 상태와 보안 검사가 추가되면서 일부 검증 시간은 늘었습니다. 하지만 이 트레이드오프는 받아들일 만하다고 판단했습니다.
배포가 몇 분 빨라지는 것보다, 문제가 있는 패키지를 배포 전에 발견하고 Slack으로 빠르게 인지하는 것이 더 중요할 때가 있습니다. 특히 팀 규모가 작고 운영 인원이 줄어든 상황에서는 "빠르게 배포하는 능력"만큼이나 "위험을 빨리 발견하는 능력"이 중요합니다.
CI/CD를 단순 배포 자동화가 아니라 운영 리스크 감지 장치로 봐야 한다는 것이 이 프로젝트에서 얻은 두 번째 인사이트였습니다.
Insight 3: 시크릿은 저장 위치보다 책임 경계가 중요하다
Jenkins UI에 env를 직접 넣던 방식의 가장 큰 문제는 값이 어디에 있는지가 아니라, 책임 경계가 불명확하다는 점이었습니다. 백엔드 런타임 값, 프론트엔드 런타임 값, GitHub Actions 배포 전용 값, Slack/Vercel 같은 외부 서비스 토큰이 같은 방식으로 관리되면 검토와 접근 제어가 어려워집니다.
그래서 GitHub Secrets에는 Infisical 접근용 bootstrap secret만 두고, 실제 secret 원본은 Infisical로 분리했습니다.
/backend: 백엔드 런타임 환경변수/frontend: 프론트엔드 런타임 환경변수/backend/github-actions: 백엔드 배포 workflow 전용 변수/frontend/github-actions: 프론트엔드 배포 workflow 전용 변수- Shared-Secrets
/slack,/vercel: 공용 외부 서비스 토큰
이렇게 경로를 나누니 secret을 단순히 "보관"하는 것이 아니라 "누가 어떤 목적으로 쓰는 값인지" 분리할 수 있었습니다. GitHub Actions는 배포 시점에 필요한 값만 Infisical에서 읽고, GitHub에는 Infisical에 접근하기 위한 최소한의 secret만 남습니다.
Infisical을 셀프호스팅한 이유도 같은 맥락입니다. 당시 팀에는 환경변수를 안전하게 보관하고 공유할 중앙 장소가 없었고, 팀원이 같은 기준으로 secret을 조회하고 관리할 수 있는 구조가 필요했습니다.
Insight 4: AI 에이전트 도입은 생산성 문제가 아니라 운영 규칙 문제다
하네스에는 GSD, GStack, Superpowers를 결합했습니다. 처음에는 CI/CD와 AI 에이전트 작업 흐름이 별개의 문제처럼 보일 수 있습니다. 하지만 저는 두 문제가 같은 뿌리를 갖고 있다고 봤습니다.
CI/CD가 배포 방식의 일관성을 만드는 도구라면, AI 에이전트 하네스는 개발 작업 방식의 일관성을 만드는 도구입니다.
개발자마다 Codex나 Claude Code에 주는 프롬프트와 작업 스타일은 다를 수 있습니다. 이 차이를 직접 통제하기는 어렵습니다. 하지만 최소한의 phase, 검증 기준, 위험 작업 경계는 팀 공통으로 맞출 수 있어야 합니다.
그래서 하네스에는 아래 기준을 넣었습니다.
- Brainstorming, Planning, Execution, Review, Verification 흐름
- GSD의
.planning/기반 장기 작업 상태 관리 - GStack의 보안, 리뷰, QA, ship gate
- Superpowers의 TDD, debugging, verification discipline
rm -rf,git reset --hard, production deploy/rollback,gh pr merge같은 위험 작업 guardrailproject-profile.yaml기반apps/front,apps/back경계 관리- Next.js/NestJS/React/Express별 패키지 매니저 정책
.harness/skillsshared,.harness/skills-localproject-owned 분리
AI 에이전트가 빠르게 코드를 만들 수 있다는 점은 장점입니다. 하지만 빠르게 잘못된 방향으로 갈 수도 있습니다. 특히 파일 삭제, 히스토리 변경, production deploy, PR merge처럼 사람의 판단이 필요한 영역까지 AI가 넘나들면 생산성보다 리스크가 커집니다.
그래서 하네스의 guardrail은 AI를 못 쓰게 막는 장치가 아니라, AI를 팀 개발 흐름에 안전하게 넣기 위한 운영 경계입니다.
The Result: 비용 절감보다 더 큰 것은 운영 모델의 정리였다
가장 명확한 성과는 Jenkins 서버 2대 제거 또는 제거 예정에 따른 월 20만 원 절감입니다. 기존 Jenkins 환경에서 여러 프로젝트가 순차 배포되던 병목도 GitHub Actions의 프로젝트별 workflow 실행 구조로 완화할 수 있었습니다.
하지만 이 프로젝트의 더 큰 의미는 도구 전환보다 운영 모델의 정리에 있습니다.
- 신규 프로젝트 세팅을 CLI와 doctor 검증 흐름으로 바꿨습니다.
- PM2, Docker, Vercel 배포 방식을 하나의 pipeline 진입점으로 묶었습니다.
- GitHub Secrets surface를 최소화하고 Infisical을 secret source of truth로 두었습니다.
- 공통 하네스 업데이트와 프로젝트 소유 파일 보존을 동시에 만족하는 구조를 만들었습니다.
- Codex와 Claude Code를 써도 같은 phase와 검증 기준을 따르게 했습니다.
- Jenkins에서 GitHub Actions로 전환 및 적용한 프로젝트는 7개 , 실제 사용 팀원은 3명 입니다.
- 현재 사내 사용 과정에서 들어오는 피드백을 바탕으로 doctor 검증과 업데이트 정책을 보완하고 있습니다.
Retrospective: DX는 개발자가 기억하지 않아도 되는 것을 늘리는 일이다
이 프로젝트를 하며 DX에 대한 생각이 조금 바뀌었습니다. 좋은 DX는 단순히 명령어를 짧게 만드는 것이 아닙니다. 개발자가 매번 기억해야 했던 정책, 순서, 예외, 위험 경계를 도구가 대신 들고 있게 만드는 것입니다.
Jenkins에서 GitHub Actions로 옮긴 것은 비용과 병목을 줄이기 위한 선택이었습니다. 하지만 ./harness CLI, Infisical wiring, shared/project-owned update, AI guardrail을 함께 만든 이유는 팀이 같은 기준으로 일하기 위해서였습니다.
결국 DX 하네스의 목표는 개발자를 느슨하게 통제하는 것이 아니라, 반복되는 판단을 줄이고 더 중요한 판단에 집중하게 만드는 것입니다. 프로젝트를 시작할 때는 CLI가 기본 구조를 만들고, 배포 전에는 pipeline이 검증하고, 위험 작업 앞에서는 guardrail이 멈춰 세우고, 긴 작업은 .planning/에 상태를 남깁니다.
제가 이 작업에서 얻은 가장 큰 인사이트는 이것입니다.
내부 도구의 가치는 기능 수가 아니라, 팀이 같은 기준으로 안전하게 반복할 수 있는 일을 얼마나 늘렸는가로 판단해야 한다.
Jenkins 제거는 시작이었고, 진짜 목표는 반복 가능한 개발 운영 모델을 만드는 것이었습니다.