grep

Engineering

AI-Driven Development의 시대

원티드

2026년 8월 5일

원문에서 보기 ↗

디자인 시스템 4.0을 에이전트 루프로 만들기

티켓 한 장을 던지면 구현부터 PR, 배포, QA 요청까지 알아서 굴러가는 파이프라인을 만들었습니다. 티켓이 들어와서 머지되기까지 걸리는 시간이 중위 34.9일에서 3.9일로 줄었는데요, 파헤쳐 보니 그 대부분은 “구현이 빨라진” 게 아니라 “기다리는 시간이 사라진” 것이었습니다.

저희 팀은 iOS 디자인 시스템을 SwiftUI 패키지로 관리하고 있습니다. 이름은 Montage이고, 공개 저장소에 MIT 라이선스로 올려두었습니다. 컴포넌트가 60개쯤 되고, 문서 페이지가 62개, 지금까지 낸 릴리즈가 146개입니다.

지금 이 시스템의 4.0 메이저 업데이트를 진행하고 있습니다. 컴포넌트 대부분의 속성 이름과 스타일 토큰이 한꺼번에 바뀌는 작업이라 양이 꽤 많습니다. 문제는 제가 그 사이 다른 프로덕트 작업도 같이 들고 있었다는 점이었어요. 하나씩 붙어서 처리하다가는 언제 끝날지 모르겠다는 생각이 들었습니다.

그래서 이번엔 방향을 좀 바꿔봤습니다. 작업을 하나씩 처리하는 대신, 처리하는 과정 자체를 파이프라인으로 만들어서 에이전트에게 넘기기로 한 겁니다.

이 글은 그렇게 만든 파이프라인의 모양과, 그걸 굴러가게 한 장치들, 그리고 실제로 측정해 본 결과에 대한 기록입니다. 잘된 얘기만 쓰면 재미도 없고 도움도 안 될 것 같아서, 측정이 어디까지만 가능했는지도 같이 적었습니다.

1. 루프의 모양

아이디어 자체는 단순했습니다. 디자이너가 만든 티켓 한 장이 배포와 QA 요청까지 알아서 도달하게 하고, QA에서 나온 이슈가 다시 티켓이 되어 같은 길로 들어오게 하는 것. 그러니까 한 바퀴 도는 고리를 만드는 거죠.

위쪽이 반복해서 도는 루프입니다.

  1. 디자이너가 이슈 트래커에 티켓을 만듭니다. 본문에 Figma 스펙 링크가 들어 있습니다.
  2. 에이전트가 브랜치를 만들고, Figma에서 스펙을 읽어와 구현합니다.
  3. API 문서를 다시 생성하고, 커밋하고, Draft PR을 엽니다.
  4. 컴포넌트 쇼케이스 앱을 TestFlight에 올립니다.
  5. 에이전트가 팀 채널에 변경 내역과 함께 QA를 요청합니다.
  6. 디자이너가 TestFlight에서 확인합니다. 문제가 있으면 QA 티켓을 새로 만들고, 그 티켓이 다시 1번으로 돌아갑니다.

여기서 개발자, 즉 제가 루프 안에 없다는 게 포인트입니다. 저는 옆에서 PR을 보고 승인하거나 다시 하라고 시키고, 머지를 누릅니다.

아래쪽은 루프와 분리된 릴리즈 트랙입니다. 머지가 어느 정도 쌓이면 버전을 릴리즈하고, 그 릴리즈 이벤트가 문서 배포 워크플로우를 건드려서 공개 문서 사이트가 갱신됩니다.

이걸 굳이 따로 떼서 그린 이유가 있습니다. 루프는 여러 바퀴 도는데 릴리즈는 그중 어느 한 시점에만 일어나거든요. 실제로 이 글을 쓰는 지금도 4.0은 QA를 돌고 있고, 공개 문서 사이트는 여전히 3.x를 보여주고 있습니다.

2. 루프가 실제로 도는 모습

말로만 하면 잘 안 읽히니까 실제로 있었던 한 라운드를 보겠습니다. 7월 중순에 컴포넌트 네 개가 한꺼번에 처리되고, 네 개 모두 QA에서 지적이 나와 두 바퀴째로 들어갑니다. 그중 하나를 끝까지 따라가 보겠습니다.

2.1 한 바퀴 — 티켓에서 QA 요청까지

디자이너가 7월 10일부터 15일 사이에 Segmented Control, Chip, Select, Filter Button 네 개의 티켓을 만듭니다. 각 본문에는 Figma 스펙 링크가 걸려 있습니다.

작업 전에 디자이너가 채널에서 먼저 물어봅니다. “outlined 형태를 사용한 게 저거 1개일까요?” 쓰는 곳이 적으면 solid로 통일하고, 많으면 기존 형태를 남겨두는 게 낫겠다는 제안이었습니다. 사실 이런 부분은 자동화가 고도화되더라도 사람의 판단이 들어갈 수밖에 없다는 신호입니다. breaking change를 어디까지 할지는 사람이 먼저 합의하고 시작하는 게 맞으니까요.

7월 15일 하루 사이에 네 개의 PR이 다 열립니다. 두 개는 제가 오후에 각각 시킨 것이고, 나머지 둘은 제가 자리에 없는 저녁 8시에 예약 실행으로 처리됐습니다. 기동 37분 뒤에 Segmented Control PR이 열려 있었습니다. Outlined 타입 제거, iconOnly 추가, 사이즈별 스타일 개편까지.

이튿날 오전, 네 개의 쇼케이스 앱 빌드가 각각 다른 빌드 번호로 TestFlight에 올라가고 슬랙 채널에 QA 요청 메시지가 보내졌습니다. 컴포넌트별로 빌드를 나눈 건 디자이너가 하나씩 따로 확인할 수 있게 하려던 것이었습니다. 이때 제가 쓴 문장을 그대로 옮겨보겠습니다.

QA 부탁드립니다. 다른 작업이 밀려있다보니 이번 작업은 거의 80% AI 작업으로 이루어져서 오류가 있을 수 있습니다. 평소보다 조금 더 꼼꼼히 살펴봐 주시면 감사하겠습니다.

솔직히 이 문장을 쓸 때 조금 망설였습니다. “AI가 짰다”고 밝히는 게 결과물에 대한 변명처럼 들릴 수도 있고 품질에 대한 책임을 떠넘기는 것 같이 보일 수도 있으니까요. 그래도 자동화를 숨기거나 품질 보증을 위해 추가적인 작업을 하는 것보다는, 정확히 밝히고 리뷰를 더 부탁하는 쪽이 낫겠다고 판단했습니다. 지금 돌아보면 이 선택이 루프를 오래 굴러가게 한 이유 중 하나였을지도 모르겠습니다. 실제로 이 시점부터 루프가 돌아가기 시작했거든요.

2.2 두 바퀴째 — QA가 다시 티켓이 됩니다

그날 오후 Filter Button과 Chip에서 지적이 나옵니다. 디자이너가 QA를 끝내고 각각 티켓을 만들고, 저는 각 티켓을 별도 에이전트 세션에 워크트리로 병렬처리하도록 던졌고, 같은 날 반영된 빌드가 두 개 더 올라갔습니다. 디자이너가 다시 확인하고 “이상없습니다”로 닫았습니다.

Select와 Segmented Control은 첫 QA를 통과했다가 나흘 뒤에 지적이 옵니다. 그중 Segmented Control 건이 이 글에서 제일 하고 싶은 얘기입니다.

QA 티켓은 이렇게 쓰여 있었습니다. “large일 때 아래 수치가 맞는지 확인 부탁드립니다 — icon size 18, 아이콘·텍스트 gap 6”, “medium일 때 … height 40, radius 12, icon size 16”. 여기서 짚어둘 게 있습니다. “이렇게 바꿔주세요”가 아니라 “이 수치가 맞는지 확인해 주세요”입니다. 검증할 수 있는 형태로 쓰인 티켓이었죠.

제가 보낸 지시는 딱 한 줄입니다.

[티켓 링크]해결하고 PR 생성해

11분 뒤에 열린 PR 본문에는 네 항목이 하나씩 체크된 검증 결과와, 아이콘 크기 before→after 표가 들어 있었습니다. 상수 하나가 텍스트 모드와 iconOnly 모드를 겸하고 있어서 네 항목을 동시에 만족시킬 수 없었고, 그래서 상수를 분기했다는 설명까지 붙어 있었습니다.

여기서 제가 얻은 건 코드가 아니라 검토 비용이었습니다. Figma를 다시 열거나 diff를 훑을 필요 없이, 이 표를 보고 “이 판정이 맞나”만 확인하면 됐습니다.

그리고 이 형식은 제가 시킨 게 아닙니다. 저장소 PR 템플릿은 「개요 / 수정사항 / 미리보기」 세 칸뿐이고, PR 스킬도 “템플릿 구조를 유지하라”는 것 외에 본문 형식을 지정하지 않습니다. 그러니 이 대목의 교훈은 AI가 똑똑했다는 쪽이 아니라고 생각합니다. 검증 가능한 형태로 쓰인 티켓이 검증 결과 형태의 산출물을 끌어냈다는 쪽입니다. 이 얘기는 5절에서 다시 하겠습니다.

그날 오후 새 빌드가 올라가고 채널에 알림이 갑니다. 디자이너가 확인합니다. “확인완료했고, 이상 없습니다!” 새 QA 티켓이 나오지 않았으니 루프는 여기서 끝입니다. 다음 날 아침에 제가 PR을 머지했습니다.

네 개가 최종적으로 닫힌 건 7월 21일부터 23일까지입니다. 4.0 작업 전체에서 이렇게 구현 티켓과 QA 티켓이 쌍으로 남은 컴포넌트가 7개입니다.

3. 루프를 돌게 만든 것들

루프가 매번 같은 모양으로 도는 건 우연이 아닙니다. 단계마다 뭔가를 만들어 붙여둔 결과인데요, 절차를 고정하는 스킬, 외부 세계와 연결하는 MCP, 규칙이 사는 파일들, 그리고 뜻밖에 큰 역할을 한 문서 파이프라인입니다. 하나씩 보겠습니다.

3.1 위임의 단위 — 스킬

절차를 붙잡아 두는 건 스킬입니다. 스킬은 Claude Code가 읽는 절차서 파일이에요. 슬래시 커맨드로 부르면 그 절차대로 실행합니다.

Montage 작업에 쓰는 스킬은 이렇게 나눠뒀습니다.

스킬하는 일montage-branch티켓에서 브랜치명 만들고 base 브랜치 확인montage-commit변경 분석해서 Conventional Commits 메시지 작성montage-preflightPR 전 검증 항목을 읽기만 해서 수집montage-prpreflight·commit 위임 → 문서 재생성 → 푸시 → Draft PRmontage-blueprint-deploy버전 도출 → 빌드번호 산정 → archive → 업로드montage-release릴리즈·태그 생성montage-icon-syncFigma에서 아이콘 동기화

설계 원칙은 한 줄로 요약됩니다. 판단은 모델에게, 절차는 스킬에게. 무엇을 어떻게 구현할지는 모델이 잘합니다. 반면 빌드 번호 규칙이나 머지 방식, 문서를 언제 다시 생성해야 하는지 같은 건 한 번 틀리면 되돌리기가 꽤 번거로워요. 그런 것들만 스킬에 박아 넣었습니다.

제일 신경 쓴 건 montage-preflight를 montage-pr에서 떼어낸 부분입니다. preflight는 공개 API가 바뀌었는지, docstring이 빠졌는지, 쇼케이스 예제가 있는지, 보호 브랜치인지, 이미 PR이 있는지를 확인하는데 판단도 질문도 하지 않고 수집한 결과만 돌려줍니다. 읽는 일과 실행하는 일을 갈라놓으면, 에이전트가 잘못 판단했을 때 그 피해 반경이 좁아집니다.

분량으로 보면 montage-blueprint-deploy가 532줄, montage-pr이 272줄, preflight가 109줄입니다. 배포 스킬이 제일 긴 건 실패할 수 있는 경우가 제일 많아서, 예외 처리 분량이 그만큼 붙었기 때문입니다.

3.2 MCP — 외부 세계, 그리고 반대 방향

스킬이 절차라면 MCP(Model Context Protocol)는 손발입니다. 이 루프에서 에이전트는 MCP를 통해 이슈 트래커에서 티켓을 읽고, Figma에서 디자인 컨텍스트와 변수를 읽고, 팀 채널에 메시지를 보내고, iOS 시뮬레이터를 띄워 스크린샷으로 자기가 만든 결과를 확인합니다.

그런데 여기서 조금 특이한 게 하나 있습니다. 디자인 시스템 자체가 MCP 서버이기도 하다는 점입니다.

저장소 안에 packages/montage-mcp가 있습니다. 이 서버는 list_components, get_component, list_tokens, list_icons, get_color_usage, coding_guidelines 같은 도구를 노출합니다. 앱을 만드는 쪽 에이전트가 "이 디자인 시스템에서 버튼을 어떻게 쓰면 되냐"고 물어보면 이 서버가 답을 해주는 구조입니다.

여기서 중요한 건 이 서버가 따로 관리하는 문서 사본이 아니라는 점입니다. 사람이 읽는 문서와 기계가 읽는 인터페이스가 같은 docstring에서 갈라져 나옵니다. 이 구조가 왜 중요한지는 3.4에서 자세히 다루겠습니다.

그러니까 이 루프에서 AI는 디자인 시스템의 생산자이면서 동시에 소비자입니다. 한쪽에서는 에이전트가 컴포넌트를 고치고 있고, 다른 쪽에서는 또 다른 에이전트가 그 컴포넌트 사용법을 물어보며 앱 코드를 쓰고 있는 거죠. 처음부터 이걸 노린 건 아니었는데, 만들어놓고 보니 이 구조가 꽤 마음에 듭니다.

3.3 규칙을 어디에 둘 것인가

에이전트에게 같은 말을 두 번 하게 되면, 그건 프롬프트가 아니라 파일에 있어야 한다는 신호입니다. 이 프로젝트에서 규칙은 결국 네 군데에 나눠 자리를 잡았습니다.

CLAUDE.md — 저장소 규약입니다. 파일명과 타입명을 맞춰라, public 키워드 빼먹지 마라 같은 것들인데, 대부분 3.4에서 설명할 문서 파이프라인 때문에 생긴 제약입니다.

스킬과 훅 — 3.1에서 본 스킬도 규칙이 사는 곳입니다. 그리고 훅이 그 스킬들과 같은 플러그인에 함께 들어 있습니다. 이 훅은 프롬프트를 보낼 때마다 “git·배포 작업은 맨손 명령 대신 스킬로 태우라”는 안내를 끼워 넣습니다. 사람이 깜빡하거나 에이전트가 지레짐작으로 git push를 하려 할 때 여기서 걸리죠. 스킬과 훅이 한 묶음으로 배포되니, 플러그인을 설치한 팀원은 같은 절차와 같은 가드를 함께 받습니다.

에이전트 메모리 — 이쪽은 개인 영역입니다. 제 로컬 경로나 작업 습관처럼, 저장소에 적으면 남에게 강요가 되는 것들이 여기로 갑니다. 저는 워크트리를 여러 개 띄워 병행 작업을 하다 엉뚱한 브랜치에 커밋한 적이 있어서 “커밋 전에 현재 브랜치를 확인하라”를 넣어뒀는데, 한 브랜치만 쓰는 사람에게는 없어도 되는 단계죠.

정리하면 CLAUDE.md와 스킬·훅은 다른 개발자와 공유하는 규칙 이고, 에이전트 메모리는 제 개인 작업 규칙입니다. 앞의 셋은 저장소나 플러그인으로 배포되니 팀원 모두에게 적용되고, 메모리는 제 머신에만 남습니다.

예약 태스크 — 이건 좀 특이한 케이스입니다. 공유 규칙도 개인 규칙도 아니고, 그때 한 번 돌릴 작업을 위한 일회성 명세니까요. 2.1에서 저녁 8시에 실행됐던 그 작업인데, 실행 시각만 걸어두면 끝나는 게 아니라 무엇을 어떻게 할지와 평소와 다른 예외적인 컨텍스트를 제가 다 써서 넣었고, 그 명세가 5KB쯤 됐습니다. 이런 걸 적었습니다.

무인 실행이 되게 하려면 사람이 없는 동안 판단이 필요한 지점을 미리 다 없애 둬야 합니다. 그리고 이렇게 적은 것 중에 반복적으로 필요해지는 게 보이면, 일회성 명세에 묻어두지 말고 스킬이나 CLAUDE.md 같은 공유 규칙으로 올려야 합니다. 그게 하네스가 두꺼워지는 과정입니다.

3.4 문서를 위해 만든 제약이 에이전트를 도왔다

예상하지 못했는데 이 루프에서 큰 역할을 한 게 문서 자동화였습니다. 더 정확히는, 문서를 자동으로 만들려고 코드에 걸어둔 제약들이었습니다.

먼저 파이프라인부터 보겠습니다. Swift 소스의 docstring이 단일 소스입니다. make를 돌리면 DocC 아카이브가 만들어지고, 그걸 입력으로 생성기 두 개가 각자의 출력을 만듭니다. 하나는 공개 문서 사이트가 쓰는 Markdown, 다른 하나는 MCP 서버가 읽는 JSON 인덱스입니다.

사람이 읽는 문서와 기계가 읽는 인터페이스가 같은 아카이브에서 갈라져 나오는 셈입니다. 그래서 둘이 어긋날 수가 없습니다. 컴포넌트 속성 하나를 고치면 양쪽이 함께 바뀌고, 둘 중 하나만 갱신한 채로 커밋하려 하면 CI가 막습니다. verify-docs 워크플로우가 그 일을 하는데, 최근 100번 실행에서 7번 걸렸습니다. 사람은 "아 깜빡했다" 하고 넘어가는데 CI는 안 봐주거든요.

릴리즈를 발행하면 auto-deploy-docs 워크플로우가 돌면서 문서 사이트 저장소의 동기화 워크플로우를 원격으로 건드립니다. 최근 12번 모두 성공했습니다. 이 워크플로우는 제가 만든 게 아니라 문서 사이트를 개발한 디자인 시스템 웹 개발자가 구성해줬습니다. 저장소 두 개에 걸친 트리거라 문서 사이트 쪽 워크플로우와 권한을 아는 사람이 붙어야 했거든요. 이 루프가 저 혼자 만든 게 아니라는 표시이기도 합니다. 아무튼 그래서 에이전트가 쓴 docstring이 며칠 뒤 공개 문서의 문장으로 게시되는데, 그 사이에 사람이 옮겨 적는 단계가 하나도 없습니다.

이 자동화에는 대가가 있었습니다. 문서를 코드에서 뽑아내려면, 코드가 생성기의 규칙을 따라야 합니다. 그래서 저장소에 이런 제약이 붙었습니다.

사람 입장에서 이건 그냥 번거로운 규칙입니다. 파일을 편한 대로 쪼갤 수 없고, 타입 이름을 바꾸면 파일도 같이 옮겨야 합니다. 문서 때문에 코드 구조가 묶이는 거죠. 그 당시에는 문서를 작성하면서도 이 규칙들이 지속 가능성이 있을까 하는 고민이 있었습니다.

그런데 에이전트에게는 이게 전부 이득이었습니다. 지금 와서 돌아보면, 문서 생성기를 위해 만들어둔 제약이 결과적으로 AI가 일하기 좋은 코드베이스를 만들어놓은 셈이 되었습니다.

에이전트 입장에서 이 저장소는 “출력 형식이 이미 정해진 작업”인 셈입니다. 어디에 무엇을 어떤 형식으로 쓰면 되는지가 미리 다 정해져 있으니까요. 물론 이런 효과를 예상하고 만든 규칙은 아니었습니다. 그 얘기는 7절에서 하겠습니다.

4. 지시문이 짧아지는 궤적

앞 절의 것들을 하나씩 붙여두면 무엇이 달라지는가. 그게 제일 잘 드러난 곳은 뜻밖에도 제가 에이전트한테 보내는 말이었습니다. 2주 사이의 변화를 세션 로그에서 그대로 옮겨왔습니다.

초기 (7월 10일) — 네 문장입니다.

[티켓 링크] 이거 작업해

먼저 티켓 내용을 읽어서 작업 범위를 파악하고, 요약과 함께 어떻게 진행할지 계획을 알려줘. 아직 확정 전이면 큰 변경을 바로 커밋하지 말고 계획을 먼저 공유해줘. (저장소명) 레포에서 작업.

티켓 읽는 방법, 커밋해도 되는 시점, 대상 저장소까지 하나하나 다 지정하고 있습니다. 규칙이 아직 프롬프트 안에 들어 있는 단계죠. 이때는 좀 불안했던 것 같습니다.

성숙기 (7월 21일) — 한 줄입니다.

[티켓 링크]해결하고 PR 생성해

2.2에서 인용한 그 한 줄입니다. 브랜치 규칙, 커밋 컨벤션, 문서 재생성, PR 형식은 이미 스킬과 CLAUDE.md에 들어가 있으니 프롬프트에서 사라졌습니다. 남은 건 "무엇을"뿐이에요. 그리고 이날 저는 61초 간격으로 서로 다른 워크트리에 티켓 두 개를 던졌습니다. git worktree로 작업 공간을 갈라두면 이런 병렬 처리가 됩니다. 이 격리가 없으면 브랜치가 서로 엉킵니다.

위임 (7월 24일) — 다시 길어지는데, 성격이 완전히 다릅니다.

[티켓 3개 목록]

위 티켓들 차례로 해결해서 스택PR로 만들어줘. 각 PR 은 다음과 같이 진행해

1. 티켓 설명에 있는 피그마나 문서 링크들을 꼼꼼히 확인해서 빠짐없이 반영해줘

  1. 수정이 끝나면 PR을 생성하고 코드 리뷰 봇 코멘트들을 해결한 후 더 이상의 코멘트가 없고 CI check 가 모두 성공하면 쇼케이스 앱 배포를 진행해

  2. 배포가 끝나면 (팀 채널)에 업데이트 소식을 메시지로 알려줘

길어진 건 절차 설명이 아니라 위임 범위입니다. PR 생성부터 리뷰 봇 대응, CI 통과 확인, 배포, 채널 공지까지 한 프롬프트에 다 들어갔어요. 루프 한 바퀴가 그대로 프롬프트 하나가 된 모양입니다.

정리하면, 지시문은 네 문장에서 한 줄로 줄었는데 위임 범위는 구현에서 배포·공지까지 늘었습니다. 줄어든 만큼이 스킬과 규약 파일로 옮겨간 거죠.

한 단계 더 나아간다면 이런 부분이 다시 스킬에 업데이트되거나 새로운 오케스트레이션 스킬로 만들어질 수도 있을 겁니다.

5. 사람은 뭘 하나

로그를 다시 읽으면서 이 루프에서 사람이 실제로 하는 일을 뽑아 봤는데, 네 가지였습니다.

하나, 없는 맥락을 보태는 일. 어떤 QA 티켓에는 색상 토큰명이 적혀 있었는데 그게 개편 이전 기준이었어요. 티켓만 읽으면 알 수가 없습니다. 그래서 이렇게 맥락을 보충했습니다. “컬러토큰은 변경되기 이전 기준으로 적혀있어서 (참조 문서)를 참조해서 4.0 토큰으로 적용해.”

둘, 범위를 잘라내는 판단. 다른 QA 티켓에는 속성 명칭을 바꾸자는 요청이 섞여 있었습니다. 좀 더 논의가 필요한 사안이라 이렇게 잘랐습니다. “leadingImage, trailingImage 명칭은 일단 보류하고 나머지만 해결해.” 채널에는 “명칭 변경은 추가 논의가 필요해서 이번엔 보류했습니다”로 QA 요청메시지가 전송됐습니다.

앞의 둘은 작업이 도는 도중에 제가 끼어든 지점입니다. 나머지 둘은 성격이 다릅니다.

셋, 검증할 수 있게 티켓을 쓰는 일. 이건 제 몫이 아니었습니다. 2.2에서 QA 티켓이 “이렇게 바꿔주세요”가 아니라 “이 수치가 맞는지 확인 부탁드립니다”로 쓰여 있었다고 했죠. 그 한 줄 차이로 에이전트에게 대조할 기준이 생기고, 출력도 검증 결과 형태로 돌아옵니다. 디자이너 쪽 기여였고, 루프에서 사람이 하는 일 중 제일 눈에 안 띄는 부분입니다. 자동화를 붙일 때 프롬프트나 스킬만 손보게 되는데, 정작 입력을 쓰는 사람 쪽이 더 중요할 수도 있다는 뜻입니다.

넷, PR 리뷰 과정을 검토하고 머지/배포하는 일. PR 리뷰에는 여전히 사람이 개입합니다. 다만 사람이 하던 리뷰의 상당 부분은 봇 리뷰가 대신 하게 됐습니다. 코드 리뷰 봇이 코멘트를 달면 에이전트가 그걸 읽고 고치고 답까지 다는 루프가 생겼거든요. 사람이 diff를 한 줄씩 짚던 자리를 봇이 메우고, 저는 봇이 뭘 지적했고 에이전트가 어떻게 답했는지를 확인 하는 쪽으로 옮겨갔습니다. 문제가 있다고 판단될 때는 직접 끼어들기도 합니다. 이 관문을 통과하는 시간 자체는 절반이 됐습니다. PR이 열린 뒤 머지까지 41.8시간에서 21.9시간입니다.

관문은 리뷰에서 끝나지 않습니다. 머지와 릴리즈 발행 판단도 사람이 합니다. 에이전트는 Draft PR까지만 만듭니다. 이 선은 일부러 그었습니다.

6. 그래서 얼마나 빨라졌나

두 메이저 버전을 비교해 봤습니다. 이슈 트래커의 티켓과 머지된 PR을 연결해서, 티켓이 들어온 순간부터 머지까지 걸린 시간을 쟀습니다. 이전 버전 34건, 현재 버전 28건입니다.

6.1 감소분은 어디서 나왔나

티켓 접수 → 머지 (중위값): 34.9일 → 3.9일.

9배라니. 숫자가 너무 크고 극적이어서 의심이 들었습니다. 구간을 나눠보니 역시 함정이 있었습니다.

줄어든 것의 대부분이 대기 구간에서 나왔습니다. 대기가 31.0일에서 1.4일로 줄어든 반면, 착수 기록 이후 구간은 47.7시간에서 23.8시간으로 줄었을 뿐입니다. 참고로 구간별로 따로 낸 중위값이라 두 구간의 합이 앞의 전체 중위값과 딱 맞지는 않습니다. 중위값은 더해지는 값이 아니니까요.

6.2 대기를 같은 조건으로 맞춰보면

이전 버전 때는 기간 앞쪽에 티켓을 몰아서 만들어두는 습관이 있었습니다. 그러면 대기 시간이 자동으로 길어지죠. 그래서 그 효과를 걷어내 보기로 했습니다. 양쪽 티켓의 대기 구간을 같은 값으로 통일하고 나머지만 비교하는 방식입니다.

4.0 시기의 대기 중위값(1.4일)을 양쪽에 적용하면 3.4일 대 2.4일이 됩니다. 8.9배가 1.4배로 확 줄어듭니다.

그런데 이 보정에도 함정이 하나 있었습니다. 치환하는 상수를 뭘로 잡느냐에 따라 배수가 흔들립니다. 대기 평균값(2.8일)을 쓰면 4.8일 대 3.8일이 되어서 1.26배가 됩니다. 같은 데이터인데 배수가 1.26배에서 1.4배까지 움직이는 거죠. 상수를 양쪽에 똑같이 더하는 연산이니 차이는 고정되고 비율만 상수에 따라 변하기 때문입니다.

고정되는 값은 이렇습니다. 대기를 걷어낸 구간에서 이전 버전이 약 24시간 더 걸렸습니다. 비율로는 2.0배(47.7시간 대 23.8시간)입니다.

그러니 이 루프의 효과는 숫자 하나로 말하기보다 범위로 말하는 게 정직할 것 같습니다.

위가 상한이고 아래가 하한입니다. 진짜 값은 그 사이 어딘가에 있을 텐데, 대기가 줄어든 것 중 얼마가 프로세스 습관 변화이고 얼마가 파이프라인 처리 능력인지를 갈라낼 방법이 없어서 더 좁히지 못했습니다.

그리고 하한 쪽에도 한계가 있습니다. 뒤에서 얘기하겠지만 착수 기록이 실제로는 브랜치 푸시 시점에 찍히기 때문에, 보정 후에 남는 구간은 사실상 “PR이 열린 뒤 머지될 때까지”에 가깝습니다. 실제로 PR 생성 시점부터 머지까지만 따로 재봐도 41.8시간에서 21.9시간(1.9배)으로 거의 같은 값이 나옵니다. 5절에서 인용한 그 숫자입니다.

즉 이 2.0배는 구현이 빨라진 정도가 아니라 리뷰가 빨리 돌아간 정도입니다.

6.3 솔직하게 밝혀둘 것 셋

하나, 순수 구현 시간은 결국 못 쟀습니다. 두 시기 모두 “작업 시작”이라는 이벤트가 어디에도 안 남아 있었어요. 티켓 상태가 진행 중으로 바뀌는 시점을 착수로 보려고 했는데, 실제로 재보니 그 전이가 브랜치 푸시 시점에 찍히더군요. 착수가 아니라 마무리 시점이라는 뜻입니다. 첫 커밋 시각도 못 씁니다. 작업을 다 해놓고 커밋한 경우가 많았거든요.

미련이 남아서 PR 생성 시점으로 한 번 더 잘라 「접수 → 착수 기록 → PR 생성 → 머지」 세 구간으로도 나눠봤습니다. 가운데 구간이 구현 시간이라면 여기서 드러나야 하니까요. 그런데 그 구간이 중위 24분(3.0)과 1분 미만(4.0) 입니다. 심지어 4.0에서는 28건 중 5건이 PR이 열린 뒤에 착수로 기록돼 있어서 값이 음수였어요. 결국 구현 시간은 접수~착수 기록 구간 안에 대기와 섞여 있고, 둘을 가르는 기록이 없습니다.

둘, 티켓 만드는 패턴이 달라졌습니다. 3.0에서는 앞쪽에 티켓을 일괄로 만들어 쌓아뒀고, 4.0에서는 필요할 때마다 만들었습니다. 대기가 줄어든 것의 일부는 이 프로세스 변화에서 옵니다. 물론 “며칠 안에 처리된다는 신뢰가 생겼으니 미리 쌓아둘 이유가 없어진 것”도 루프의 효과이긴 하겠지만요.

셋, 표본이 작습니다. 34건과 28건입니다. 추세로만 읽어야 합니다.

6.4 그 밖에 남겨둘 만한 숫자들

7. 무엇이 이걸 가능하게 했을까

이 루프는 설계도를 그려놓고 만든 게 아닙니다. 스킬이 저장소에 처음 들어온 게 3월 말이고 마지막 스킬이 6월 중순인데, 그 사이 여섯 번의 커밋으로 하나씩 늘었습니다.

특히 좀 재밌는 기록이 하나 있습니다. PR 스킬을 만든 바로 다음 날 preflight 스킬이 분리됐어요. PR 스킬을 만들어서 한 번 돌려보니 검증 단계가 실행 단계에 섞여 있는 게 문제라는 걸 그날 알아챈 거죠. 큰 그림이 있어서 나눈 게 아니라, 한 번 데어보고 나눈 겁니다. 이 루프에 있는 대부분의 구조가 대개 이런 식으로 생겼습니다.

두 번째로, 작년부터 해오던 전혀 다른 일이 예상 밖으로 도움이 됐습니다. 이 저장소를 오픈소스로 전환하려고 코드와 문서를 정비하고 API 문서 파이프라인을 만들고 라이선스를 붙이는 작업을 했는데, 그게 에이전트 도입보다 10개월 전입니다.

3.2에서 본 것처럼, 그때 쓴 docstring이 MCP 서버를 통해 지금 에이전트가 실제로 조회하는 데이터가 됐습니다 . 사람 보라고 쓴 문서가 문자 그대로 기계 입력이 된 거죠. 또한 비교 대상이 없어서 확인할 방법은 없지만 코드가 정돈돼 있어서 에이전트가 더 잘 이해했을 수 있다고 생각합니다.

짧게 남은 교훈들입니다.

맺으며 — 일해라 클로드

여기까지는 개발자 쪽 이야기였습니다. 그런데 알고 보니 저쪽에서도 똑같은 일이 벌어지고 있었습니다.

저희 디자인 시스템 디자이너가 최근 쓴 글의 제목이 「컴포넌트 하나 고치는 데, 온 MCP가 다 필요하다」입니다. 아바타 모서리 둥글기 하나 고치려면 세 플랫폼 현황 파악, 리서치, Figma 브랜치 작업, 문서 갱신, 티켓 발급, 채널 공유까지 여섯 단계를 거쳐야 했고 그게 5일 걸렸다는 이야기. 그리고 그걸 에이전트에게 넘겨서 1일로 줄인 이야기입니다. 결론이 이렇게 적혀 있습니다. 판단과 검수는 AI가 아닌 사람이 직접 한다.

제가 이 글에서 도달한 결론과 사실상 같은 문장이더군요. 서로 상의한 적도 없는데, 각자 자기 쪽 반복 작업을 파이프라인으로 만들고 판단만 남겨둔 겁니다.

문제는 양쪽이 동시에 그러고 있다는 점입니다. 앞에서 인용한 그 QA 스레드에 이런 대화가 남아 있습니다.

그래서 요즘 저희 팀의 실제 워크플로우는 대충 이렇게 정리됩니다.

농담인데 절반은 사실입니다. 그리고 이 루프에서 사람이 아직 붙어 있는 자리가 어디인지도 알 수 있는 것 같습니다. 무엇을 만들지 결정하는 사람과, 만든 것을 배포할지 승인하는 사람. 다행히 아직은 사람이 필요합니다.

Montage 4.0 은 지금도 이 루프를 돌고 있고, 몇 바퀴 더 돌면 릴리즈될 예정입니다. 4.0에서는 컴포넌트 속성명과 시맨틱 컬러 토큰이 꽤 많이 달라지고, iOS 고유 기능인 Dynamic Type 대응과 접근성도 이번에 같이 손을 봤습니다. 깃헙 저장소에서 release/4.0.0 브랜치를 통해 그 내용을 미리 확인해 보실 수 있으며, 릴리즈되면 문서 사이트도 업데이트 될 예정입니다. 많은 관심 부탁드립니다!