AI 믿고 맡겼다가 크게 데인 이야기
작성자 웅진씽크빅 플랫폼 엔지니어링팀 이은주 연구원, 개발전략/ Eunjoo Lee
AI 믿고 맡겼다가 크게 데인 이야기
작성자 웅진씽크빅 플랫폼 엔지니어링팀 이은주 연구원, 개발전략/ Eunjoo Lee

AI를 이용해 생성한 이미지
AI를 이용해 생성한 이미지# AI 믿고 맡겼다가 크게 데인 실패기
> 클라우드 비용 정산 기안을 매달 손으로 만들던 일을 자동화한 이야기입니다. AI에게 대부분의 코드를 맡겼지만, 가장 오래 붙잡고 가장 많이 실패한 건 “남이 만든 엑셀 한 장”이었습니다.
죄송합니다. 제목에 “실패기”라고 쓴 건 사실 어그로였습니다. 결론부터 말씀드리면 자동화는 성공했고, 매달 며칠씩 걸리던 비용 기안은 이제 클릭 몇 번으로 끝납니다.
그런데도 굳이 “실패기”라고 붙인 데는 이유가 있습니다. 돌아보니 이 프로젝트에서 제가 정작 많이 배운 건 매끄럽게 굴러간 부분이 아니라, 계속 넘어졌던 부분이었거든요. 성공한 결과는 표 한 줄로 요약되지만, 실패한 과정에는 할 이야기가 훨씬 많습니다. 그래서 이 글은 자랑이라기보다 삽질의 기록에 가깝고, 그 편이 누군가에겐 더 쓸모 있으리라 생각했습니다.
그중에서도 가장 의외였던 실패는 이겁니다. 코드는 AI에게 거의 다 맡겼는데, 딱 하나 ‘엑셀’ 앞에서만큼은 AI도, 저도 번번이 실패했습니다.어그로를 끈 점은 사과드리지만, 절반은 진심이었다는 뜻입니다.
1. 매월 초, 저는 숫자를 옮겨 적고 있었습니다
매월 초가 되면 비슷한 장면이 반복됐습니다.
Azure 청구서 PDF를 내려받아 엽니다. “합 계” 옆에 찍힌 숫자를 눈으로 확인하고, 상세내역서 엑셀을 열어 RI 금액과 환율을 찾습니다. 그리고 그 숫자들을, 수식이 잔뜩 걸린 기안용 엑셀에 손으로 옮겨 적습니다. 한 칸이라도 잘못 넣으면 배부 계산이 통째로 틀어지기 때문에, 옮겨 적은 뒤에는 다시 처음부터 검산을 합니다.
이게 Azure OpenAI 하나만이 아니었습니다. CDN, AWS.. 등등— 매월 여러 종류의 클라우드 비용 기안을 이렇게 만들어야 했습니다. 각 클라우드마다 청구서 양식이 다르고, 뽑아야 하는 숫자가 다르고, 기안 엑셀의 구조도 달랐습니다. 파일을 열고, 숫자를 찾고, 옮기고, 검산하고, 기안 본문을 쓰고… 다 끝내면 며칠이 훌쩍 지나가 있었습니다.
제일 견디기 힘들었던 건 시간이 아니라, “이 일에는 판단이 거의 없다”는 사실이었습니다. 청구서에서 숫자를 옮기는 데는 창의력이 필요 없습니다. 그런데도 실수는 늘 사람 몫이었죠. 숫자 하나를 잘못 보거나, 수식 참조 행을 하나 밀려 쓰면 그대로 오류가 됐으니까요.
그래서 만들기로 했습니다. “이 반복을, 매달 사람이 하지 않아도 되게.”
2. 무엇이 진짜 문제였는지부터 정리했습니다
곧바로 코드를 짜기 전에, 이 업무가 왜 이렇게 오래 걸리고 왜 이렇게 자주 틀리는지를 먼저 뜯어봤습니다.
- 입력 파일이 한 종류가 아니었습니다. Azure 하나만 해도 청구내역서 PDF, 상세내역서 엑셀(CSV), AOAI 토큰 사용량 엑셀 — 등의 여러 파일에서 각각 다른 숫자를 뽑아 조합해야 했습니다.
- 계산이 단순 합산이 아니었습니다. 서비스별로 토큰 사용량에 따라 비용을 배부하는 로직이 얽혀 있었고, 그 결과가 다시 기안 엑셀의 여러 시트에 흩어져 들어갔습니다.
- 기안 엑셀 자체가 지뢰밭이었습니다. 셀마다 수식이 서로를 참조하고, 스타일과 병합셀이 걸려 있고, 매월 데이터를 아래로 누적하는 구조였습니다. 한 행을 잘못 건드리면 아래 수식들이 연쇄적으로 깨졌습니다.
결국 문제는 두 가지로 요약됐습니다. 시간과 정확성. 그리고 이 둘은 사실 하나였습니다. 사람이 오래 붙잡고 있을수록 실수가 늘어나니까요.
3. 완전 자동화를 목표로 하지 않았습니다
의외로 들리실 수 있지만, 처음부터 “버튼 하나로 기안 제출까지”를 노리지 않았습니다.
기안을 최종 제출하는 건 사람의 결재 판단이 들어가는 영역입니다. 사내 그룹웨어(웅진넷)에 로그인하고, 결재선을 걸고, 첨부를 붙이는 일까지 무리하게 자동화하면 오히려 위험하다고 봤습니다. 그래서 자동화의 범위를 ”반복 계산과 숫자 옮겨 적기”로 명확히 그었습니다.
정리하면 이런 역할 분담입니다.
- 사람이 판단하는 것: 어떤 달을 정산할지, 최종 기안을 검토하고 제출할지
- 기계가 반복하는 것: PDF/엑셀에서 숫자 추출 → 배부 계산 → 기안 엑셀 자동 업데이트 → 기안 본문 생성
그리고 여러 종류의 클라우드를 모두 같은 틀 안에서 처리할 수 있도록 설계했습니다. 클라우드마다 세부 규칙은 다르지만, “파일에서 숫자를 뽑아 → 계산하고 → 엑셀에 넣는다”는 큰 흐름은 같으니까요.
4. 실제로 어떻게 만들었나
4–1. 전체 그림
시스템은 세 겹으로 나눴습니다.

AI를 이용해 생성한 이미지
[ 사용자 ] ──▶ Streamlit 웹 UI (파일 업로드, 단계 실행)
[ 처리 계층 ] ──▶ 파일 관리, 데이터 추출, 엑셀 업데이트, 검증, AI 비용 예측
[ 데이터 계층 ] ──▶ PDF 청구서, CSV 상세내역, 기안 엑셀
기능을 독립된 서비스(Unit)로 쪼갠 이유는 단순합니다. 클라우드를 하나씩 추가하고, 추출 규칙을 자주 고쳐야 했기 때문입니다. 추출 로직이 바뀌어도 엑셀 업데이트 서비스는 건드리지 않아도 되도록, 경계를 명확히 두고 싶었습니다.
사용자가 실제로 마주하는 건 Streamlit 화면 하나입니다. 클라우드 종류와 월을 고르고, 파일을 올리고, 단계별 버튼을 누르면 됩니다.
4–2. PDF와 엑셀에서 숫자 뽑아내기 — 규칙은 늘 지저분했습니다
가장 먼저 부딪힌 건 “청구서에서 숫자를 어떻게 안정적으로 뽑을 것인가”였습니다.
청구서 PDF는 사람 눈에는 명확하지만, 코드에게는 그냥 텍스트 덩어리입니다. 그래서 규칙은 늘 좀 지저분해졌습니다. 예를 들어 PDF에서 총청구액을 찾는 방법은 결국 “’합계’라는 글자 다음에 나오는 큰 숫자”를 잡는 것이었습니다. 우아하지 않지만, 실제 문서가 그렇게 생겼으니 어쩔 수 없었습니다.
상세내역서 엑셀에서 RI(예약 인스턴스) 금액을 뽑는 규칙도 마찬가지였습니다.
# RI 금액 찾기 (F열에서 "Azure OpenAI (RI)"만 정확히 매칭 - 종량제 제외)
for row in ws.iter_rows(min_row=10, max_row=30):
service_name = str(row[5].value or "") # F열
if "Azure OpenAI" in service_name and "(RI)" in service_name:
amount = row[15].value # P열 = 총청구액(VAT 포함)
if isinstance(amount, str):
amount = float(amount.replace(",", ""))
ri_amount += float(amount)
”(RI)”가 들어간 행만 정확히 골라야 했습니다. 종량제까지 같이 더해지면 금액이 부풀려지니까요. 환율은 더 노골적이었습니다. 상세내역서의 M9 셀에 늘 환율이 들어 있어서, 그냥 그 위치를 콕 집어 읽었습니다.
exchange_rate = ws.cell(9, 13).value # M9 셀
“이렇게 위치를 하드코딩해도 되나?” 싶지만, 매월 같은 양식으로 내려오는 문서라면 이게 가장 튼튼했습니다. 그리고 값이 없을 때를 대비해 기본값과 예외 처리를 겹겹이 걸어뒀습니다. 자동화에서 무서운 건 “틀린 값이 조용히 흘러가는 것”이라, 값이 이상하면 차라리 멈추게 만들었습니다.
4–3. 가장 오래 걸린 일 — 몇 년 묵은 기안 엑셀 해부하기
솔직히 말씀드리면, 이 프로젝트에서 제일 오래 걸린 건 추출도 계산도 아니었습니다. 그 기안 엑셀이 대체 어떤 원리로 굴러가는지 알아내는 일이었습니다. 몇 년 동안 여러 담당자의 손을 거치며 다듬어져 온 파일이라, 하나의 살아 있는 생물처럼 복잡했거든요.
그 엑셀은 이렇게 생겼습니다.
- 시트마다 역할이 달랐습니다. (서비스별 배부 기준, 기안첨부, 월별 집행 내역…)

- 셀들이 서로의 수식을 참조하고 있었습니다.

- 매월 데이터를 아래로 한 줄씩 쌓아가는 구조였습니다.

- 스타일과 병합셀이 곳곳에 걸려 있었습니다.

비용 기안 작성 스타일 및 병합셀들
그래서 제 저장소에는 지금도 analyze_*.py, check_*.py, verify_*.py 같은 파일이 수십 개 남아 있습니다. 전부 “이 엑셀이 도대체 어떻게 생겨먹었는지” 알아내려고 짠 정찰용 스크립트들입니다. 어떤 시트에 어떤 수식이 걸려 있는지, 병합셀은 어디인지, 스타일은 어떻게 이어지는지를 하나씩 찍어보며 지도를 그려 나갔습니다.
새 월 데이터를 넣는 건 단순히 “빈 칸에 값을 쓰는 것”이 아니었습니다.
첫째, 마지막 데이터 행을 찾고 중복을 체크해야 했습니다. 같은 달을 두 번 넣으면 안 되니까요.
둘째, 이전 행의 스타일을 새 행에 복사해야 했습니다. 값만 넣으면 서식이 깨져서, 사람이 만든 것처럼 보이지 않았습니다.
셋째, 수식의 행 번호를 자동으로 맞춰야 했습니다. 새 행이 20행이면 수식도 그 행 기준으로 다시 써야 했습니다.
# E열: 총청구액 =SUM(B{row}+C{row}+D{row})
ws.cell(row, 5).value = f"=SUM(B{row}+C{row}+D{row})"
# F열: 전월 대비 증감 =E{row}-E{row-1}
ws.cell(row, 6).value = f"=E{row}-E{row-1}"
특히 스타일 복사는 예상보다 훨씬 성가셨습니다. openpyxl에서 셀의 채우기(fill)를 복사하는 게 매번 얌전히 되지 않아서, 실패할 경우를 대비한 코드를 따로 둬야 했습니다. 제 코드에는 이런 주석이 그대로 남아 있습니다.
# fill 복사 (PatternFill은 copy가 제대로 작동하지 않을 수 있음)
try:
target_cell.fill = copy(source_cell.fill)
except Exception:
# copy 실패 시 새로 생성
병합셀도 마찬가지였습니다. 병합된 셀에 값을 그냥 쓰면 에러가 나서, “이 셀이 병합된 셀인지” 먼저 확인하고 안전하게 값을 넣는 함수를 따로 만들어야 했습니다.
이 과정을 지나며 확실히 배운 게 하나 있습니다.
4–4. AI는 엑셀을, 정말로, 잘 못합니다
요즘은 코드를 AI에게 맡기는 일이 흔합니다. 저도 그렇게 개발했습니다. 구체적으로는 AI IDE인 Kiro를 썼고, 그 안에서 Claude 모델(claude-opus-4.8)로 코드를 생성했습니다. 그런데 이 프로젝트에서 AI가 유독 힘을 못 쓴 영역이 딱 하나 있었습니다. 바로 앞서 그렇게 씨름했던 그 엑셀이었습니다.
일반적인 로직 — API를 만들거나, 파일을 파싱하거나, 데이터를 변환하는 코드는 AI가 정말 빠르고 정확하게 짜줬습니다. 그런데 엑셀 업데이트 로직에 들어가면 이야기가 완전히 달라졌습니다.
- “이 수식의 참조 행을 새 행 기준으로 바꿔줘”라고 하면, 얼핏 맞아 보이는데 한두 칸씩 어긋난 코드를 내놓았습니다.
- 스타일 복사, 병합셀 처리처럼 openpyxl의 특수한 동작이 얽히면, 그럴듯하지만 실제로는 서식이 깨지는 코드를 자신 있게 제안했습니다.
- 무엇보다, AI는 그 엑셀이 실제로 어떻게 생겼는지 볼 수 없었습니다. 시트 간의 참조 관계, 병합셀의 위치, 매월 데이터가 쌓이는 규칙 — 이건 파일을 직접 열어 하나하나 찍어봐야 알 수 있는 것들인데, AI에게는 그 맥락이 없었습니다.
왜 유독 엑셀이었을까요. 곰곰이 생각해보면 이유는 이렇습니다. 일반적인 소스 코드는 그 자체로 논리가 완결돼 있어서, 텍스트만 읽어도 무슨 일을 하는지 알 수 있습니다. 하지만 엑셀은 “코드”가 아니라 데이터·서식·수식·병합이 뒤섞인 하나의 상태(state)입니다. 같은 =E20-E19 수식이라도 그 셀이 어디에 있고 위 행에 무엇이 들어 있느냐에 따라 의미가 달라집니다. 그런데 이 상태는 파일을 직접 열어보지 않으면 알 수 없습니다. AI가 학습한 건 “openpyxl로 이렇게 쓴다”는 일반적인 패턴이지, “지금 이 파일의 20행이 병합돼 있고 파란 배경이다”라는 구체적 사실이 아니니까요. 게다가 openpyxl의 스타일 복사 같은 동작은 예외 케이스가 많고 문서화도 얕아서, AI가 가장 흔한 패턴을 자신 있게 내놓지만 실제로는 어긋나는 경우가 잦았습니다.
결국 엑셀 부분은 제가 정찰 스크립트로 구조를 완전히 파악한 뒤에야 진도가 나갔습니다. AI는 “무엇을 어떻게 바꿔야 하는지”를 제가 정확히 알려줬을 때만 도움이 됐습니다. 반대로 제가 구조를 모른 채 맡기면, AI도 똑같이 헤맸습니다.
한 줄로 요약하면 이렇습니다. AI는 코드는 잘 짜지만, 사람이 오래 매만져 온 엑셀의 속사정까지 대신 읽어주지는 못했습니다.
4–5. AI 도구가 UI 전체를 멈춰 세운 날
이 이야기는 조금 부끄럽지만, 남기는 게 맞다고 생각합니다.
NCP 연동을 추가하려고 AI 도구로 Streamlit UI를 수정하던 날이었습니다. 어느 순간 화면을 보니, 잘 돌던 5단계 워크플로우가 1·2단계까지만 보이는 겁니다. “왜 1, 2단계밖에 안 보이지?” 하고 들여다보니, UI 자체가 실행되지 않고 있었습니다.
에러는 이랬습니다.
File "simple_ui/app.py", line 276
elif response.status_code == 404:
^
SyntaxError: expected 'except' or 'finally' block
AI가 코드를 수정하는 과정에서 try-except 블록의 들여쓰기 구조를 잘못 건드렸고, elif가 try 블록 바깥으로 삐져나가면서 문법 자체가 깨진 상태였습니다. 더 곤란했던 건, 복원하려고 꺼낸 백업 파일에도 같은 오류가 들어 있었다는 점입니다.
그때 AI는 “간단히 한 줄 고치기”보다 “파일 전체를 다시 쓰기”를 자꾸 제안했습니다. 하지만 전체 재작성은 다른 곳을 또 망가뜨릴 위험이 있었죠. 결국 해결은 정공법이었습니다. python -m py_compile로 오류 위치를 정확히 짚고, try-except 구조를 눈으로 확인하면서, 276행 근처의 들여쓰기만 최소한으로 고쳤습니다.
이 사건에서 얻은 교훈을 저는 이렇게 정리했습니다.
- AI의 제안을 맹신하지 말 것. 그럴듯해 보여도 전체 구조를 깨는 수정일 수 있습니다.
- 수정 전에 백업하고, 작은 단위로 고치고, 즉시 검증할 것. 백업조차 손상될 수 있으니 검증은 필수입니다.
- 문제가 생기면 전체 구조부터 파악할 것. 급하게 부분만 건드리면 오히려 더 깊이 망가집니다.
5. 그래서, 무엇이 달라졌나
가장 크게 달라진 건 “시간”입니다.
예전에는 매월 여러 종류의 비용 기안을 손으로 만드느라 며칠씩 걸렸습니다. 파일을 열고, 숫자를 찾고, 옮겨 적고, 검산하고, 기안 본문을 쓰는 그 모든 반복이 지금은 파일을 올리고 단계 버튼을 누르는 몇 번의 클릭으로 끝납니다.
두 번째는 “정확성”입니다. 사람이 숫자를 옮기지 않으니 전사 오류가 사라졌고, 수식은 코드가 행 번호까지 맞춰 넣으니 “수식 참조가 한 칸 밀리는” 실수가 나오지 않습니다. 값이 이상하면 조용히 넘어가지 않고 멈추도록 검증을 겹겹이 걸어둔 것도 컸습니다.
세 번째는 “표준화”입니다. 예전에는 그날의 컨디션에 따라 기안의 모양이 미묘하게 달랐습니다. 지금은 네 종류 클라우드 모두 같은 파이프라인을 거치니, 결과물의 품질이 일정합니다.
6. 마치며 — 자동화의 핵심은 코드가 아니었습니다
이 프로젝트를 끝내고 가장 오래 남은 생각은 이것입니다.
자동화에서 제일 어려운 건 코드를 짜는 게 아니라, 업무를 정확히 이해하는 것이었습니다.
청구서에서 어떤 숫자를 뽑아야 하는지, 그 숫자가 기안 엑셀의 어느 칸으로 가야 하는지, 수식은 어떻게 얽혀 있는지 — 이 구조가 머릿속에 완전히 들어오고 나면, 코드는 오히려 그것을 받아 적는 일에 가까웠습니다. 실제로 저를 가장 오래 붙잡은 건 파이썬이 아니라, 그 한 장의 스프레드시트였습니다.
AI도 같은 자리에서 멈췄습니다. AI는 일반적인 코드는 놀랄 만큼 빠르게 짜줬지만, “이 엑셀이 실제로 어떻게 동작하는가”라는 맥락만큼은 대신 채워주지 못했습니다. 그 맥락을 넣는 건 결국 사람의 몫이었습니다. AI를 잘 쓴다는 건, 역설적으로 무엇을 시켜야 하는지 사람이 정확히 아는 것이라는 걸 이번에 확실히 배웠습니다.
지금 이 시스템은 클라우드 비용 기안을 처리하고 있습니다. 앞으로는 비용 데이터가 매월 자동으로 쌓이는 걸 활용해, 추이 분석과 비용 예측까지 확장해보려 합니다. 매달 사람이 숫자를 옮겨 적던 그 며칠이, 이제는 다른 일을 할 수 있는 시간이 됐다는 것 — 저에게는 그게 이 프로젝트의 가장 큰 결과였습니다.
읽어주셔서 감사합니다.
참고 문서
메타데이터
- post_id
- cd61a224ae98
- slug
- ai-믿고-맡겼다가-크게-데인-이야기-부제-ai-자동화-실패기-cd61a224ae98
- url
- https://medium.com/wjthinkbig/ai-%EB%AF%BF%EA%B3%A0-%EB%A7%A1%EA%B2%BC%EB%8B%A4%EA%B0%80-%ED%81%AC%EA%B2%8C-%EB%8D%B0%EC%9D%B8-%EC%9D%B4%EC%95%BC%EA%B8%B0-%EB%B6%80%EC%A0%9C-ai-%EC%9E%90%EB%8F%99%ED%99%94-%EC%8B%A4%ED%8C%A8%EA%B8%B0-cd61a224ae98
- canonical_url
- https://medium.com/wjthinkbig/ai-%EB%AF%BF%EA%B3%A0-%EB%A7%A1%EA%B2%BC%EB%8B%A4%EA%B0%80-%ED%81%AC%EA%B2%8C-%EB%8D%B0%EC%9D%B8-%EC%9D%B4%EC%95%BC%EA%B8%B0-%EB%B6%80%EC%A0%9C-ai-%EC%9E%90%EB%8F%99%ED%99%94-%EC%8B%A4%ED%8C%A8%EA%B8%B0-cd61a224ae98
- author_url
- https://medium.com/@eunjooleepaige
- status
- ok
- fetched_at
- 2026-08-03 05:51:28