
설계는 채팅 AI, 구현은 코딩 에이전트: xlflow로 Excel VBA 도구를 만든 이야기
요약
xlflow를 활용하여 Excel VBA 도구를 개발한 사례를 소개합니다. 채팅 AI로 상세 설계서와 HTML 목업을 먼저 확정함으로써, 코딩 에이전트가 저렴한 비용으로 정확하게 구현할 수 있는 워크플로우를 제시합니다.
핵심 포인트
- 설계 단계에서 채팅 AI와 대화하며 지시서와 목업을 확정하는 것이 핵심
- HTML 목업을 통해 코드 작성 전 UI/UX를 시각적으로 검증 가능
- 잘 설계된 지시서가 있다면 저렴한 모델로도 고품질 구현 가능
- xlflow를 통해 VBA 소스 코드를 현대적인 개발 환경처럼 관리
서론
Excel VBA 개발이라고 하면, VBE(Visual Basic Editor)를 열고, F5로 실행하며, 디버깅은 MsgBox나 Debug.Print를 사용하는 방식이 수십 년간 변하지 않았습니다. AI 에이전트에게 VBA 개발을 맡기려 하면, 이 "VBE에 갇힌 개발 스타일"이 큰 장벽이 됩니다.
xlflow는 이 장벽을 타파하는 도구입니다. Excel VBA의 소스 코드를 .bas / .cls / .frm 파일로 관리하며, CLI에서 매크로 실행·테스트·lint·포맷팅을 할 수 있게 해줍니다.
본 기사에서는 "답변 취합 도구"를 개발한 경험을 바탕으로 다음 두 가지를 소개합니다.
- 설계 단계: 채팅 AI(Claude)와 대화하며 개발 지시서와 목업(Mockup)을 확정해 나간 과정
- 구현 단계: 코딩 에이전트(OpenCode)가 xlflow로 구현할 때 겪었던 시행착오
이번 체제는 역할에 따라 완전히 분업화되어 있습니다.
| 역할 | 담당 | 수행 내용 |
|---|---|---|
| 발주자·리뷰·판단 | 인간 (나) | 요구사항 도출, 설계 판단, 결과물 검증 |
| ... |
"AI에게 만들게 한다"라고 하면 구현 단계에 주목하기 쉽지만, 실제로 시간을 들인 것은 설계 단계의 대화였습니다. 역으로 말하면, 설계만 잘 되어 있다면 구현은 저렴한 모델로 빠르게 끝낼 수 있다—실제로 구현에 사용된 API 이용료는 총 $1.94였습니다. 이것이 이번의 가장 큰 배움입니다.
만든 것
제 업무에는 "조회" 작업이 자주 있습니다.
- 답변 양식(Excel)을 관계 기관에 배포
- 수십~수백 개의 파일이 회신됨
- 답변 내용을 수작업으로 하나의 표에 붙여넣기
- 표기 불일치를 육안으로 수정
이 2~4번의 "보이지 않는 수작업"을 도구화하자는 것이 이번의 동기입니다. 취합 도구가 필요해서 만들었다, 정도의 온도감입니다. 다만, 한 가지 방침이 있습니다.
본 도구는 표기 불일치를 묵묵히 고치는 도구가 아니다.
무엇을 어떻게 고쳤는지, 무엇을 고치지 못했는지를 인간에게 보여주는 도구이다.
사양 (발췌)
- 답변 파일을 ReadOnly로 열어 지정된 셀 주소의 값을 취합 (비파괴적)
- 표기 불일치를 자동 검출 및 후보 제시 (강제 변환은 하지 않음. 이체자 등의 판단은 인간의 몫으로 남김)
- 4가지 공정(모으기→버리기→점검하기→쓰기)으로 구성. 두 번째인 "버리기"는 인간의 작업
- 설정은 모두 Excel 시트에 작성하며, VBA 코드에 규칙을 심어두지 않음
최종적으로 개발 지시서는 587행, 목업은 HTML(Excel 화면을 탭 전환 방식으로 재현한 것)이 되었습니다.
설계 단계: 구현 전에 지시서와 목업을 확정하기
왜 먼저 지시서와 목업인가
코딩 에이전트에게 모호한 요구사항을 전달하면, 빈칸을 "친절하게" 채워버립니다. 이번 도구의 경우, 이체자를 멋대로 통일하거나 불필요한 행을 자동으로 삭제하는 등, 이 도구의 사상과 정반대되는 구현이 돌아올 리스크가 있습니다.
그래서 구현 AI에게 전달하기 전에, 채팅 AI와 대화하며 다음 두 가지를 만들었습니다.
- 개발 지시서 (Markdown): 설계 원칙, 시트 구성, 모듈 구성, 코딩 규약
- 목업 (HTML): 각 시트의 모습을 탭 전환으로 재현한 것
목업을 먼저 만드는 효과는 상상 이상이었습니다. 코드가 한 줄도 없는 단계에서 "이 화면의 이 버튼은 필요 없어", "이 열은 방해돼"라는 대화를 할 수 있으므로, 수정 작업이 화면을 다시 그리는 수준에서 끝납니다.
대화로 설계가 바뀌었던 사례: 명세 행의 처리
설계 상담의 재미가 가장 잘 드러난 부분이 "명세 행이 있는 양식"의 처리입니다. 경위를 다이제스트로 적습니다.
초기 안 (v0.1): 양식 정의를 설정 시트의 표로 관리. 명세는 독자적인 기법인 B10↓를 사용하여 "여기서부터 아래로 반복"한다고 표현. 종료 지점은 "↓열이 전부 빈 행까지"로 자동 판정.
나의 위화감: 명세를 넣으면 아무래도 이해하기 어렵다. 게다가 종료 판정은 "빈 행이 2행 연속되면", "소계 행은 제외"... 등 맞을지 틀릴지 모르는 추측이 늘어난다.
전환점: "취합 후에 자동으로 필터를 걸어둔 상태로 만들어서, 불필요한 행을 사람이 필터링하거나 삭제할 수 있게 하는 편이 직관적이지 않을까?"라고 던졌더니, 설계가 단번에 단순해졌습니다.
취합은 "넉넉하게 가져온다". 필요 없는 것은 사람이 버린다.
- 종료 판정 → 불필요.
B10:B60
그리고 여유 있게 지정해서, 남는 빈 행은 나중에 버린다 - 입력 예시 시트 제외 설정 → 불필요합니다. 시트 이름 열로 필터링하여 지우면 됩니다.
- 고유 기호
↓
→ 불필요합니다. 엑셀 사용자가 이미 알고 있는 범위 표기법인B10:B60만으로 충분합니다.
'눈에 보이지 않는 수작업을 드러내는' 도구의 사상과 구현의 단순함이 같은 방향을 향한 순간이었습니다. 예측 가능성이 있는 추측을 코드에 넣지 않는 것은 AI 구현과도 잘 맞을 것입니다 (추측 로직은 테스트하기도 어렵습니다).
대화로 설계가 변화한 예: 시트 세대 관리
또 하나입니다. 처음에는 실행할 때마다 20_점검_20260724_142211와 같은 타임스탬프가 붙은 시트를 만드는 설계였으나, '3번 돌리면 시트가 12장이 되어 어떤 것이 최신인지 알 수 없게 된다'는 이유로 상시 보관용 4개 시트 + 덮어쓰기 방식으로 변경했습니다.
이때 문제가 되는 것은 '덮어쓰기로 인해 사람의 작업(삭제나 검토 판단)이 사라진다'는 점입니다. 이곳은 제가 '엑셀의
작성하게 하고 품질을 확인하는 '작게 시도하기' 순서도 지시서의 구현 순서에 포함했습니다.
실제로 사용한 것은 DeepSeek V4-Flash (OpenRouter 경유)이며, 모든 모듈의 구현 및 테스트 완료까지의 이용료는 $1.94였습니다.
무료 모델도 검토했지만, 통신 단절이 잦아 에이전트의 루프가 도중에 멈추기 때문에 단념했습니다. 에이전트 개발은 한 태스크당 수십 번씩 API를 왕복하므로, 단가의 저렴함보다 회선이 끊기지 않는 것이 더 중요합니다. 2달러 미만으로 안정성을 살 수 있다면, 솔직하게 지불하는 것이 더 빠르다는 것이 결론입니다.
한 가지 주의할 점은, VBA는 SWE-bench 등의 주요 벤치마크에서 거의 다뤄지지 않는 언어라는 것입니다. 벤치마크 수치가 VBA에서의 실력을 직접 보장하지 않으므로, 첫 번째 모듈에서의 품질 확인은 필수라고 생각합니다.
환경 구축
xlflow 설치
winget install HarumiWeb.Xlflow
겨우 이뿐입니다. 패스(Path)가 설정되었다면 버전을 확인합니다.
xlflow version
# => Version: 0.26.0
프로젝트 생성
cd C:\Users\root\Documents\Excel_Develop
xlflow new 照会回答集約ツール.xlsm
이렇게 하면 다음과 같은 구성이 생성됩니다.
├── xlflow.toml
├── build/
│ └── 照会回答集約ツール.xlsm
...
동작 확인
xlflow doctor --json
xlflow test --json
xlflow macros --json
개발 흐름
기본 CI 루프
xlflow에서의 개발은 다음 사이클을 반복합니다.
# 1. 소스 편집 (src/ 이하의 파일을 직접 편집)
# 2. lint + push (북에 반영)
xlflow lint --json
...
고속 루프 (세션 모드)
매번 Excel을 실행/종료하는 것이 느린 경우에는 세션 모드를 사용합니다.
xlflow session start
xlflow push --fast --session --no-save --json
xlflow test --session --json
...
구현한 모듈 목록
개발 지시서의 구현 순서에 따라 다음 순서로 구현했습니다. 공유 예정인 M_Normalize를 가장 먼저 완성시키고, 단체 테스트(Unit Test)로 품질을 확인한 뒤 다음으로 넘어가는 순서입니다.
| Step | 모듈 | 책임 |
|---|---|---|
| 1 | M_Normalize | 문자열 정규화 (전각→반각, 탁점 처리, 날짜 파싱 등) |
| 2 | M_Settings | 설정 시트 읽기·검증, 사전 CSV 입출력 |
| 3 | make_testdata | 더미 답변 파일 생성 매크로 |
| 4 | M_Ingest | 파일 열거, CSV 안전 오픈, 범위 전개 |
| 4 | M_Prepare | 셀 병합 해제, 제외 후보 판정, 오토 필터 |
| 4 | M_Transfer | 공정 간 전기(Transfer), 덮어쓰기 확인 |
| 5 | M_Review | 후보 열 생성, 색상 구분, 범례 쓰기 |
| 6 | M_Output | 확정 출력, ReadMe 생성 |
| 6 | M_Log | 에러 표시, 로그 출력 |
| 6 | M_Util | 범용 헬퍼 |
| 7 | F_Review | 리뷰용 UserForm (YAML→자동 생성) |
테스트 결과: 22/22 PASS (그중 2건은 TODO로 보류). M_Normalize부터 F_Review까지 총 11개 모듈을 구현하였고, v1.0.0로서 릴리스 격리까지 완료했습니다.
여기서부터는 관점이 바뀝니다. 구현을 담당한 AI 에이전트(OpenCode)가 개발 중에 남긴 지견입니다. xlflow로 Excel VBA 개발을 하는 AI(와 AI에게 지시하는 인간)를 위한 '함정 모음집'으로서 거의 그대로 게재합니다.
AI에서 AI로: xlflow VBA 개발의 함정 10선
1. VBA 파서(Parser)는 완벽하지 않다
xlflow의 lint는 Go 언어로 제작된 VBA 파서를 사용합니다. Excel VBA의 컴파일러와 완전히 동일하지는 않습니다.
특히 주의해야 할 구문:
' ❌ Mid$ 문 (좌변 대입)은 파서가 오류를 일으킴
Mid$(buf, i, 1) = ChrW(code - &HFF21 + 65)
' ✅ 문자열 연결로 대체
...
' ❌ ElseIf와 같은 줄에 대입을 쓰면 파서가 혼란을 느낌
ElseIf code = &HFF01 Then ch = "!"
' ✅ Then 뒤에서는 줄바꿈을 할 것
...
' ❌ ChrW(&H2010)은 파서가 받아들이지 못하는 경우가 있음
input = ChrW(&H2010) & "-"
' ✅ 10진수로 쓰거나, 변수를 통해 전달
...
2. Select Case 보다 If의 연속
xlflow의 파서는 항목 수가 많은 Select Case에서 에러가 발생할 수 있습니다. 63개 항목의 반각 가나(Half-width Kana) → 전각 가나(Full-width Kana) 변환 테이블에서는 Select Case 대신 If의 연속으로 작성하는 것이 더 안정적이었습니다.
' ❌ Select Case는 항목 수가 많으면 파서 에러 발생
Select Case code
Case &HFF61: HalfToFullKana = ChrW(&H3002)
...
3. Public Type은 사용할 수 없음 (회피책 있음)
표준 모듈의 Public Type (사용자 정의 타입)을 함수의 인자나 반환값으로 사용하면 VBA 컴파일 에러가 발생합니다.
컴파일 에러: 사용자 정의 형식이 정의되어 있지 않습니다.
회피책: Scripting.Dictionary로 대체한다.
' ❌ Public Type은 안 됨
Public Type TNormResult
Value As String
...
또한, 이번 개발 지시서에는 Public Type을 통한 인터페이스 정의(공유 모듈의 "계약")가 포함되어 있었기 때문에, 이 제약으로 인해 Dictionary 방식으로의 설계 변경을 발주 측에 확인한 후 구현했습니다. 지시서에 "임의로 보완하지 말고 확인 사항으로 반환할 것"이라고 적혀 있었던 점이 여기서 빛을 발했습니다.
4. VBA의 Dictionary 조작은 늪
VBA의 Scripting.Dictionary를 인자로 받은 함수 안에서 조작할 때, 몇 가지 함정이 있습니다.
문제: For Each entry In entryList는 Dictionary의 Keys를 열거한다 (Items가 아님).
' ❌ entry는 String (Key)이지 Object가 아님
For Each entry In entryList
Set entry = entryList(key) ' "개체가 필요합니다"
...
회피책: .Items 배열을 가져와 인덱스로 접근하거나, Keys를 열거한 후 개별적으로 접근한다.
' ✅ Keys를 열거한 후 개별적으로 접근
Dim key As Variant
For Each key In entryList.Keys()
...
단, Scripting.Dictionary.Keys()는 배열 (Variant)을 반환하므로, For Each로 돌릴 때의 타입에 주의가 필요합니다. 가장 안전한 방법은 중첩된 Dictionary가 아니라, 단일 레벨의 Dictionary로 데이터를 갖게 하는 것입니다.
5. 배열을 Object 타입의 인자로 전달할 수 없음
VBA에서는 Dim arr(0 To 5) As Variant와 같은 고정 길이 배열을 ByVal param As Object 함수에 전달할 수 없습니다.
컴파일 에러: 형식이 일치하지 않습니다.
회피책: Scripting.Dictionary로 래핑(Wrap)한다.
' ❌ 배열은 Object 인자로 전달할 수 없음
Dim manyFac(0 To 39) As Variant
Call CreateDetailFile("file.xlsx", manyFac) ' 에러
...
6. 탁점·반탁점의 반각 가나 변환은 직접 구현이 필요
ToFullWidthKana 구현에서 고생했습니다. 반각 가나의 탁점(゙ = U+FF9E)과 반탁점(゚ = U+FF9F)은 후속 문자와 결합하여 하나의 전각 문자로 만들어야 합니다.
입력: デ → 출력: テ + ゛(NG)→ 기대: デ(OK)
해결책: 탁점·반탁점을 검출하면, 직전 문자와 결합하여 탁음·반탁음의 전각 문자로 변환하는 함수를 별도로 구현합니다.
7. xlflow의 시트 조작은 세션 모드가 필요함
xlflow edit sheet add
나 xlflow edit cell
은 세션 중이 아니면 사용할 수 없습니다.
xlflow session start
xlflow edit sheet add --name "01_집약설정" --session --json
xlflow edit cell --sheet "01_집약설정" --address A5 --value "[북]`" --session --json
...
또한, xlflow push
로 소스 코드를 반영할 때, 세션 중의 변경 사항이 덮어씌워질 가능성이 있으므로, push와 edit는 배타적으로 사용해야 합니다.
8. 기존 북을 프로젝트화하기
기존의 .xlsm 파일이 있는 경우에는 xlflow new가 아니라 xlflow init을 사용합니다.
xlflow init 기존의북.xlsm
이렇게 하면 기존의 VBA 코드가 src/에 익스포트(Export)되어, xlflow 프로젝트로서 관리할 수 있게 됩니다. xlflow new로 만든 프로젝트에 나중에 기존 북을 머지(Merge)할 수는 없으므로, 처음에 결정해야 합니다.
9. 문자 코드 주의
개발 지시서에 "소스는 CP932(Shift_JIS)로 저장"이라고 되어 있더라도, xlflow가 다루는 소스 파일은 기본적으로 UTF-8입니다. xlflow가 push 시에 UTF-8→CP932 변환을 자동으로 수행하므로, 통상적으로는 의식할 필요가 없습니다.
단, CSV 파일 읽기(M_Ingest.ReadCsvAsText)에서는 CP932 / UTF-8(BOM 유무)를 적절히 판정해야 합니다. xlflow의 FSO(FileSystemObject)를 사용할 경우, OpenTextFile의 세 번째 인자로 문자 코드를 지정할 수 있습니다.
' UTF-8
Set stream = fso.OpenTextFile(filePath, 1, False, -2)
' 시스템 기본값(CP932)
...
10. xlflow의 lint에 걸리는 "허용해야 할 경고"와 "고쳐야 할 에러"
xlflow의 lint는 AI 에이전트가 VBA의 나쁜 관습에 빠지는 것을 방지하기 위해 설계되었습니다. 하지만 프로젝트의 성격상 허용해야 할 경고도 있습니다.
| 코드 | 의미 | 대응 |
|---|---|---|
| VB002 | Select 사용 금지 | 사용자에게 시트를 활성화하는 목적이라면 허용 |
| ... | VBA.Interaction.MsgBox라고 명시할 것 |
xlflow.toml에서 특정 규칙을 무효화할 수 있습니다.
[lint]
disabled_rules = ["VB007"] # MsgBox를 허용 (대화형 도구이므로)
요약
xlflow를 사용함으로써 Excel VBA 개발을 CLI 기반의 모던한 개발 플로우에 태울 수 있었습니다. AI 에이전트에게 있어 최대의 이점은 VBE를 열지 않고도 코드의 편집·테스트·실행을 완결할 수 있다는 점입니다.
한편, 이번 개발을 통해 느낀 점은, 결과물의 질을 결정한 것은 구현 페이즈가 아니라 설계 페이즈였다는 것입니다.
- 설계 페이즈 (인간 × 채팅 AI): 지시서 v0.1 → v0.3 대화. 모순 체크를 포함하여 개발 전체 시간의 대부분을 차지함
- 구현 페이즈 (DeepSeek V4-Flash × xlflow): 경과 시간은 약 10시간, API 이용료 $1.94. 다만 그 대부분은 인간(나)의 승인 대기 시간이었으며, 에이전트가 실제로 손을 움직인 순수 시간은 1시간 정도
이 '경과 시간 10시간 · 순수 시간 1시간'이라는 차이야말로 이번 진행 방식의 핵심입니다. 각 공정의 실행 전에 인간의 승인을 거치기 때문에 경과 시간은 늘어납니다. 하지만 그것은 설계 판단을 인간이 쥐고 있다는 증거이기도 합니다. 에이전트를 완전히 자율 주행(Autonomous)시키면 경과 시간은 단축되겠지만, 그만큼 '정신을 차려보니 설계 의도와 다른 구현이 되어 있었다'는 리스크를 떠안게 됩니다. 승인 게이트(Approval Gate)는 그 속도를 의도적으로 포기하는 대신, 결과물이 설계 원칙에서 벗어나지 않도록 보장하는 메커니즘입니다.
채팅 AI와의 설계 대화는 안을 내놓게 하고 인간이 판단하는 과정의 반복입니다. '넉넉하게 뽑고 사람이 버리는' 방식으로의 전환처럼, 인간 측의 현장 감각(Excel 필터로 버리는 것이 더 직관적임)이 설계를 한 단계 더 단순하게 만드는 장면이 여러 번 있었습니다. AI는 선택지와 정합성 체크(Consistency Check)를, 인간은 판단을 담당합니다. 이 분담이 원활하게 돌아가면, 구현 AI에게는 '판단의 여지가 없는 지시서'를 전달할 수 있습니다.
VBA의 언어 사양과 xlflow 파서(Parser) 사이에 간극이 있다는 점(Public Type 문제, Dictionary 조작, Select Case 문제 등)은 AI 에이전트가 사전에 알고 있어야 할 중요한 지식입니다. 이 글의 'ハマりどころ(주의해야 할 함정) 10선'이 다음에 xlflow로 VBA 개발을 할 AI(그리고 그 발주자)에게 도움이 되기를 바랍니다.
감사 인사
애초에 VBA를 CLI(Command Line Interface)에서 다룰 수 있다는 발상이 없었다면, 이 개발 플로우 자체도 성립하지 않았을 것입니다. .bas / .cls / .frm 파일을 파일로서 관리하고, 린트(Lint) · 테스트 · 실행을 커맨드라인에서 완결 짓는 것——VBE(Visual Basic Editor)에 갇혀 있던 세계를 열어준 xlflow 개발자분들께 진심으로 감사드립니다. AI 에이전트에게 VBA 개발을 맡길 수 있게 된 것은 바로 이 토대가 있었기에 가능했습니다. 겪었던 많은 시행착오 또한, 뒤집어 생각하면 '그 정도로 깊이 파고들어 자동화할 수 있는 도구가 있다'는 증거라고 생각합니다.
Discussion

AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기