주도하기(Driving) vs 위임하기(Delegating): AI와 페어 프로그래밍하는 두 가지 방식의 시간 측정
요약
AI 코딩 어시스턴트 활용 시 '주도하기(Driving)'와 '위임하기(Delegating)' 방식의 효율성을 비교 분석합니다. 작업의 속도는 단순히 코드 생성 시간이 아니라, 생성된 결과물을 검증하는 데 드는 비용에 의해 결정됨을 강조합니다.
핵심 포인트
- 위임하기는 초기 생성 속도는 빠르나 사후 검토 및 수정 비용이 높음
- 주도하기는 작성 속도는 느리지만 결과물에 대한 신뢰 구축 비용이 저렴함
- 효율성을 결정하는 핵심 축은 작업의 크기가 아닌 '검증 가능성'임
- 결과물을 직접 작성하는 것보다 빨리 확인할 수 있을 때 위임하는 것이 유리함
AI 코딩 어시스턴트와 페어 프로그래밍(Pairing)을 하는 데에는 두 가지 방식이 있습니다. 지난 1년 동안 저는 어떤 방식이 실제로 더 빠른지는 말하지 못하고, 그저 어떤 방식이 더 빠르게 느껴지는지만 말씀드릴 수 있었습니다. 6월의 어느 오후, 저는 에이전트(Agent)가 약 1분 만에 300줄짜리 모듈을 다시 작성하도록 내버려 두었고, 그 후 두 시간 동안 그것을 뒷수습하는 데 시간을 보냈습니다. 그 격차의 크기, 그리고 제가 그 사실을 무시해 왔다는 점이 제가 이 글을 쓰기 위해 앉은 이유의 전부입니다.
'주도하기(Driving)'란 코드에 직접 손을 대고 있는 것을 의미합니다. 제가 대부분의 코드를 작성하며, 모델은 한 줄 또는 하나의 함수 단위로 작동합니다. 여기서는 코드 완성(Completion)을, 저기서는 "이 API의 시그니처(Signature)가 무엇인가요?"라고 묻거나, 이미 작성한 내용을 다시 읽어보는 식입니다. '위임하기(Delegating)'란 그 반대의 태도를 의미합니다. 전체 작업을 설명하고, 넘겨주고, 자리를 떠났다가, 제가 작성하지 않은 디프(Diff)를 검토하기 위해 돌아옵니다. 같은 어시스턴트, 같은 나, 같은 오후입니다. 결과물로 나오는 코드에 대해 완전히 다른 두 가지 관계를 맺는 것입니다.
요약 (TL;DR)
- 에이전트에게 전체 작업을 위임하는 것은 초반에는 빠르지만 후반에 비용이 발생하는 방식입니다. 디프(Diff)는 몇 초 만에 나타나지만, 작업은 검토(Review) 단계라는 하류로 이동할 뿐이며, 검토 단계야말로 모델의 잘못된 가정이 숨어 있는 바로 그 지점입니다. 주도하기는 그 반대입니다. 타이핑은 느리지만, 신뢰하는 데 드는 비용은 저렴합니다.
- 어떤 방식이 더 빠른지를 실제로 결정하는 축은 작업의 크기가 아닙니다. 그것은 바로 검증 가능성(Verifiability)입니다. 저는 결과를 직접 작성하는 것보다 더 빨리 확인할 수 있을 때 위임하고, 확인하는 비용이 작성하는 비용만큼 비쌀 때는 주도합니다. 나머지 내용은 제가 이 한 문장에 도달하기까지 겪은 느린 과정입니다.
여기서 "더 빠르다"는 것은 무엇을 의미하는가?
제가 1년 동안 속도에 관한 질문에 답할 수 없었던 이유는 두 가지 서로 다른 시계(Clock)를 혼동했기 때문입니다.
코드가 나타나는 동안 돌아가는 시계가 있습니다. 그 시계로 따지면 위임하기가 매번 압도적으로 승리합니다. 제가 의도를 담은 세 문장을 타이핑하면, 40초 후에 6개의 파일에 디프(Diff)가 적용됩니다. 주도하기는 이에 경쟁할 수 없으며, 제 신경계도 이를 알고 있습니다. 커다란 디프(Diff)가 스크롤되어 지나가는 것은, 신중하게 함수 하나를 작성하는 것과는 전혀 다른 방식으로 진전(Progress)이 느껴지기 때문입니다.
그리고 코드가 실제로 정확해지고 제가 그것을 신뢰할 때까지 돌아가는 또 다른 시계가 있습니다. 그 시계에는 diff(차이점)를 읽는 것, 모델이 조용히 가정해버린 것을 잡아내는 것, 3일 뒤에 통합 버그(integration bug)를 발견하는 것, 그리고 모델이 처음에 무시했던 제약 조건(constraint)을 다시 설명하는 과정이 모두 포함됩니다. 이 시계로 측정하면 순위가 종종 뒤바뀌는데, 두 시계가 보고되는 시점이 다르기 때문에 그 반전은 그 순간에는 보이지 않습니다. 위임하기(Delegating)는 지금 청구하고 나중에 비용을 부과합니다. 저는 그동안 첫 번째 청구서만 읽고 있었습니다.
따라서 "어느 쪽이 더 빠른가"라는 질문에 대한 정직한 답변은 두 번째 시계에 관한 질문이며, 이 두 번째 시계는 한 가지 요소에 의해 좌우됩니다. 바로 출력을 검증(verify)하는 데 시간이 얼마나 걸리는가 하는 점입니다.
두 가지 태세, 나란히 비교하기
이번 봄, 리뷰 부하를 분담할 팀이 없는 단독 iOS 코드베이스에서 수십 개의 작업을 의도적으로 추적하며 비교한 결과는 다음과 같습니다.
| 주도하기 (Driving) | 위임하기 (Delegating) | |
|---|---|---|
| 나의 역할 | 작성자(Author), 모델이 보조 | 검토자(Reviewer), 에이전트가 작성 |
| ... | ... | ... |
제가 가장 신경 쓰는 행은 마지막 행입니다. 주도하기(Driving)는 실패할 때 요란하게 실패합니다. 저는 느리게 흘러가는 매 분을 온몸으로 느끼기 때문에, 결코 모델을 과도하게 신뢰하지 않습니다. 반면 위임하기(Delegating)는 조용히 실패하며, 이러한 조용한 실패는 비용이 매우 많이 드는 종류입니다.
위임하기가 명확하게 승리하는 경우
검증 비용이 저렴한 작업의 경우, 위임하기는 단순히 더 빠를 뿐만 아니라 명백히 올바른 선택입니다. 저는 이런 식으로 작업을 주도하며 똑똑하다고 느끼는 것을 그만두었습니다.
새로운 독립적인 SwiftUI 설정 화면(settings screen)이 깔끔한 예시입니다. 정확성을 확인하기가 매우 쉽습니다. 실행해 보고, 눈으로 확인하고, 필드들을 탭하며 지나가면 됩니다. 만약 에이전트가 레이아웃을 잘못 잡았다면 5초 안에 알 수 있으며, 잘못되었을 때의 비용은 단순히 다시 렌더링하는 것일 뿐 파일이 손상되는 것이 아닙니다. 예시 입력값과 예상 출력값 테이블을 사용하여 파서(parser)의 뼈대(scaffolding)를 만드는 것도 또 다른 예시인데, 예시 자체가 검증 수단이 되기 때문입니다. 40개의 호출 지점(call sites)에 걸쳐 개념의 이름을 바꾸는 기계적인 리팩터링(refactor)도 마찬가지입니다. 이 경우 컴파일러가 저보다 훨씬 더 나은 검토자이며, 에이전트는 제가 평생 할 수 있는 것보다 훨씬 빠르게 타이핑을 수행합니다.
이 세 가지 패턴의 공통점은 동일합니다. 결과물이 자체적인 테스트를 포함하고 있다는 점입니다. 저는 결과를 보고 그것이 맞다는 것을 제가 직접 만드는 것보다 더 빠르게 알 수 있으므로, 생산을 넘기는 것은 순수한 이득입니다. 이것이 일반적인 코드베이스(codebase)의 대부분을 차지하는 영역이며, 그렇지 않은 척하며 모든 줄을 직접 손으로 쓰겠다고 고집하는 것은 시간 비용만 발생하는 장인 정신에 대한 향수(craft nostalgia)일 뿐입니다.
주도하기(Driving)가 조용히 승리하는 지점
반면, 코드가 비용이 많이 드는 부분이 아닌 다른 종류의 작업도 있습니다.
비용이 많이 드는 부분은 단 하나의 잘못된 가정을 알아차리는 것이며, 이는 당신이 이미 머릿속에 전체 모델을 담고 있을 때만 알아차릴 수 있습니다. 저의 Outbox 프로젝트가 저에게 이를 가르쳐준 사례입니다. 이는 실패한 전송을 재시도하는 작은 오프라인 우선(offline-first) 큐이며, 네트워크가 불안정하거나 플러시(flush) 도중 앱이 백그라운드로 전환될 때 얼마나 주의 깊게 동작해야 하는지에 대해 이전에 작성한 적이 있습니다. 제가 재시도 경로(retry path)에 대한 변경 사항을 위임했을 때, 에이전트는 제가 요청한 대로 백오프(backoff)에 지터(jitter)를 정확히 추가하는 깔끔하고 그럴듯하며 이름이 잘 지어진 코드를 생성했습니다. 또한 에이전트는 언급하지도 않고, 두 번의 재시도가 동일한 틱(tick)에 깨어나는 것을 막아주던 유일한 장치였던 락(lock) 외부로 상태 쓰기(state write)를 이동시켰습니다.
이것은 스타일을 확인하기 위해 디프(diff)를 읽는 것만으로는 잡아낼 수 없는 버그입니다. 몇 달 전 동시성 모델(concurrency model) 내부에서 일주일을 보냈고, 그것이 잘못되었다는 것을 말로 표현하기도 전에 그 잘못됨을 느꼈을 때만 잡아낼 수 있습니다. 해당 디프를 검토하는 것은 변경 사항을 직접 작성하는 것보다 더 많은 주의력을 요구했습니다. 왜냐하면 제대로 검토하기 위해서는 주도하기(driving)를 했다면 내내 유지되었을 정확한 정신적 모델(mental model)을 다시 구축해야 했기 때문입니다. 다른 앱의 iCloud 폴더에 한 줄을 추가하는 코드의 경우도 마찬가지였는데, 조율된 쓰기 경로(coordinated-write path)에서의 미묘한 실수는 누군가의 노트가 사라지기 전까지는 조용히 숨어 있습니다.
그러한 종류의 작업에서는 위임하기(delegating)가 시간을 절약해주지 않습니다. 그저 어려운 부분을 제가 강점을 가진 곳, 즉 머릿속에서 모델과 함께 코드를 작성하는 곳에서, 제가 약점을 가진 곳, 즉 타인의 그럴듯한 코드 속에 숨겨진 가정을 찾아내는 곳으로 옮길 뿐입니다.
그렇다면 실제 규칙은 무엇인가?
오랫동안 저는 기준축이 작업의 크기(task size)라고 생각했습니다. 작은 일은 직접 하고, 큰 일은 에이전트(agent)에게 맡기는 식이었죠. 그것은 완전히 거꾸로 된 생각이었으며, 이를 깨닫는 데 대가를 치러야 했습니다.
진정한 기준축은 검증 비용(verification cost)입니다. 결과를 직접 작성하는 것보다 더 빠르게 확인할 수 있을 때 위임하십시오. 확인하는 것이 작성하는 것만큼 어려울 때는 주도하십시오(drive). 작업의 크기는 거의 중요하지 않습니다. 300줄짜리 SwiftUI 뷰를 검증하는 것이 9줄짜리 락(lock) 변경 사항을 검증하는 것보다 더 저렴할 수 있습니다. 제가 어떤 태도를 선택할 때 실제로 하고 있는 일은, 이제 측정하지 않고도 느낄 수 있는 두 가지 숫자, 즉 생성 비용(cost to produce)과 확인 비용(cost to check)을 비교하는 것입니다. 확인 비용이 생성 비용보다 훨씬 적을 때는 작업을 넘겨주고 뒤돌아보지 않습니다. 확인 비용이 생성 비용에 근접할 때는 핸들을 직접 잡고 있습니다. 모델의 속도가 제가 안전하게 수용할 수 있는 이득을 전혀 주지 못하기 때문입니다.
이것이 바로 "프롬프팅(prompting)만 더 잘하면 모든 것을 위임할 수 있다"라는 유행하는 프레임워크가 핵심을 놓치고 있는 이유이기도 합니다. 병목 현상은 제가 작업을 얼마나 잘 명시할 수 있느냐의 문제가 아니었습니다. 저는 Outbox 변경 사항을 마지막 세부 사항까지 명시할 수 있지만, 리뷰는 여전히 비용이 많이 드는 부분입니다. 왜냐하면 빈틈없는 명세(spec)가 있다고 해서 그것을 미묘하게 위반한 사항을 찾아내는 비용이 저렴해지지는 않기 때문입니다. 한계는 지시(instruction)가 아니라 검증(verification)에 있습니다. 제가 모델과 작업할 때 프롬프트가 아닌 컨텍스트(context)를 중심에 두는 이유도 이와 같습니다. 레버리지(leverage)는 요청의 문구에 있는 것이 아니라, 모델과 제가 공통으로 보유할 수 있는 것에 있기 때문입니다.
내가 계속 빠지는 함정
규칙을 안다고 해서 제가 그 규칙으로부터 면역이 되는 것은 아닙니다. 깔끔한 법칙으로 글을 끝내기보다는 그 점에 대해 솔직해지고 싶습니다.
위임하기(delegating)로 향하는 끌림은 사실 속도에 관한 것이 아닙니다. 커다란 diff(차이점)가 마치 성취처럼 느껴지고, 주도하기(driving)는 노동처럼 느껴지기 때문입니다. 피곤한 오후라면, 설령 제가 직접 붙잡고 있어야 할 작업일지라도 성취감을 주는 쪽을 선택하게 됩니다. 저는 이런 방식으로 조용한 버그(quiet bug)를 한두 번 이상 배포한 적이 있습니다. 매번 빠른 diff가 주는 좋은 기분이, '내가 이것을 실제로 검증할 수 있는가'라는 지루한 질문을 대신하게 내버려 두었던 날들이었습니다. 제때 이를 잡아낼 때 나타나는 징후는, 제가 직접 작성할 수 없었을 diff를 승인하려 하거나, 틀렸을 때 비용이 크게 발생하는 시스템의 영역을 건드리고 있다는 점입니다. 그 특정한 조합이 나타나면 그것은 제가 멈추고, diff를 닫고, 직접 주도해야 한다는 신호입니다.
따라서 이 양방향의 선택은 하나의 질문으로 이루어진 습관이 되었습니다. 인계(handoff)를 받아들이기 전에, 저는 이것을 직접 만드는 것보다 더 저렴하게 검증할 수 있는지 스스로에게 묻습니다. 만약 그렇다면, 에이전트(agent)가 작업을 계속합니다. 만약 망설여진다면, 그 망설임 자체가 답이며, 저는 다시 운전대를 잡습니다.
계속해서 받는 몇 가지 질문들
이것은 그저 "보일러플레이트(boilerplate)에 AI를 사용하는 것" 아닌가요? 비슷하지만, 보일러플레이트는 대리 지표(proxy)이며, 결함이 있는(leaky) 지표입니다. 보안에 민감한 글루 코드(glue code)처럼 검증 비용이 비싼 보일러플레이트가 있는가 하면, 출력이 명확한 순수 함수(pure function)처럼 검증이 사소한 새로운 코드도 있습니다. 검증 비용(verification cost)이 진짜 변수입니다. "보일러플레이트"는 단지 그 비용이 보통 낮은 경우를 일컫는 말일 뿐입니다.
더 많이 위임하다 보면 시간이 흐를수록 어려운 부분을 처리하는 능력이 떨어지지 않을까요? 저의 솔직한 걱정이기도 합니다. 저의 대비책은, 검증하는 비용이 작성하는 비용만큼이나 큰 그 '어려운 부분들'이야말로 규칙이 저에게 계속 주도(driving)하라고 말하는 바로 그 영역이라는 점입니다. 이 규칙이 유효하다면, 저는 예리함이 핵심적인 역할을 하는 곳에서는 정확히 예리함을 유지하고, 그렇지 않은 곳에서만 업무를 넘기게 될 것입니다.
에이전트의 자율성(Autonomy)은 어디에 위치할까요? 동일한 축 위에서, 더 높은 이해관계가 걸려 있습니다. 에이전트가 더 자율적으로 작동할수록, 제가 검토하기 전에 쌓이는 코드는 더 많아지며, 저렴한 비용으로 확인할 수 없다면 검증 부채(Verification debt)도 더 많이 쌓이게 됩니다. 긴 자율적 실행(Autonomous runs)은 검증 가능한 작업에는 환상적이지만, 앞서 언급한 이유들로 인해 그렇지 않은 작업에는 조용히 위험할 수 있습니다.
제가 실제로 답을 얻고 싶은 열린 질문
제가 여전히 놓치고 있는 부분은 바로 이것이며, 매우 구체적입니다. 에이전트에게 실제 작업을 위임할 때, 변경 사항(Diff)을 신뢰하기 전에 실행하는 단 하나의 확인 절차는 무엇인가요? "주의 깊게 읽는다"와 같은 추상적인 것이 아니라, 구체적인 행동을 의미합니다. 즉, 가장 먼저 작성하는 테스트, 단언(Assert)하는 불변량(Invariant), 항상 열어보는 단 하나의 파일, 혹은 모델의 출력물에 대해 모델 자신에게 던지는 질문 같은 것 말입니다. 현재 저의 모든 접근 방식은 검증 비용을 낮추는 것에 달려 있으며, 위임을 잘하는 사람들은 제가 아직 훔치지 못한 검증 기술을 가지고 있을 것이라 확신합니다. 저는 제 방식을 방어하기보다 여러분의 방식을 배우고 싶습니다.
Simple Memo는 제가 만드는 1인 iOS 앱입니다. 한 줄을 입력하면 약 1초 후 이메일로 전송되어, 생각이 증발하기 전에 붙잡을 수 있습니다. 저는 이곳에 혼자 제품을 출시하며 배우는 것들에 대해 며칠마다 글을 올리며, 이 글도 그중 하나입니다. 제가 만들고 있는 것은 simplememofast.com에서 확인하실 수 있습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기