새로운 결제 제공업체를 추가하는 작업은 예전에는 일주일 정도가 걸렸습니다. 지난 결제 회사에서의 다섯 번의 통합 작업을 거치면서, 각 작업에 하루에서 이틀밖에 걸리지 않았고, 검토자들 역시 누락된 케이스를 더 적게 발견했습니다. 그런데 AI는 그 사이에 똑똑해지지 않았습니다. 저는 작업 순서를 바꿨고, 코드를 작성한 에이전트가 스스로 평가하는 것을 멈췄습니다.
이 글은 제3자 API(결제 시스템 여부와 관계없이)를 통합하는 엔지니어 중, AI 코딩 에이전트를 어느 정도까지 신뢰해야 할지 궁금해하는 분들을 위한 것입니다. 제가 사용하는 다섯 가지 단계, 무엇을 먼저 구축할지 결정하는 규칙, 그리고 AI 검토가 쓸모없게 만드는 편향성 하나에 대해 알려드리겠습니다.
일주일은 타이핑에 쓰이지 않았다
통합 작업 주간의 대부분은 코드를 작성하는 것이 아니라 읽고 확인하는 데 사용됩니다.
새로운 공급업체를 받는 식당을 생각해 보세요. 요리는 빠릅니다. 느린 부분은 공급업체의 카탈로그를 익히고, 배송이 늦거나 잘못되었을 때 무슨 일이 일어날지 결정하며, 아무도 같은 상자를 두 번 결제하지 않도록 하는 것입니다.
결제 통합도 마찬가지입니다. 제공업체의 문서를 읽고, 그들의 결제 상태를 자신의 시스템에 매핑하고, 발생할 수 있는 모든 문제(중복 웹훅, 알 수 없는 상태, 시간 초과)를 나열하며, 코드를 작성하고 테스트를 진행한 다음, 이 제공업체를 한 번도 본 적이 없는 검토자에게 이 모든 것을 설명해야 합니다.
계속 읽기 전에, 제가 가장 많은 시간을 들이는 단계가 무엇인지 추측해 보세요. 그것은 어떤 코드도 존재하기 전의 첫 번째 단계입니다.
다섯 가지 단계: 스크립트(script), 테스트용 데이터(fixtures), 빌드(build), 새로운 검토(fresh review), 확인(check)
규칙: 에이전트가 시작하기 전에 '정확하다'는 것이 무엇을 의미하는지 제가 결정하고, 다른 에이전트가 그 결과를 확인합니다.
[UPLOAD IMAGE HERE: /Users/taewoo/hustle/profiles/x-images/01-workflow-5-steps.png]
- 스크립트 (나). 내가 시나리오와 **목표(goals)**를 작성한다: 완성된 코드가 통과해야 하는 구체적인 검사 사항들이다. 각 목표는 코드를 호출하고 결과를 고정값(fixture)과 비교하거나, 제공자에게 샌드박스(sandbox)가 있는 경우 해당 샌드박스를 호출한다. 또한 위험도를 낮음, 중간, 높음으로 분류한다. 높은 위험도는 돈을 잃거나 두 배로 늘릴 수 있는 모든 것들이다: 상태 매핑(status mapping),멱등성 키(idempotency key)(반복된 요청이 두 번 적용되어도 안전하도록 만드는 ID), 그리고 폴링(polling)이 적절한 순간에 멈추는지 여부 등이 있다.
- 고정값 (Claude Code). **고정값(fixture)**은 제공자로부터 저장된 예시 응답이다. 만약 제공자가 샌드박스를 가지고 있다면, 에이전트가 이를 호출하여 실제 JSON을 저장한다. 나는 이 동일한 JSON을 사용하여 무언가를 통합하기 전에 테스트할 수 있다. 샌드박스가 없다면, 에이전트는 문서를 가져와서 그로부터 고정값을 만든다.
- 구축 (Claude Code). 높은 위험도의 로직부터 먼저 작성하며, 모두가 아직 주의를 기울이고 있을 때 진행한다. 익숙한 부분들은 기존의 규칙을 그대로 따른다. 모든 목표가 통과할 때까지 이 단계는 완료되지 않는다.
- 검토 (새로운 에이전트). 새로운 세션은 오직 변경 사항(diff)과 목표만 받는다. 더 자세한 내용은 아래에서 설명한다.
- 확인 (나). 하나의 스킬(skill)은 두 가지를 내보낸다: 코드에 있는 모든 중요한 결정의 맵, 그리고 내가 한 번에 API를 재테스트할 수 있도록 JSON 요청들이다. 나는 전체 변경 사항이 아닌 이 맵을 읽는다.
엔지니어들은 이러한 재사용 가능한 지침들을 **스킬(skills)**이라고 부른다: Claude Code가 특정 종류의 작업을 위해 로드하는 저장된 레시피 같은 것이다. 5단계의 위험도 맵이 그중 하나이다.
에이전트가 하기 전에 무서운 경우를 결정하기
가장 가치 있는 규칙은 어떤 코드보다 먼저 작성되는 것들이다: 예상치 못한 것이 도착했을 때 무슨 일이 일어나는지 말이다.
내가 가장 좋아하는 예시는 다음과 같다: 알 수 없는 상태가 pending이 되고, 알림(alert)이 발생하는 경우. 결제 제공자들은 당신에게 알려주지 않고 상태를 추가한다. 만약 코드가 추측한다면, 지불되지 않은 주문을 지불된 것으로 표시하거나, 지불된 주문을 실패한 것으로 표시할 수 있다. 이를 보류(parking) 상태로 두는 것은 아무것도 잃지 않으며, 알림은 같은 날 사람이 확인한다는 것을 의미한다.
저는 이것을 AI가 아니라 코드 리뷰를 통해 배웠습니다. 새로운 코드는 인식하지 못하는 상태에 도달했고, 오류를 발생시켰으며, 이를 포착하고 계속 진행했습니다. 그것은 무해해 보였습니다. 하지만 더 아래에서 공유 규칙이 있었습니다. 결제 상태가 처리 완료 시점까지 변경되지 않았다면 실패로 표시하라는 것입니다. 그래서 모든 알 수 없는 상태는 조용히 실패한 결제로 기록되었습니다. 실제로 성공했을 수도 있는 결제가 실패로 기록되었고, 성공률이 떨어졌으며, '실패'가 실제 원인을 숨겼기 때문에 아무도 이유를 알 수 없었습니다. 해결책은 추측을 멈추는 것이었습니다. 보류(pending) 상태로 두고 팀에 경고를 보내서 사람이 확인하도록 하는 것입니다.
AI 에이전트도 같은 종류의 선택을 할 것이며, 기꺼이 합리적으로 보이는 기본값을 만들어냅니다. '합리적으로 보인다'는 점이 바로 문제입니다. 왜냐하면 아무도 리뷰 과정에서 그것에 대해 의문을 제기하지 않기 때문입니다. 규칙을 스크립트에 먼저 작성하면 에이전트는 스스로 결정하는 대신 제가 내린 결정을 구현하게 됩니다.
엔지니어를 위한 내용: 스크립트 속의 위험 목록(risk list)은 어떤 모습인가
실제 제공업체는 아니지만, 형태를 간소화한 예시입니다:
## Risks
- HIGH 상태 매핑(status mapping): 알 수 없는 상태 → 보류(pending) + 경고, 결제되지 않음/실패
- HIGH 멱등성 키(idempotency key): 지급액당 하나의 키, 모든 재시도 시 재사용
...
각 목표는 피처(fixture)를 대상으로 하거나, 샌드박스(sandbox)가 있는 경우 그 대상을 가지고 실행되는 테스트입니다.
코드를 작성한 에이전트가 그것을 리뷰해서는 안 됩니다
같은 세션에게 자신의 작업물을 검토하도록 요청하면 대부분 스스로에게 동의합니다. 그것은 자신이 왜 각 선택을 했는지 기억하기 때문에, 그 이유를 따라 코드를 읽어 내려갑니다.
따라서 리뷰는 빌드에 대한 메모리가 없는 새로운 에이전트여야 합니다. 이 에이전트는 두 가지 것을 받습니다: 디프(diff)와 목표(goals). 그것은 코드가 목표에서 말하는 바를 수행하는지 처음부터 파악해야 합니다. 제 경험상, 이것은 간극을 잘 포착합니다. 왜냐하면 새로운 팀원이 코드를 읽는 방식과 같기 때문입니다.
에이전트가 여전히 잘못할 수 있는 부분
혼자 두면, 에이전트는 필요한 것보다 더 많은 것을 구축하는 경향이 있으며, '더 많은 것'은 당신이 가지고 있던 것을 조용히 삭제할 수 있습니다.
안전 검사를 건너뛴 오버라이드(The override that skipped the safety checks). 프로젝트에는 이미 프로바이더 어댑터(provider adapters)를 위한 공유 기본 클래스(shared base class)가 있었고, 필요한 메서드도 그 안에 존재했다. 그런데 에이전트(agent)는 아무런 실질적인 이유 없이 임의로 서브클래스를 만들고 해당 메서드를 오버라이드 했다. 이 오버라이드는 기본 클래스가 이전 인시던트들로부터 수집했던 검사들을 건너뛰었다. 새로운 테스트가 통과한 이유는 테스트들이 오직 새로운 프로바이더만을 다루었기 때문이다. 그래서 내 스크립트는 이제 '익숙한 부분은 컨벤션을 따른다(familiar parts follow conventions)'고 말하고, 위험 지도(risk map)는 모든 오버라이드를 나열한다.
뛰어넘고 표류하는 리스트들(Lists that jumped and drifted). 내 브라우저 확장 프로그램인 Tabby에서, 나는 에이전트에게 화면에 보이는 행들만 렌더링하도록 요청했다. 이렇게 하면 저장된 링크가 5,000개에 달해도 대시보드가 빠르게 유지된다. 실제로 작동했고, 대시보드는 약 4초에서 0.14초로 줄었다. 하지만 생성된 코드는 잘못된 스크롤 위치에서 시작했으며 마진(margin) 하나를 두 번 계산하여 행들이 서서히 표류했다. 이 문제는 종단 간 테스트(end-to-end tests)가 모두 잡아냈다.
이 두 가지 버그는 에이전트 스스로의 검사에서는 통과했다. 하지만 에이전트가 통제할 수 없는 것, 즉 역사를 아는 리뷰나 사전에 작성된 테스트에 의해 포착되었다.
내가 지켜야 할 비용과 규칙들(What it costs, and the rules I keep)
속도에는 선행적인 대가가 따른다. 스크립트와 목표 설정은 실제 시간이 필요하며, 이것들을 생략하는 것이 곧 돈을 잃는 방법이다.
- 금전 흐름 경로는 인간의 눈이 필요하다: 서명 확인(signature checks), 금액 변환(amount conversion), 아이뎀포턴시(idempotency) 및 모든 데이터베이스 마이그레이션(database migration).
- 프롬프트에 비밀 정보나 고객 데이터를 넣지 않는다. 샌드박스 키만 사용한다.
- 코드보다 목표가 먼저다. 내가 '정확하다'는 것이 무엇인지 말할 수 없다면, 에이전트도 할 수 없다.
- 에이전트가 과잉 엔지니어링(over-engineer)하게 두지 않는다. 존재하는 것을 재사용하고; 설명되지 않은 오버라이드는 리뷰 플래그다.
- 문서 기반의 테스트 케이스는 추측이다. 샌드박스 없이, 내게 남은 것은 이 추측을 실제 API와 대조하는 JSON 파일뿐이다.
대안은 모든 것을 수동으로 작성하는 것이다. 패턴이 반복되지 않는 일회성 통합(one-off integration)이거나 결과를 제대로 검토할 수 없을 때는 그 방법이 옳다. 하지만 몇 주마다 새로운 프로바이더가 등장하고 매번 동일한 위험이 적용되는 경우라면 잘못된 선택이다.
참고 사항: '약 일주일(about a week)'과 '1~2일(1–2 days)'은 제가 업무에서 5개의 통합 작업을 진행하며 경험한 전후 기간입니다. 티켓을 생성하는 것부터 PR(Pull Request)을 병합하기까지의 시간이죠. 이는 통제된 실험이 아니며, 각 제공업체마다 복잡도가 달랐습니다.
주요 시사점 (Takeaways)
- 에이전트가 코드를 작성하게 하기 전에 목표와 위험한 규칙(예: '알 수 없는 상태 → 보류 + 알림')을 먼저 작성하세요.
- 위험도가 높은 로직부터 구축하고, 익숙한 부분은 기존의 관례를 따르도록 하세요.
- 코드를 작성했던 세션 전체가 아닌, 변경된 내용(diff)과 목표만을 보고 검토하는 새로운 에이전트를 활용하세요.
결제 제공업체를 통합하는 방식에 차이가 있거나, 제가 규칙을 잘못 이해했다고 생각하시면 [email protected]으로 이메일을 보내주세요. 게시물을 수정하겠습니다.
원래 발행일: taewoo.me.