내 학습허브 — 학습 전자책

학습위키를 책으로 묶었어요. 로드맵이 서문, 주차가 챕터예요. 지금 19꼭지.

내 학습허브

AI와 함께한 한 달의 학습 기록

서문 · 전체 지도

4주 로드맵

4주 뒤 내 손에 남는 것

  1. 매일 아침·밤 실제로 받아보는 텔레그램 브리핑 봇 — 일정·주가시황·가계부가 빠짐없이, 읽기 편한 형태로 오는 것
  2. 봇이 틀리거나 빠뜨렸을 때 스스로(혹은 내가) 바로 알아채는 최소한의 점검 습관
  3. 한 달 전엔 없던 걸 지금은 매일 쓰고 있다는 사실 — 발표·전자책 재료

시간이 부족해서 트랙2(글 검증 에이전트)는 이번 챌린지에서 뺌. 트랙1(브리핑봇) 하나에 집중.

1주차 · 기획 — 봇 점검 + PRD

이미 만들어둔 Hermes 봇을 뜯어보고, 뭘 고칠지 정한 뒤 손대기 시작하는 주.

  • 현재 cron 잡 목록 확인(/cron list) — 모닝/밤 브리핑이 실제로 매일 정시에 도는지 로그로 검증
  • 모닝 프롬프트에 개인일정·주가시황·준비문 세 가지가 빠짐없이 매번 들어가는지 확인 (cron은 매번 새 세션이라 프롬프트에 빠진 항목은 그대로 누락됨)
  • 밤 브리핑의 지출내역/가계부 항목 — 어떤 데이터 소스에서 가져오는지(수동 입력 vs 연동) 파악하고 안 되는 부분 리스트업
  • 실패했을 때 텔레그램으로 에러 알림이 오는 구조인지 점검 (없으면 이번 주에 추가)
  • 위 점검 결과로 “이번 챌린지에서 뭘 고칠지” 짧은 PRD 세우기 (예: 종목 리스트 고정, 카테고리 자동분류, 포맷 통일 — 아래 2주차 항목 확정)
  • 가장 만만한 것 하나(예: 에러 알림)는 이번 주에 바로 손대기 시작

2주차 · 본격 제작

1주차 PRD대로 고도화 — “돌아가게”에서 “쓸만하게”로.

  • 주가시황 파트: 관심 종목만 콕 집어 요약하도록 프롬프트에 종목 리스트 고정
  • 가계부 정리 파트: 지출을 카테고리별로 자동 분류해서 주간 합계까지 보여주도록 개선
  • 모닝/밤 메시지 포맷 통일 (이모지, 섹션 구분선 등으로 가독성 개선)
  • 여유가 되면: 밤 브리핑 가계부 요약본이 실제 지출 데이터와 맞는지 스스로 한 번 더 확인하는 문장 한 줄 추가 (본격 검증 에이전트는 아니고, 간단한 자체 점검 정도)

Hermes 공식 가이드의 cron 기반 구조와 현재 방식이 동일해서 큰 틀을 바꿀 필요는 없어 보임.

3주차 · 완성·다듬기

  • 1주일간 실제로 매일 받아보면서 어색한 부분·틀린 부분 메모하고 바로 수정
  • 빠지는 날 없이, 형식 안 깨지고 며칠 연속 잘 도는지 확인
  • 그동안 나온 사소한 버그·오탈자 정리해서 마무리

4주차 · 회고·발표

  • 4주 전(자동화 하나 없던 나) vs 지금(매일 브리핑 받는 나) 비교 회고
  • 브리핑 봇 데모 준비 (실제로 받은 메시지 캡처)
  • 발표자료·강사 제출용 요약 정리
  • 전자책용 정리 마무리

참고한 실제 사례 및 방법론

Chapter

1주차

개념

AI 결과물, 왜 틀리고 왜 한 번에 못 잡을까 — 검수 하네스 개념

2026-07-23

이 개념페이지는 왜 만들었나

부기부기님이 궁금해하신 질문 두 가지에서 출발해요.

  1. 왜 클로드로 글을 쓸 때 계속 잘못된 정보가 들어가서 오류가 생길까?
  2. 왜 그 실수를 클로드가 한 번에 못 찾아낼까?

이 두 질문에 대한 답을 이 사례글에 나온 개념들로 정리해봤어요. 이 글은 “AI 결과물을 스스로 검수하는 하네스 만들기”라는 4주 스터디 소개글이에요. 실행 방법이나 로드맵은 다루지 않고, 개념만 정리했어요.

💡 원문은 실습 방법을 알려주는 사례글이 아니라, 4주짜리 스터디를 모집하는 소개 페이지예요. 그래서 “왜 이렇게 설계했는가”라는 개념·철학 위주로 쓰여 있고, 실습 단계는 없어요. 이 페이지도 그래서 실행 로드맵 없이 개념만 정리했어요.


0. 그 전에 — AI는 왜 애초에 “틀리는 줄 모르고” 대답할까

이 뒤에 나오는 개념들을 이해하려면, 먼저 AI가 원래 어떻게 동작하는지부터 아는 게 도움이 돼요. (원문엔 없지만, 왕초보를 위해 순이가 보충하는 배경 설명이에요. 💡 표시할게요.)

💡 클로드 같은 AI(LLM, 대형 언어 모델)는 “사실을 찾아서” 답하는 게 아니라, “다음에 올 말로 가장 그럴듯한 단어”를 이어 붙이는 방식으로 답을 만들어요. 비유하자면, 엄청나게 많은 책을 읽은 사람이 “음, 이런 질문엔 보통 이런 식으로 답하더라” 하고 말투와 흐름만 보고 자연스럽게 이어 쓰는 것과 비슷해요.

그래서 이런 일이 생겨요:

  • 진짜 판례를 찾아본 게 아니라, “이런 사건엔 보통 이런 판례가 인용되더라”는 패턴으로 그럴듯하게 판례 번호를 만들어낼 수 있어요.
  • AI 입장에서는 자기가 틀렸다는 느낌 자체가 없어요. “말이 되게 이어 쓴 것”과 “사실인 것”을 AI 스스로는 구분하지 못해요. 그래서 항상 자신 있는 말투로 답해요 (거짓말도 당당하게).

→ 이게 바로 부기부기님 질문 두 개의 근본 원인이에요. AI는 “확인 후 말하기”가 아니라 “그럴듯하게 잇기”로 작동하기 때문에 ①잘못된 정보가 섞이고, ②본인 스스로는 그게 틀렸는지 알 방법이 없어서 한 번에 못 잡아요. 아래 개념들은 전부 이 근본적인 한계를 사람이 바깥에서 보완하는 방법들이에요.


1. 할루시네이션 — “거짓말”보다 훨씬 넓은 개념

우리는 보통 할루시네이션(=환각)을 “AI가 없는 사실을 지어내는 것”으로만 생각해요. 그런데 이 글은 그걸 3가지로 나눠요:

유형쉬운 뜻예시
거짓/오출처사실과 다른 주장, 없는 인용, 틀린 숫자존재하지 않는 판례를 인용함
누락있어야 할 근거·내용을 안 가져옴중요한 사실관계 하나를 빠뜨림
부실일을 대충 함, 분석이 얕음검토는 했지만 깊이 파고들지 않음

→ 부기부기님 질문 1에 대한 답: “잘못된 정보를 입력”하는 문제는 이 세 유형 중 어디에나 걸칠 수 있어요. 특히 글 작성에서는 “누락”(빠뜨림)과 “부실”(대충 검토)도 넓은 의미로는 할루시네이션에 들어간다는 게 이 글의 핵심 관점이에요. 그래서 “숫자·인용이 진짜인가”뿐 아니라 “빠진 게 없는가”, “충분히 깊이 봤는가”까지 다 점검 대상이 돼요.

2. 왜 처음부터 잘못될까 — “전단(front-end)“이 절반을 결정한다

이 글의 핵심 메시지는 이거예요: “결과물을 다 만든 뒤에 고치는 게 아니라, AI에게 일을 시키기 전(입력 단계)에서 품질의 절반 이상이 정해진다.”

즉, 우리가 클로드한테 “이거 검토해서 글 써줘”라고 두세 줄로만 던지면, 클로드는 뭘 검토해야 하는지 정확히 모른 채로 시작해요. 그러면:

  • 관련 없는 자료까지 끌고 오거나
  • 출처가 불명확한 자료를 그대로 쓰거나
  • 핀트가 살짝 어긋난 결과물을 만들게 돼요.

이 글은 이걸 “의도 정렬” (내가 원하는 바를 AI가 완전히 이해할 때까지 대화로 맥락을 채우는 단계)로 해결하자고 해요. 두세 줄 지시 대신, AI가 되물어보게 하거나 충분히 설명해서 “무엇을 검토할지” 자체를 먼저 문서로 못 박는 거예요.

→ 부기부기님 질문 1에 대한 답 (이어서): 글 작성 오류가 반복된다면, “글 써줘” 지시 자체가 짧고 맥락이 부족해서일 가능성이 커요. 클로드가 사건 배경·쟁점·참고할 자료 범위를 처음부터 명확히 알고 시작하도록 만드는 것 (의도 정렬)이 첫 번째 해법이에요.

3. 왜 한 번에 못 잡을까 — “같은 AI의 자기 검토”의 한계

이 글이 지적하는 문제: “단일 AI의 ‘다시 검토해줘’는 같은 오류를 반복해서 놓친다.” 왜냐하면 같은 AI가 같은 맥락·같은 사고방식으로 다시 보면, 처음에 놓친 것을 두 번째에도 비슷하게 놓치기 쉽기 때문이에요. (자기가 쓴 글을 자기가 교정 볼 때 오타를 못 보는 것과 비슷해요.)

이 글이 제안하는 해법 3가지:

방법쉬운 뜻
결정론적 검증 게이트”이 조건을 만족하는가”를 사람 판단이 아니라 프로그램(숫자 대조, 규칙 검사)으로 딱 잘라 확인하는 것. 애매하지 않고 기계적으로 통과/실패가 나옴
Cross-Check (독립 모델 상호 검증)클로드가 쓴 걸 클로드한테 다시 보라고 하지 않고, 다른 AI(예: Codex, Gemini)한테 보여줘서 확인받는 것. 다른 AI는 같은 맹점을 안 갖고 있어서 더 잘 잡아냄
멀티에이전트 반대신문여러 AI가 각자 역할(의심하는 역할 · 방어하는 역할 · 결론 내리는 역할)을 맡아 서로 캐묻고 반박하게 만들어서, “그럴듯하지만 틀린” 결과물을 걸러내는 방식

→ 부기부기님 질문 2에 대한 답: 클로드가 실수를 한 번에 못 찾는 이유는, 같은 AI가 같은 맥락으로 스스로를 다시 검토하기 때문이에요. 이 글의 해법은 “다른 AI에게 검증을 맡기기”(Cross-Check)와 “기계적으로 딱 잘라 확인하는 규칙”(검증 게이트)을 같이 쓰는 거예요. 사람이 매번 눈으로 다 보지 않아도, 이 둘을 조합하면 놓치는 게 줄어요.

4. 이 모든 걸 묶는 이름 — “검수 하네스”

하네스는 원래 “장비를 몸에 고정하는 벨트/장치”라는 뜻인데, 여기서는 AI가 일하는 전체 작업 환경(규칙+도구+반복 절차)을 하나로 묶은 시스템을 뜻해요. “검수 하네스”는 위에서 나온 것들 — 의도 정렬, 검증 게이트, Cross-Check, 반대신문 — 을 한 세트로 묶어서, 매번 새로 설명하지 않아도 반복 재사용할 수 있게 만든 것이에요.

이 글에 나온 관련 용어들도 짧게 정리하면:

용어쉬운 뜻
Skill반복되는 작업을 AI에게 시킬 때 쓰는 재사용 가능한 지시서 (이 학습허브의 .claude/skills/ 폴더에 있는 것들도 Skill이에요)
Eval결과물이 잘 됐는지 판단하는 채점 기준표(루브릭). “근거가 있는가”, “숫자가 맞는가” 같은 항목들
Loop (Loop Engineering)한 번 만들고 끝내는 게 아니라, 실행 → 평가 → 개선을 반복해서 갈수록 더 좋아지게 만드는 것
PRD / Goal / Recovery무엇을·왜 검수하는지(PRD), 언제 끝난 건지(Goal), 실패하면 어떻게 되돌릴지(Recovery)를 각각 문서로 정리한 것

5. 왕초보를 위한 비유 정리 (순이가 보충)

💡 지금까지 나온 개념을, 사람에 비유해서 한 번 더 정리해볼게요.

  • AI 혼자 글 쓰고 AI 혼자 검토 = 신입 직원이 글을 쓰고, 자기가 쓴 걸 자기가 또 읽어보며 “음, 괜찮은 것 같은데?” 하는 상황. 자기가 놓친 실수는 자기가 다시 봐도 잘 안 보여요.
  • 의도 정렬 = 글을 시키기 전에 사건 배경·쟁점·참고자료를 꼼꼼히 브리핑해주는 것. 브리핑을 안 받고 “이거 써줘”만 들으면 신입도 헤매요.
  • 검증 게이트 = “이 글에 인용된 판례 번호가 실제로 존재하는가?”처럼 기계적으로 셀 수 있는 것을 프로그램이 자동으로 체크리스트처럼 훑는 것. 사람의 “느낌”이 아니라 “있다/없다”로 딱 잘라 확인.
  • Cross-Check = 신입이 쓴 글을 선배 동료(=다른 AI) 가 처음 보는 눈으로 다시 검토하는 것. 같은 사람이 아니라 다른 사람이 봐야 놓친 게 더 잘 보여요.
  • 멀티에이전트 반대신문 = 글을 놓고 **“이거 진짜 맞아?” 캐묻는 사람(SKEPTIC)**과 **“아니, 이건 이래서 맞아” 방어하는 사람(DEFENDER)**이 토론하고, **의장(CHAIR)**이 최종 판단하는 모의 재판 같은 절차. 한 사람 의견보다 여러 관점이 부딪히면 허점이 더 잘 드러나요.

지금 부기부기님이 바로 해볼 수 있는 것 (간단한 응용 하나만)

원문처럼 거창한 4주 시스템을 만들지 않아도, 오늘 당장 흉내 낼 수 있는 것 한 가지만 짚어드릴게요:

클로드가 글 초안을 써주면, 그 결과물을 다른 AI(예: ChatGPT나 Gemini) 에 붙여넣고 “이 글에서 사실과 다르거나 근거 없는 부분, 빠진 부분이 있는지 검토해줘”라고 한 번 더 물어보기.

이게 바로 위에서 배운 Cross-Check를 가장 간단하게 실천하는 방법이에요. 클로드한테 “다시 검토해줘”만 반복하는 것보다, 다른 AI에게 한 번 더 보여주는 게 놓친 걸 잡을 확률이 높아요.


개념 정리표

단어쉬운 뜻어디서 나왔나
할루시네이션AI가 그럴듯하게 틀리는 것. 거짓/오출처·누락·부실 3종류로 나뉨1번
의도 정렬AI에게 짧게 시키지 않고, 무엇을 원하는지 충분히 맥락을 채워주는 단계2번
검증 게이트사람 판단 대신 프로그램이 기계적으로 통과·실패를 가리는 자동 점검3번
Cross-Check결과물을 만든 AI가 아니라 다른 AI에게 다시 확인받는 것3번
멀티에이전트 반대신문여러 AI가 역할을 나눠 서로 캐묻고 반박하며 오류를 걸러내는 방식3번
검수 하네스위 개념들을 한 세트로 묶어 반복 재사용하는 작업 시스템 전체4번
Skill / Eval / Loop재사용 지시서 / 채점 기준표 / 실행-평가-개선 반복4번

한 줄 요약

클로드가 글 작성에서 실수하는 이유는 대부분 “시작할 때 맥락을 충분히 안 줘서” 생기고, 그 실수를 못 잡는 이유는 “같은 AI가 같은 관점으로 스스로 다시 봐서” 예요. 해법은 ① 일 시키기 전에 맥락을 충분히 채우기(의도 정렬), ② 기계적으로 딱 잘라 확인하는 규칙 만들기(검증 게이트), ③ 다른 AI에게 검증을 맡기기(Cross-Check) — 이 세 가지예요.

사례

스터디 소개글 하나로, 개념페이지 하나 뽑아내기

2026-07-23

한 줄 요약

관심 있던 스터디 소개글 링크 하나를 던졌더니, 학습메이트가 “실행 계획 말고 개념만” 정리해줬어요. 그 과정에서 자바스크립트 사이트라 못 읽히는 문제도 만났고, 어떻게 뚫었는지까지 정리해봤어요.

누구에게 도움이 될까

  • 관심 있는 글(스터디 소개, 블로그, 사례글 등)을 봤는데 너무 어려워서 이해가 안 될 때
  • 클로드가 웹페이지 링크를 줬는데 “내용이 안 보인다”고 할 때

Before — 이런 상황이었어요

클로드로 글을 쓸 때 “왜 계속 잘못된 정보가 들어가고, 왜 그 실수를 한 번에 못 찾을까”가 궁금했어요. 그러다 우연히 “AI 결과물을 스스로 검수하는 Claude Code·Codex 하네스”라는 스터디 소개 링크를 발견했는데, 내용이 너무 전문적이라 혼자 읽어서는 이해가 안 됐어요.

어떻게 했나 — 실제 쓴 프롬프트

이렇게 요청했어요 (그대로 복사해 써도 돼요):

[사례글 주소나 내용] 이 사례 따라 하면서 개념도 같이 배우고 싶어.
이건 개념을 이해하려는 거야. 실행 로드맵이나 상세 계획까지 짜지 말고,
사례에 나온 개념을 이해하는 데만 집중해서 개념페이지로 정리해줘.

막힘 1: 순이가 링크를 열어봤더니, 스터디 모집 페이지 제목만 읽히고 본문이 안 읽혔어요. 알고 보니 그 사이트가 자바스크립트로 화면을 나중에 채워 넣는 방식이라, 자동으로 가져오는 방법으로는 빈 껍데기만 보였던 거예요.

해결: 순이가 정직하게 “지어내지 않고” 알려줬어요 — “본문을 복사해서 붙여주시거나, 화면을 캡처해서 보여주세요.” 그래서 페이지 본문을 통째로 복사해서 붙여넣어 줬더니, 그때부터 제대로 개념 정리가 시작됐어요.

막힘 2: 처음 나온 정리는 그 스터디 소개글의 전문 용어(할루시네이션, 검증 게이트, Cross-Check 등)를 그대로 옮긴 수준이라, 왕초보인 저한테는 여전히 어려웠어요.

해결: “왕초보가 독자라고 생각하고 더 쉽게 풀어달라”고 한 번 더 요청했더니, “AI는 왜 애초에 틀리는 줄 모르고 대답하는가” 같은 배경 설명과, 일상 업무에 빗댄 비유(“신입 직원이 자기 글을 자기가 교정 보는 것과 같다” 등)를 추가로 붙여줬어요.

After — 결과

원문은 4주짜리 고급 스터디 소개라 실습 없이는 이해하기 어려웠는데, 개념만 뽑아서 제 상황(글 작성 오류)에 맞춰 다시 풀어보니 훨씬 잘 이해됐어요. 특히 “AI는 확신에 차서 틀린다”는 배경 설명 하나가, 왜 검증 단계가 필요한지를 납득시켜줬어요.

배운 것 / 재사용 자산

  • 자바스크립트 사이트 우회 순서: ① 브라우저 도구로 직접 열어보기 → ② 안 되면 본문 복사해서 붙여넣기 → ③ 그래도 안 되면 화면 캡처. (이제 학습메이트 설정 파일에도 반영해뒀어요)
  • 재사용 프롬프트: “실행 계획 말고 개념만 정리해줘” + “왕초보가 독자라고 생각하고 더 쉽게” — 이 두 문장 조합이 어려운 글을 소화 가능한 크기로 쪼개는 데 잘 먹혔어요.
  • 오늘 배운 핵심 개념: 할루시네이션(거짓/누락/부실 3종), 의도 정렬, 검증 게이트, Cross-Check — 자세한 설명은 개념페이지에 정리해뒀어요.
사례

4일차 — 일부러 망가뜨리고 되돌리기, 그리고 "저장할 게 없다"는 것의 의미

2026-07-23

한 줄 요약

랜딩페이지 첫 문장을 일부러 망가뜨렸다가 되돌리는 연습을 해봤어요. 그 과정에서 “커밋은 항상 뭔가를 남기는 게 아니라, 진짜 바뀐 게 있을 때만 의미가 있다”는 걸 몸으로 이해했어요.

누구에게 도움이 될까

  • “되돌리기”가 실제로 어떻게 되는지 궁금한 초보자
  • 커밋을 아무 때나 눌러야 하는 줄 알고 헷갈리는 분

Before — 이런 상황이었어요

전날 사례글 2편(모닝/나이트 브리핑 봇)을 커밋까지 해둔 상태였어요. 오늘은 실전 전에 “되돌리기”를 안전하게 연습해보고 싶었어요.

어떻게 했나 — 실제 대화

1단계 — 일부러 망가뜨리기

내 랜딩 소개글 첫 문장을 아무렇게나 바꿔서 살짝 망가뜨려줘.
바뀐 걸 로컬로 보여줘. (이건 되돌리기 연습이야, 걱정 마)

랜딩페이지 첫 문장이 “안녕하세요, 저는 부기부기입니다.”에서 엉뚱한 문장으로 바뀌었고, 로컬 미리보기에서 실제로 망가진 걸 확인했어요.

2단계 — 되돌리기

방금 바꾼 걸 취소하고, 마지막으로 커밋한 상태로 되돌려줘.
그리고 원래대로 돌아왔는지 로컬로 보여줘.

터미널에서 이 명령 한 줄로 처리됐어요:

git checkout -- src/content/landing/landing.md

막힘 없이 한 번에 해결됐어요. 로컬 미리보기를 다시 확인하니 원래 문장으로 정확히 돌아와 있었어요.

3단계 — 여기서 예상 못 한 부분

되돌리기가 끝난 뒤 “오늘 여기까지 잘 된 것 같아. 커밋으로 저장해줘”라고 요청했는데, git status를 확인해보니 저장할 게 아예 없었어요. 왜냐하면 “망가뜨렸다가 다시 원래대로 되돌린 것”은 결과적으로 파일이 하나도 안 바뀐 것과 똑같은 상태였기 때문이에요.

After — 결과

여기서 중요한 걸 하나 배웠어요: 커밋은 “작업을 했다”를 기록하는 게 아니라, “파일 내용이 실제로 달라졌다”를 기록하는 것이더라고요. 뭔가 하긴 했지만 결과적으로 원상복구됐다면, git 입장에서는 “변경 없음”이라 커밋할 대상 자체가 없어요.

배운 것 / 재사용 자산

  • 되돌리기 명령: git checkout -- {파일 경로} — 아직 커밋 안 한 변경사항을 버리고, 마지막 커밋 시점 내용으로 되돌림. (GitHub Desktop에서는 “Discard changes” 버튼과 같은 동작)
  • 커밋 전 확인 습관: git status로 “지금 저장할 게 있는지” 먼저 확인하는 습관이 생겼어요. 목록에 아무것도 안 뜨면 저장할 이유가 없는 거예요.
  • 오늘 새로 이해한 것: 커밋은 “작업 인증”이 아니라 “달라진 내용의 기록”이다. 원상복구되면 기록할 차이가 없다.
사례

내 사이트가 안 바뀌었어요 — 로컬·GitHub·배포, 3단계를 헷갈렸던 하루

2026-07-23

한 줄 요약

로드맵을 다 만들었는데 실제 사이트엔 안 보여서 한참 헤맸어요. 알고 보니 “내 컴퓨터에서 미리보기”와 “인터넷에 진짜 올라간 사이트”는 완전히 다른 곳이었고, 그 사이를 GitHub과 Vercel이 이어주고 있었어요.

누구에게 도움이 될까

  • 학습메이트한테 “이거 만들어줘”를 시켜서 파일은 생겼는데, 실제 사이트(배포된 주소)엔 안 보여서 당황한 적 있는 분
  • “로컬”, “커밋”, “푸시”, “배포”라는 말이 다 따로따로 들려서 뭐가 뭔지 헷갈리는 분

Before — 이런 상황이었어요

4주 로드맵을 인터뷰로 완성하고 “위키에 저장했어요, 미리보기에서 확인해보세요”라는 말을 들었어요. 그런데 제가 실제로 열어본 건 https://learning-hub-template.vercel.app — 진짜 인터넷 주소였는데, 거기엔 로드맵이 안 보였어요. “분명 만들었다고 했는데 왜 없지?” 싶어서 답답했어요.

어떻게 했나 — 실제 대화

이렇게 물어봤어요:

https://learning-hub-template.vercel.app/ 여기엔 없잖아 ㅠㅠ

막힘: 여기서 저는 “로컬 미리보기(localhost:4321)“랑 “실제 배포된 사이트(.vercel.app 주소)“가 같은 건 줄 알았어요. 그런데 순이가 확인해보니, 로컬엔 있는데 배포 사이트엔 없었어요.

해결 — 순이가 설명해준 구조:

단계뭐하는 곳언제 바뀌나
① 로컬 (localhost:4321)내 컴퓨터 안에서만 보이는 임시 미리보기파일 저장하면 바로 반영
② GitHub코드를 저장해두는 온라인 창고커밋 + 푸시를 해야 반영
③ Vercel (.vercel.app 주소)실제로 남들이 보는 배포된 사이트GitHub이 바뀌면 자동으로 감지해서 새로 만듦

즉 로컬에서 파일을 만드는 것만으로는 부족하고, “GitHub Desktop에서 커밋 → Push” 버튼까지 눌러야 그게 GitHub에 올라가고, 그걸 Vercel이 알아서 가져다가 실제 사이트를 다시 만들어줘요.

그다음 GitHub Desktop을 열어서, 아래 순서로 진행했어요:

  1. 변경된 파일 목록 확인 (이번엔 00-roadmap.md 등 3개 파일)
  2. Summary 칸에 짧게 메시지 쓰기
  3. “Commit 3 files to main” 클릭
  4. “Push origin” 클릭

막힘 2: 이 과정을 채팅으로 하나하나 확인하면서 진행하다 보니, 코드로 직접 하는 것보다 “지금 뭐가 어디 상태인지”가 눈에 잘 안 보여서 훨씬 답답하게 느껴졌어요. 화면 캡처를 여러 번 주고받으며 겨우 상황을 맞춰갔어요.

After — 결과

Push 이후 1~2분 기다렸다가 실제 배포 주소에서 다시 확인하니, 로드맵이 정상적으로 떠 있었어요. “로컬 → GitHub → Vercel” 3단계가 순서대로 이어진다는 걸 몸으로 이해하고 나니, 다음부터는 “왜 안 바뀌지?” 하고 당황하지 않고 “아, 아직 커밋·푸시를 안 했구나”를 바로 떠올릴 수 있게 됐어요.

배운 것 / 재사용 자산

  • 기억할 3단계: 로컬(내 컴퓨터, 즉시 반영) → GitHub(커밋·푸시해야 반영) → Vercel(자동 배포, 1~2분 소요).
  • 사이트에 변화가 안 보일 때 체크리스트:
    1. 로컬 미리보기(localhost)에서는 보이나요? → 안 보이면 파일 자체를 다시 확인
    2. GitHub Desktop에서 커밋·푸시를 했나요? → 안 했으면 지금 하기
    3. 푸시한 지 1~2분 지났나요? → Vercel이 새로 만드는 시간이 필요해요
  • 오늘의 솔직한 마음: 채팅으로 진행하다 보니 중간 상태가 눈에 안 보여서 실제 코드 작업보다 더 답답하고 힘들었어요. 그래도 한 번 겪고 나니 다음엔 훨씬 수월할 것 같아요.
사례

매일 아침 8시, 오늘 하루를 요약해서 보내주는 비서 만들기

2026-07-23

무엇을 받나

매일 아침 8시 정각에 텔레그램으로 이런 메시지가 옵니다.

☀️ 좋은 아침이에요. 오늘 일정 정리해 드릴게요.

━ 오늘 일정 ━
· 10:00 ○○ 미팅
· 15:30 △△ 약속
· 19:00 저녁 모임

━ 시장 요약 ━
· 나스닥 +0.8% / S&P500 +0.4% / 다우 -0.1%
· 반도체 섹터는 전반적으로 강세였습니다
· 주요 기업 실적 발표: ○○ 예상치 상회

━ 오늘 날씨 ━
· 최고 28℃ / 최저 19℃, 강수확률 60%
· 오후에 비 소식이 있어요. 우산 챙기세요 ☂️

오늘 하루도 무리하지 마시고, 차분하게 잘 보내시길 바라요.

핸드폰을 열어 앱을 여러 개 확인할 필요가 없습니다. 캘린더, 날씨 앱, 증시 앱을 각각 볼 일을 아침 한 통으로 줄인 게 목적이었어요.


밤 브리핑과 무엇이 다른가

저는 밤에도 가계부 브리핑을 받고 있는데, 둘의 성격이 완전히 다릅니다.

밤 브리핑모닝 브리핑
데이터가 어디서 오나내가 만든 것 (내 지출 기록)바깥에서 끌어옴 (캘린더·날씨·시장)
핵심 난이도문자열 파싱, 계산 규칙여러 API를 인증해서 붙이기
어디서 실행구글 시트에 붙은 스크립트클라우드 서버

밤 브리핑은 “내 데이터를 어떻게 정확히 계산할까”의 문제였고, 모닝 브리핑은 “남의 서비스에서 데이터를 어떻게 가져올까” 의 문제였습니다.


전체 구조

[서버의 스케줄러(cron)]  ← 매일 정해진 시각에 실행


[수집 스크립트]  ── 구글 캘린더 API → 오늘 일정
      │           ── 날씨 API      → 기온·강수확률
      │           ── 시장 데이터 API → 지수·실적

[생성 AI]  ← 모아온 숫자를 사람이 읽을 문장으로


[텔레그램 봇]

저는 월 몇 천 원짜리 저렴한 클라우드 서버에 파이썬 스크립트를 올려두고 cron으로 돌리고 있습니다.


왜 서버를 썼나 (구글 앱스스크립트로도 되는데)

밤 브리핑은 구글 시트에 딸린 Apps Script로 만들었고 서버가 전혀 필요 없었습니다. 무료고, 트리거만 걸면 자동 실행돼요.

그런데 모닝 브리핑은 서버로 옮겼습니다. 이유는:

  • 외부 API를 여러 개 붙이는데 파이썬 쪽이 다루기 편했음
  • 수집한 데이터를 파일로 쌓아두고 여러 스크립트가 나눠 쓰기 좋았음
  • 실행 시간 제한이 없음 (Apps Script는 한 번에 오래 못 돌립니다)

처음 만드는 분이라면 Apps Script로 시작하시는 걸 권합니다. 캘린더와 날씨만이라면 Apps Script로 충분하고 무료예요. 서버는 “이것만으로는 안 되네” 싶을 때 옮기면 됩니다.


구글 캘린더 연결하기 (이게 핵심이었어요)

왜 캘린더를 바꿨나

원래 다른 캘린더 서비스를 쓰고 있었는데 구글 캘린더로 옮겼습니다. 기능이 더 좋아서가 아니라, 프로그램이 읽을 수 있는 API가 제대로 열려 있어서였어요.

이게 자동화의 첫 번째 갈림길입니다. 아무리 좋은 앱이라도 API가 없으면 자동화 대상이 될 수 없습니다. 화면을 긁어오는 방법도 있지만 툭하면 깨져요.

무언가를 자동화하고 싶다면, 그 서비스에 API가 있는지 먼저 확인하세요. 없으면 도구를 바꾸는 게 코드로 씨름하는 것보다 빠릅니다.

인증이 제일 번거로운 부분

캘린더는 개인 데이터라서 “이 프로그램이 내 캘린더를 봐도 된다”는 허락이 필요합니다. 문제는 서버에서 새벽에 혼자 돌아갈 때는 허락을 눌러줄 사람이 없다는 거예요.

그래서 처음 한 번만 사람이 허락하고, 그때 받은 인증 정보를 저장해두는 방식을 씁니다. 이후에는 프로그램이 그걸 꺼내 쓰면서 자동으로 갱신해요.

구글 클라우드 콘솔에서 프로젝트를 만들고 → 캘린더 API를 켜고 → 인증 정보를 발급받아 서버에 두는 흐름입니다. 처음 하면 낯설지만 한 번만 넘으면 끝이에요.

여기서 배운 것: 자동화에서 가장 많이 막히는 지점은 코드가 아니라 인증이었습니다. 그리고 이 인증 파일은 내 캘린더를 열 수 있는 열쇠라서, 절대 남에게 보여주거나 공개 저장소에 올리면 안 됩니다.

가져와서 하는 일

오늘 날짜의 일정을 불러와 시작 시각 순으로 정리합니다. 종일 일정과 시각이 있는 일정을 구분해서 표시하고, 아무것도 없으면 “오늘 등록된 일정은 없어요”로 넘어갑니다.


날씨 — 기온보다 “그래서 뭘 챙기지”

날씨 API에서 최고·최저 기온과 강수확률을 받아옵니다. 위치는 자주 출근하는 지역 기준으로 잡았어요. 집 근처 날씨보다 그게 실용적이었습니다.

여기서 신경 쓴 건 숫자를 그냥 나열하지 않는 것입니다.

  • ❌ “강수확률 60%, 최고 28℃”
  • ✅ “오후에 비 소식이 있어요. 우산 챙기세요”

행동으로 바꿔주는 한 줄이 붙으면 체감 효용이 확 올라갑니다. 아침에 숫자를 해석할 여유는 없으니까요.


시장 정보 — 필요한 만큼만

지수 등락률, 관심 섹터 동향, 주요 실적 발표를 요약해서 받습니다.

처음엔 욕심을 내서 이것저것 다 넣었는데 메시지가 너무 길어져서 안 읽게 됐습니다. 그래서 “아침에 30초 안에 훑을 수 있는 양”으로 줄였어요. 항목을 늘리는 것보다 줄이는 게 어려웠습니다.


개인 데이터를 보여주는 규칙 (여기가 제일 얘기하고 싶은 부분)

투자 현황도 브리핑에 넣었는데, 표시 규칙을 꽤 까다롭게 정했습니다.

1. 장기 보유 계좌는 아예 표시하지 않는다

장기로 들고 갈 것들은 매일 볼 필요가 없습니다. 매일 보면 팔고 싶어집니다. 그래서 브리핑에서 완전히 배제했어요.

2. 손실 중인 항목은 보여주지 않는다

아침에 마이너스 숫자를 보는 게 하루에 도움이 안 된다고 판단했습니다. 플러스인 것만 표시하고, 하나도 없으면 이렇게만 나옵니다.

· 수익률이 플러스(+)인 항목이 없습니다.

숨기는 게 아니라 “확인이 필요할 때 직접 열어보면 된다” 는 판단이었어요. 어차피 앱을 열면 다 보이니까요.

3. 왜 이런 규칙을 정했나

자동 브리핑은 내가 선택하지 않아도 눈에 들어옵니다. 그래서 “볼 수 있는 정보”가 아니라 “매일 봐서 좋은 정보”만 넣어야 한다고 생각했습니다.

자동화에서 “무엇을 넣을까”보다 “무엇을 빼야 할까” 가 더 중요한 결정이었습니다. 넣는 건 쉽지만, 매일 강제로 보게 되는 것의 무게는 무겁습니다.


AI에게 문장을 맡기면서 정한 원칙

모아온 숫자를 사람이 읽을 문장으로 바꾸는 건 생성 AI에 맡겼습니다. 다만 지시를 꽤 강하게 걸어뒀어요.

1. 주어진 숫자 밖의 이야기는 절대 하지 말 것

이게 제일 중요했습니다. 안 그러면 그럴듯하게 지어냅니다. “오늘 반도체가 좋으니 내일도 오를 것 같네요” 같은 말을 아침마다 들으면 판단이 흐려져요.

2. 모르면 모른다고 할 것

API 호출이 실패했을 때 “데이터를 가져오지 못했습니다”라고 하는 게, 대충 채워 넣는 것보다 백 배 낫습니다.

3. 형식은 고정, 문장만 자유

항목 순서와 구성은 코드로 고정하고, AI에게는 문장 다듬기와 마지막 응원 한마디만 맡겼습니다. AI가 구조까지 정하게 하면 매일 형식이 달라져서 훑어보기가 어려워요.

4. 마지막 한 줄은 사람 말투로

기능적으로는 아무 쓸모 없는 응원 한마디를 넣었는데, 이게 의외로 계속 쓰게 만드는 요소였습니다. 알림이 아니라 누가 챙겨주는 느낌이 나요.


시행착오

시간대 때문에 한 번 헤맸습니다. 서버의 스케줄러는 보통 UTC 기준으로 돌아가는데, 저는 한국 시간 8시에 받고 싶었어요. 처음에 이걸 놓쳐서 엉뚱한 시각에 왔습니다. 9시간 차이를 계산해서 설정해야 했어요.

메시지가 너무 길었습니다. 처음엔 정보를 최대한 담았는데, 길면 안 읽습니다. 지금은 스크롤 없이 한 화면에 들어오는 길이를 지키려고 해요.

API 키를 코드에 그대로 써두고 있었습니다. 편하다는 이유로 코드 안에 키를 적어뒀는데, 나중에 코드를 남에게 보여줄 일이 생기면서 키가 그대로 노출된다는 걸 깨달았습니다.

이건 정말 조심해야 하는 부분입니다. 이번에 정리하면서 이렇게 바꿨습니다.

  • 키는 코드가 아니라 환경변수나 별도 설정 저장소에 두기
  • 실수로 노출됐다면 미련 없이 재발급 (지우는 것보다 새로 받는 게 확실합니다)
  • 남에게 코드를 보여줄 때 키·토큰·주소·개인정보가 없는지 먼저 훑기

특히 텔레그램 봇 토큰은 그것만 있으면 남이 내 봇으로 메시지를 보낼 수 있습니다. 캘린더 인증 파일도 마찬가지로 위험해요.


정리하면

1. API가 있는 서비스를 고르는 게 시작이다

캘린더를 옮긴 게 가장 결정적인 선택이었습니다. 도구를 바꾸는 게 코드로 우회하는 것보다 빠릅니다.

2. 자동화의 진짜 난관은 코드가 아니라 인증이다

로직은 몇 줄인데, “내 데이터를 이 프로그램이 봐도 된다”를 설정하는 데 시간이 더 걸립니다. 처음 한 번만 넘으면 되니 여기서 포기하지 마세요.

3. 숫자를 행동으로 바꿔줘야 쓸모가 있다

“강수확률 60%“가 아니라 “우산 챙기세요”. 이 차이가 매일 읽게 만듭니다.

4. 무엇을 뺄지를 먼저 정한다

매일 자동으로 눈에 들어오는 정보는 무게가 다릅니다. 손실 수치를 아침에 보지 않기로 한 게 이 시스템에서 가장 잘한 설계라고 생각해요.

5. AI에게는 구조가 아니라 표현을 맡긴다

형식은 코드로 고정하고, 문장만 다듬게 하기. 그리고 없는 얘기는 하지 말라고 명확히 지시하기.


다음 글에서는 **밤에 받는 가계부 브리핑**을 다뤄봅니다. 폰 결제 알림을 자동으로 모아서 가계부를 채우는 쪽인데, 그쪽은 성격이 완전히 달라서 시행착오가 훨씬 많았습니다.

사례

폰 알림을 가계부로, 가계부를 매일 밤 텔레그램 브리핑으로

2026-07-23

무엇을 만들었나

카드를 쓸 때마다 폰에 오는 결제 알림을 자동으로 모아서, 매일 밤 10시에 텔레그램으로 이런 메시지를 받습니다.

🌙 오늘의 가계부

━ 오늘 지출 ━
· ○○카페 5,600원 (식비/카페)
· △△마트 32,000원 (일반생활비)
합계 37,600원 · 2건

━ 이번달 ━
누적 지출 ***원
예산 대비 ○○% 소진

💳 카드 실적 & 혜택
A카드  ▓▓▓░░░░░░░  실적 진행중
B카드  ▓▓▓▓▓▓░░░░  실적 진행중

💡 오늘의 카드 팁
• A카드 병원·카페 10% 할인(월 2회) 다 썼어요 → 다음은 B카드로
• ⚠️ 주유 할인은 토·일만 적용돼요 — 주유는 주말에!

━ 분석 ━
이 페이스면 예산 안에서 마무리될 것 같아요 👍

잘 자요 😴

직접 입력하는 게 하나도 없습니다. 카드를 긁으면 알림이 오고, 그게 시트에 쌓이고, 밤에 정리돼서 옵니다.


전체 구조

[폰 결제 알림]
      │  MacroDroid가 알림을 낚아채서

[Apps Script 웹앱]  ← 문자열을 해석해서 날짜·금액·상점·카테고리로 쪼갬


[구글 스프레드시트]  ← 한 줄씩 쌓임 (거래내역 시트)


[Apps Script 밤 브리핑]  ← 매일 밤 10시 자동 실행


[텔레그램 봇]  ← 메시지 발송

무료입니다. MacroDroid는 무료 버전으로 충분하고, Apps Script와 구글 시트도 무료, 텔레그램 봇도 무료예요.


1단계 — 폰 알림 낚아채기 (MacroDroid)

안드로이드용 자동화 앱입니다. “이런 일이 생기면 → 저걸 해라”를 만드는 앱이에요.

  • 트리거: 알림 수신 (카드사·은행 앱 알림)
  • 액션: HTTP 요청(POST)으로 알림 본문을 어딘가로 보내기

알림 본문은 MacroDroid의 “매직 텍스트”라는 변수로 꺼내 쓸 수 있습니다. 보낼 주소는 다음 단계에서 만드는 웹앱 URL이에요.

아이폰은 알림 접근이 막혀 있어서 이 방식이 안 됩니다. 안드로이드 전용이에요.

여기서 한 가지 안전장치: URL만 알면 누구나 내 시트에 데이터를 넣을 수 있으니, 요청에 비밀 토큰을 같이 보내고 서버에서 확인하게 했습니다. 토큰이 틀리면 무시합니다.


2단계 — 알림 문자열을 가계부 한 줄로 (Apps Script 웹앱)

구글 시트에서 확장 프로그램 → Apps Script로 들어가 코드를 쓰고, 배포 → 웹 앱으로 배포하면 URL이 나옵니다. 그 URL로 POST가 들어오면 doPost 함수가 실행돼요.

하는 일은 결국 문자열 파싱입니다.

"[○○카드 승인] 5,600원 07/26 21:14 △△카페"

날짜 2026-07-26 / 시각 21:14 / 금액 5,600 / 상점 △△카페 / 분류 식비·카페

카테고리는 키워드 규칙으로 정합니다. “카페·커피·스타벅스” 같은 단어가 있으면 식비, “약국·병원”이 있으면 의료… 이런 목록을 만들어두는 거예요.

의외로 이 단계에서 걸러야 할 게 많았습니다.

  • 누적 금액 무시 — 알림에 “이번 달 누적 ○○원”이 같이 오는 경우가 많아요. 금액 앞 글자를 보고 “누적·잔액·한도” 같은 말이 붙어 있으면 건너뜁니다.
  • 카드값 갚은 건 지출이 아님 — 개별 결제가 이미 기록됐는데 카드 대금 출금까지 넣으면 두 번 세게 됩니다.
  • 내 계좌끼리 이체도 지출이 아님 — 통장에서 통장으로 옮긴 건 돈이 나간 게 아니죠.
  • 취소·환불은 마이너스로 — 원래 지출을 상쇄하게.

이런 예외를 하나씩 발견해서 추가하는 게 작업량의 절반이었습니다.


3단계 — 매일 밤 브리핑 발송

같은 프로젝트에 브리핑용 코드를 하나 더 만들고, 시간 기반 트리거로 매일 밤 실행되게 걸어둡니다. (Apps Script 왼쪽 시계 아이콘 → 트리거 추가)

텔레그램 봇은 @BotFather에게 말 걸어서 만들면 토큰이 나오고, 그 토큰으로 API를 호출하면 메시지가 갑니다. 이 부분은 코드 몇 줄이에요.

브리핑에 넣은 것들:

  • 오늘 쓴 돈 목록과 합계
  • 이번 달 누적, 예산 대비 소진율, 이 페이스로 갔을 때 월말 예상
  • 다가오는 금융 일정 (고정 지출은 하루 전에 알림, 구글 캘린더에서 “돈 나가는 일정” 키워드를 뽑아 7일 전부터 알림)
  • 분류가 애매한 거래는 물어보기

마지막 항목이 실용적이었습니다. 사람 이름만 찍힌 현금 이체나, 어디에 썼는지 알 수 없는 간편결제 건을 AI가 멋대로 “생활비”로 넣어버리면 가계부가 엉망이 돼요. 그래서 자동 분류를 포기하고 되묻게 했습니다.

━ ❓ 분류 확인 필요 ━
아래 거래는 성격이 애매해요. 시트에서 카테고리를 정해주세요:
· (내용 없음) 30,000원

분석 코멘트는 생성 AI에 맡겼습니다. 다만 주어진 숫자 외에는 절대 추측하지 말라고 못박아뒀어요. 안 그러면 없는 얘기를 지어냅니다.


4단계 — 카드 혜택 계산 (여기서 가장 많이 틀렸습니다)

카드를 여러 장 쓰면 “이건 어느 카드로 긁어야 하지?”가 매번 헷갈립니다. 그래서 카드별 실적 진행률과 “이번 달 이 혜택은 다 썼으니 다음은 저 카드로” 같은 팁을 브리핑에 넣기로 했어요.

그런데 여기서 틀린 게 정말 많이 나왔습니다. 카드사 약관을 하나씩 대조하면서 잡은 것들입니다.

문제 1. 혜택 조건을 대충 알고 코드를 썼다

“월 2회 할인” 같은 조건이 어디까지 공유되는지를 잘못 이해하고 있었습니다.

  • 실제로는 “병원·약국·카페·편의점”이 한 묶음으로 월 2회인데, 코드는 각각 월 2회로 세고 있었어요.
  • 어떤 카드는 “A혜택 50% + B혜택 30%“가 하나의 월 한도를 나눠 쓰는데, 코드는 각각 별도 한도로 계산해서 최대 2배로 부풀려 있었습니다.
  • 한도별 주머니가 여러 개인 것도 몰랐어요. 같은 카드 안에서 “시간대 할인 한도”, “공과금 한도”, “주말 한도”가 각각 따로 관리됩니다.

문제 2. 횟수는 세면서 금액은 계속 더하고 있었다

전형적인 버그였어요. “월 2회 초과”를 감지해서 알림은 띄우는데, 할인 금액 누적은 멈추지 않았습니다. 3번째, 4번째 결제의 할인도 계속 더해지고 있었죠. 그래서 “이번 달 아낀 돈”이 실제보다 훨씬 크게 나왔습니다.

문제 3. 시간대 할인을 검증할 수 없었다

“밤 9시~오전 9시 결제 10% 할인” 같은 혜택이 있는데, 시트에 결제 시각이 없었습니다. 날짜만 있었어요.

처음엔 “전부 밤 결제라고 가정하자”고 했는데, 그러면 낮에 먹은 점심까지 할인받은 것으로 잡혀서 숫자가 크게 부풀려집니다. 그래서 밤 할인은 총액에서 빼고 “밤이었다면 최대 얼마”로 따로 표시하게 했어요.

그리고 확인해보니 — 알림 문구에 시각이 이미 들어 있었습니다. (“07/26 21:14 △△카페”) 그동안 파싱해서 저장만 안 했던 거예요. 한 줄 추가로 해결됐습니다. 이제 이렇게 나옵니다.

🌙 밤 할인
· 21:14 ○○ 32,000원 → 1,000원 할인
· ⚠️ 낮에 결제해서 놓친 밤 할인 5건 — 온라인 쇼핑은 밤 9시 이후로!

문제 4. 간편결제로 결제하면 알림이 2개 온다

가장 찾기 어려웠던 문제입니다.

가게에서 페이로 결제하면 페이 앱 알림과 카드사 알림이 둘 다 옵니다. 그런데 같은 결제가 두 번 기록되면 안 되니까, “90초 안에 같은 금액이 오면 무시”하는 중복 제거 로직이 있었어요.

문제는 먼저 온 것만 남는다는 점입니다. 페이 알림이 먼저 오면 카드사 알림이 버려지고, 페이 알림에는 카드 이름이 없으니 “어느 카드로 썼는지”가 통째로 사라졌습니다. 그래서 카드 실적이 계속 0으로 나왔어요.

해결은 버리지 말고 합치기였습니다.

페이 앱 알림카드사 알림합친 결과
카드(없음)○○카드○○카드
상점(페이 이름)△△카페△△카페
시각(없음)21:1421:14
결제수단○○페이(없음)○○페이

두 번째 알림이 오면 빈 칸만 채우도록 했습니다. 어느 쪽이 먼저 와도 결과가 같아요.

문제 5. 그리고 이게 진짜 돈이 되는 발견이었습니다

위 문제를 고치면서 “어떤 페이로 결제했는지”가 기록되기 시작했는데, 덕분에 몇 달째 놓치고 있었을 혜택을 발견했습니다.

쓰는 카드 중 하나는 간편결제로 결제할 때만 10% 할인이 됩니다. 그런데 약관의 대상 목록을 자세히 보니 — 자주 쓰던 페이 하나가 목록에 없었습니다.

이유가 구조적이었어요. 어떤 페이는 결제가 그 페이 회사의 가맹점을 거쳐서 카드사가 “간편결제”로 분류하는데, 다른 페이는 카드를 폰에 넣어 그냥 긁는 것과 같아서 가맹점이 실제 상점으로 찍힙니다. 그러면 간편결제로 분류되지 않아 할인이 0원이에요.

습관 하나 때문에 매달 몇 만원씩 새고 있었던 셈입니다. 이제 브리핑이 이렇게 잡아줍니다.

• 💸 할인 대상이 아닌 페이로 3건 결제 → 최대 4,500원 놓쳤어요

문제 6. 투자 거래가 지출로 잡혀 있었다

주식을 사면 돈이 나가지만 그건 쓴 게 아니라 현금이 주식으로 바뀐 것입니다. 팔아도 번 게 아니고요. 그런데 이게 지출·수입에 섞여서 “이번 달 얼마 썼다”가 왜곡되고 있었습니다.

지출·수입에서 빼고 “자산 이동”으로 따로 표시하게 바꿨습니다. 손익은 평균 매입가를 별도로 관리해서 실제 손익만 계산해요.

여기에 딸린 문제가 하나 더 있었는데, 평균 매입가가 등록되지 않은 종목은 손익을 알 수 없는데도 “판 금액 − 산 금액”을 손익처럼 AI에 넘기고 있었습니다. 그래서 주식을 그냥 팔기만 해도 “이번 달 ○○원 벌었으니 예산에 여유가 있네요” 같은 사실과 다른 분석이 나올 수 있었어요. 모르는 건 넘기지 않도록 고쳤습니다.


오늘 얻은 교훈

1. 자동화는 “수집 → 해석 → 판단”으로 나눠서 보면 쉬워진다

폰 알림 잡기 / 문자열 파싱 / 규칙 계산이 각각 다른 문제입니다. 한꺼번에 만들려니 막막했는데, 단계를 쪼개니 각각은 단순했어요.

2. 규칙 기반 자동화는 “원문”을 봐야 한다

혜택 계산이 틀린 이유는 코드가 아니라 약관을 대충 알고 있었기 때문입니다. 약관 화면을 캡처해서 조건을 한 줄씩 대조하니 예상보다 훨씬 많은 오류가 나왔어요. 특히 “월 몇 회”와 “한도가 어디까지 공유되는지”는 반드시 원문을 봐야 합니다.

3. 애매한 건 자동 분류하지 말고 물어보게 하기

AI에 맡기면 그럴듯하게 채워 넣지만 틀립니다. 가계부는 틀린 데이터가 쌓이면 아무 쓸모가 없어져요. “모르겠으면 물어봐” 가 훨씬 나았습니다.

4. AI가 써준 코드도 검증해야 한다

AI와 함께 코드를 고쳤는데, 받은 코드에서 오류가 두 개 나왔습니다.

  • 대화 중 옮겨 적은 시트 ID가 틀려서 실행이 안 됐어요 → 애초에 ID가 필요 없는 코드였습니다
  • 주석 블록이 잘못 닫혀서 그 뒤 설명글이 코드로 해석됐어요 → 프로젝트 전체가 저장조차 안 되는 상태가 됐습니다. 다른 파일의 함수를 실행하려 해도 실패해서 원인 찾기가 까다로웠어요

두 번째는 AI가 문법 검사를 돌려서 즉시 찾아냈습니다. “고쳤습니다”를 믿지 말고 실제로 돌려보는 것, 그리고 오류가 났을 때 어느 파일이 문제인지 표시(편집기의 경고 아이콘)를 먼저 확인하는 게 빠른 길이었습니다.

5. 버그는 “안 나오는 것”보다 “그럴듯하게 나오는 것”이 위험하다

카드 실적이 0으로 나온 건 눈에 보였지만, 할인 금액이 2배로 부풀려진 건 그냥 “오, 많이 아꼈네” 하고 넘어갈 수 있었습니다. 숫자가 그럴듯할 때 한 번 더 의심하는 습관이 필요합니다. 브리핑 숫자와 카드사 앱의 “이번 달 받은 혜택” 화면을 대조해서 확인했습니다.


다음에 해볼 것

  • 브리핑 숫자와 카드사 앱 실제 혜택을 며칠 비교해서 키워드 규칙 다듬기
  • 상점명 매칭을 키워드로 하고 있어서 한계가 있음 (카드사는 업종 코드로 판정하는데 이름으로 추측하는 중)
  • 알림 2개가 90초보다 벌어져 도착하면 아직 행이 2개 생길 수 있음

완벽하지 않지만, 매일 밤 뭘 놓쳤는지 알려주는 것만으로도 충분히 쓸모 있습니다.

← 이전 글: 매일 아침 8시, 오늘 하루를 요약해서 보내주는 비서 만들기

Chapter

2주차

개념

에러 알림, 어떻게 동작하는 걸까

2026-07-27

브리핑 봇에 “실패하면 알려줘” 기능을 추가할 때 나온 개념 세 가지를 정리했어요.

try/except — “일단 해보고, 안 되면 이렇게 해”

프로그램 코드에서 “이 부분을 일단 실행해봐(try). 만약 중간에 문제가 생기면(except), 대신 이걸 해줘”라고 미리 정해두는 문법이에요.

예를 들면 이런 흐름이에요:

try:
    브리핑 만들어서 보내기
except:
    (문제가 생겼으면) 나한테 "실패했어요" 메시지 보내기

이게 없으면, 중간에 뭔가 하나라도 잘못되면 프로그램이 그냥 조용히 멈춰버려요. 아무 알림도 못 받고, 다음 날 아침에야 “어? 오늘 브리핑이 안 왔네?”라고 알아채게 돼요. try/except를 걸어두면 그 순간 바로 알림이 오니까, 훨씬 빨리 알아챌 수 있어요.

텔레그램 봇 API — 프로그램이 나한테 메시지 보내는 통로

지금 헤르메스 봇이 매일 아침·밤 브리핑을 보내주는 것도 이 통로를 쓰는 거예요. “텔레그램 봇 API”는, 프로그램이 사람 손을 거치지 않고도 텔레그램으로 메시지를 자동으로 보낼 수 있게 해주는 기능이에요.

에러 알림도 원리는 똑같아요. 평소엔 “오늘 일정은…”같은 정상 브리핑을 보내던 그 통로로, 문제가 생겼을 때는 “실패했어요”라는 메시지를 대신 보내는 것뿐이에요. 새로운 걸 따로 만드는 게 아니라, 이미 있는 통로를 한 번 더 쓰는 것이라 생각하면 쉬워요.

로그 — 프로그램이 남기는 기록

“로그”는 프로그램이 실행되는 동안 “지금 이런 일이 있었어요”라고 남기는 기록이에요. 일기랑 비슷해요.

알림 테스트했는데 메시지가 안 왔을 때, 무작정 다시 해보기보다 로그부터 보는 게 순서예요. 로그를 보면 “어디서, 왜” 안 됐는지 힌트가 남아있거든요. 그래서 튜토리얼 4단계에서 막히면 “로그를 확인해서 원인을 알려줘”라고 AI에게 물어보라고 한 거예요.

개념 요약

용어쉬운 뜻
try/except일단 해보고, 문제 생기면 정해둔 대응(알림 보내기 등)을 실행하는 문법
텔레그램 봇 API프로그램이 사람 대신 텔레그램으로 메시지를 자동으로 보내는 통로
로그프로그램이 남기는 실행 기록. 문제 원인을 찾을 때 제일 먼저 보는 곳
사례

AI에게 맡길 일과 코드에 맡길 일 — 반복 서류 정리 자동화 사례

2026-07-23

1. “스킬”이 뭔가요

AI에게 “이 일은 이렇게 하는 거야”를 적어둔 설명서입니다. 한 번 적어두면 다음부터는 처음부터 설명할 필요가 없습니다.

구성은 단순합니다. 설명서 한 장 + 작은 프로그램 몇 개. 설명서에는 순서와 함정을 적고, 프로그램에는 사람이나 AI가 하면 실수하는 일을 맡깁니다.

2. 풀려던 문제

수백 쪽짜리 스캔 PDF를 문서 단위로 잘라내는 일이었습니다.

목록에는 “3번 문서는 10쪽부터”라고 적혀 있습니다. 그런데 PDF를 열면 그게 몇 번째 장인지 알 수 없습니다. 종이에는 손으로 쓴 쪽번호가 있는데 흐리고, 수백 장을 눈으로 넘겨야 합니다.

3. 첫 시도와 실패

“OCR 돌리면 되겠네” 하고 무료 OCR(테서랙트)을 붙였습니다. 16쪽 시도, 0쪽 성공.

OCR은 인쇄된 글자를 읽도록 만들어진 도구라 손글씨를 못 읽습니다.

그런데 같은 이미지를 AI에게 보여주니 15쪽 중 15쪽을 읽었습니다.

교훈 1. 전통 도구가 못 하는 걸 AI가 하는 영역이 있습니다. 안 된다고 포기하기 전에 도구를 바꿔 보세요.

4. 그럼 AI에게 다 시키면 될까요 — 아닙니다

읽는 건 잘합니다. 그런데 “읽은 숫자 스무 개로 나머지 위치를 계산해” 하면 틀립니다. 사람이 암산하는 것과 똑같이 실수합니다.

그래서 역할을 갈랐습니다.

하는 일맡은 쪽이유
흐린 손글씨 읽기AI프로그램이 못 함
숫자 계산·검증프로그램AI가 실수함
파일 자르고 저장프로그램정확성이 전부
애매한 상황 판단AI규칙으로 못 적음

교훈 2. AI에게는 을 맡기고, 은 프로그램에 맡깁니다.

5. AI가 잘못 읽으면 어떻게 잡나

이 작업에는 절대 깨지지 않는 규칙이 하나 있었습니다. 어떤 값이 절대 줄어들 수 없다는 것이었죠. 종이는 순서대로 넘어가니까요.

그래서 프로그램에 이렇게 넣었습니다. 줄어드는 값이 나오면 = 누군가 잘못 읽은 것 → 멈춘다.

실제로 작동했습니다. 어떤 숫자가 299인지 399인지 애매했는데, 299로 넣자 프로그램이 거부했습니다. 옆 페이지를 읽어보니 399가 맞았습니다.

교훈 3. AI 결과를 그냥 믿지 말고, 검산할 수 있는 규칙을 하나 찾아 걸어두세요. 그게 없으면 틀려도 모릅니다.

6. 모르는 걸 모른다고 말하게 만들기

결과마다 확신도를 붙였습니다.

확정      직접 읽어서 확인함
추정      앞뒤로 계산한 값
확인필요  자신 없음, 사람이 봐야 함

사람은 「확인필요」만 보면 됩니다. 나머지는 넘어가도 됩니다.

교훈 4. AI에게 전부 맞히라고 하지 말고, 어디가 불확실한지 알려달라고 하세요. 훨씬 쓸모 있습니다.

7. 실패를 없애려 하지 말고 우회로를 만들기

번호가 아예 안 적힌 페이지가 실제로 있었습니다. 아무리 잘 읽어도 없는 건 못 읽습니다.

그래서 찍지 말고 ?로 넘기게 했습니다. 그러면 프로그램이 옆 페이지를 대신 읽으라고 알려줍니다. 옆 페이지에서도 같은 정보를 얻을 수 있거든요.

교훈 5. 실패를 0으로 만들려 하면 오히려 찍어서 틀립니다. 실패를 인정하고 돌아가는 길을 만드세요.

8. 가장 크게 배운 것 — 일을 잘못 시키고 있었다

처음엔 문서를 처음부터 끝까지 고르게 훑었습니다. 5쪽, 10쪽, 15쪽… 이런 식으로요. 오래 걸렸습니다.

그러다 깨달았습니다. 제가 알아야 하는 건 문서 31개의 시작 위치 31개뿐이었습니다. 나머지 페이지는 볼 필요가 없었습니다.

필요한 곳만 찍어서 보게 바꾸니 왕복이 절반으로 줄었습니다.

교훈 6. 자동화가 느리면 도구를 의심하기 전에 **“내가 무엇을 묻고 있는가”**를 의심하세요. 대개 질문이 잘못돼 있습니다.

마무리

스킬은 거창한 게 아닙니다. “AI에게 무엇을 시키고 무엇을 시키지 않을지”를 적어둔 문서입니다.

이번 사례에서 그 경계는 이렇게 정리됐습니다.

  • 눈으로 봐야 아는 것 → AI
  • 틀리면 안 되는 계산 → 프로그램
  • 애매하면 → 찍지 말고 사람에게

이 세 줄이 전부입니다.

사례

AI가 만든 가짜 출처, 코드로 잡아내는 시스템 만들기

2026-07-27

한 줄 요약

AI가 만든 글에서 “존재하지 않는 출처”를 사람이 눈으로 대조하지 않고, 코드가 기계적으로 잡아내는 시스템을 직접 만들었어요. 예시 데이터로 테스트했을 땐 멀쩡했는데, 형태를 좀 더 복잡하게 바꿔서 다시 돌려보니 진짜 버그가 2개 나왔습니다.

이런 분께 도움 돼요: AI로 보고서·글을 쓰는데, 인용한 출처(문서번호·규정 조항 같은 것)가 실제로 맞는지 매번 눈으로 확인하기 지치는 분.


Before — 뭐가 불편했나

AI에게 글을 써달라고 하면, 문장은 그럴듯한데 가끔 존재하지 않는 문서번호를 인용해요. 예를 들면 이런 식이에요.

“정책문서 2024-A-1204호에 따르면 ~해야 한다”

문제는 이게 진짜 있는 문서인지, AI가 그냥 그럴듯하게 지어낸 건지 문장만 보고는 구분이 안 된다는 거였어요. 매번 원문을 찾아 눈으로 대조하는 게 시간도 오래 걸리고, 무엇보다 사람이 하다 보면 놓치는 게 생겨요.

어떻게 — 3단계 검증 시스템

AI한테 “이거 맞는지 확인해줘”라고 다시 물어보는 건 별로 효과가 없었어요. 같은 AI가 착각해서 지어낸 걸, 같은 AI에게 다시 검토시키면 또 못 잡는 경우가 많더라고요. 그래서 AI 판단이 아니라 코드가 판정하는 방식으로 설계했어요.

1단계 — 출처 자동 추출 (정규식, AI 없음)

글에서 “문서번호” 패턴을 코드로 뽑아냅니다. AI한테 “뽑아줘”라고 시키지 않는 이유는, 추출 단계부터 AI에게 맡기면 AI가 지어낸 출처를 “잘 뽑았다”고 착각할 위험이 있어서예요.

import re

DOC_ID_PATTERN = re.compile(r"\d{4}-[A-Z]-\d{3,5}?")

def extract(text):
    return [m.group() for m in DOC_ID_PATTERN.finditer(text)]

2단계 — 공식 출처와 원문 대조 (API 연동)

추출된 문서번호로, 그 문서를 관리하는 공식 시스템(사내 문서관리 API 등)에서 원문을 실제로 가져옵니다. 여기서 못 찾으면 “원문 자체가 없다”는 뜻이니 바로 의심 대상이 돼요.

3단계 — 기계적 대조로 PASS/FAIL 판정

def verify(claim_text, source_texts):
    target = normalize(claim_text)
    for source in source_texts:
        if target in normalize(source):
            return "PASS"
    return "FAIL"

이 판정에는 AI가 전혀 개입하지 않아요. 원문에 있으면 PASS, 없으면 FAIL — 그게 다예요.

막힘 → 해결: 진짜 자료로 테스트하니까 나온 버그 2개

처음엔 간단한 예시 문장 몇 개로 테스트했는데 잘 작동했어요. “됐다!” 싶었는데, 실제로 쓰이는 형태의 복잡한 문서로 다시 테스트해보니 진짜 버그가 튀어나왔습니다.

버그 1 — 번호 표기 방식 차이

원문 문서는 항목 번호를 “①②③” 같은 동그라미 숫자로 쓰는데, 글에서는 “제1항”이라고 풀어서 씁니다. 사람 눈엔 당연히 같은 뜻인데, 코드는 문자 그대로만 비교하니까 다른 걸로 인식해서 진짜 있는 출처인데도 FAIL로 잘못 판정했어요.

# 수정: ①→"제1항" 자동 변환 후 비교
CIRCLED_TO_HANG = {"①": "제1항", "②": "제2항", "③": "제3항"}
def normalize(s):
    for circled, spelled in CIRCLED_TO_HANG.items():
        s = s.replace(circled, spelled)
    return re.sub(r"\s+", "", s)

버그 2 — 엉뚱한 파일에서 우연히 일치

더 심각했던 건 이거예요. 문서 A번을 검증해야 하는데, 전혀 상관없는 문서 B번 안에 우연히 같은 글자 조합이 들어있어서 엉뚱한 문서에서 “찾았다”고 잘못 PASS 판정한 거예요. 존재하지 않는 걸 존재한다고 잘못 통과시키는, 더 위험한 종류의 오류였어요.

# 수정: 같은 종류(문서 유형)의 원문에서만 대조하도록 후보를 좁힘
candidates = [s for s in sources if s.doc_type == claim.doc_type]

두 버그 다 간단한 예시로는 절대 못 찾았을 거예요. 처음에 “테스트 통과했으니 됐다”고 안심했으면 그대로 넘어갔을 문제들이었습니다.

After — 뭐가 달라졌나

  • 출처 확인을 사람이 일일이 안 하고, 코드가 PASS/FAIL/확인불가로 자동 분류
  • FAIL·확인불가만 골라서 보면 되니, 검토 시간이 크게 줄었어요
  • 이 과정 전체를 “스킬”로 저장해둬서, 다음부턴 “검수해줘” 한마디로 3단계가 자동으로 돌아가요

배운 것 / 재사용 자산

  • 간단한 예시로 통과했다고 안심하지 말 것. 실제로 쓰는 형태(특수기호, 복잡한 문서 구조)로 다시 테스트해야 진짜 버그가 보여요.
  • AI에게 “다시 확인해줘”보다, AI 판단이 아예 안 들어가는 코드 대조가 훨씬 확실해요. 대신 이건 “존재 여부”만 잡을 수 있고, “내용이 맞게 인용됐는지”는 별도로 사람이나 다른 AI가 봐야 해요.
  • 위 정규식·대조 코드는 문서번호 패턴만 바꾸면 다른 분야에도 그대로 쓸 수 있어요.

이번에 새로 배운 개념

이 시스템을 설계하면서 처음 마주친 용어들을, 제가 이해한 만큼 쉽게 풀어 정리해봤어요.

용어쉬운 설명
결정론적 게이트”무조건 이 규칙대로만 판정한다”는 뜻. AI가 “대충 괜찮은 것 같은데?”라고 판단하는 게 아니라, 코드가 딱 정해진 기준으로만 통과/불통과를 가르는 것.
Gold-in Gold-out”좋은 걸 넣어야 좋은 게 나온다.” 아무 자료나 막 쌓아두지 말고, 실제로 검증에 쓰인 것만 골라서 저장해두자는 원칙.
루프의 3요소 (목표·검증자·정지조건)뭔가를 “될 때까지 반복시키는” 자동화를 만들 때 꼭 정해야 하는 3가지. ① 뭘 이루고 싶은지 ② 이뤄졌는지 누가/뭐가 확인하는지 ③ 언제 멈출지. 이게 없으면 무한히 돌거나 엉뚱하게 끝남.
독립 모델 교차검증AI 한 명한테만 물어보면 그 AI가 착각한 걸 그대로 믿을 수 있으니, 다른 AI한테도 같이 물어봐서 답이 갈리면 의심해보는 방법.
결정성 경계”여기까지는 코드/규칙이 정하고, 여기부터는 AI가 판단해도 된다”는 선을 미리 그어두는 것. 중요한 최종 판정은 AI 맘대로 못 바꾸게 막아두는 원칙.

앞으로의 계획

지금 만든 건 “출처가 존재하는가”만 잡아냅니다. 다음 단계로 생각하고 있는 건 이거예요.

  1. 독립 모델 교차검증 자동화 — 지금은 사람이 수동으로 다시 확인하는데, 서로 다른 AI 두 개에게 같은 내용을 동시에 검토시키고 의견이 갈리는 지점만 자동으로 뽑아내는 스크립트를 만들 계획이에요. 특히 “출처는 진짜인데 내용을 반대로 인용한 경우”는 지금 시스템의 최대 사각지대라, 이 부분을 메우는 게 목표예요.
  2. 반복 검증 루프 — FAIL이 0건 될 때까지 자동으로 원문을 계속 찾아보고, 정말 없으면 스스로 멈추고 사람에게 알리는 자동화를 붙이려고 해요.
  3. 지식창고 축적 — 매번 API를 새로 부르는 대신, 자주 쓰이는 출처는 검증에 실제로 쓰인 것만 골라 누적해두는 구조(Gold-in Gold-out 원칙 그대로)로 발전시킬 예정이에요.
  4. 완료 기준 명확히 하기 — “다 됐다”를 AI 혼자 판단하게 두지 않고, 제출 직전에 “왜 이게 됐다고 확신하는지”를 저 스스로 설명해야 넘어가는 마지막 관문을 만들어두려고 해요. AI에게만 맡기고 안 읽어보고 넘기는 걸 구조적으로 막기 위해서예요.

이 사례는 실제 업무 대신 재구성한 예시로 작성했어요. 방법론과 배운 점은 실제로 겪은 것 그대로입니다.

사례

AI 에이전트를 서버에 띄웠더니 일주일에 5만원이 사라졌다

2026-07-28

사건

개인 비서 AI를 텔레그램에 연결해서 쓰고 있습니다. 매일 아침·밤에 브리핑을 보내주고, 평소엔 그냥 말을 걸면 대답해주는 봇이에요. 클라우드 서버에 올려두고 24시간 돌립니다.

어느 날 API 선불 크레딧 5만원이 다 떨어졌다는 걸 알았습니다. 충전한 지 일주일 만에요.

이상했습니다. 제가 쓰는 건 하루 브리핑 두 번에 가끔 던지는 잡담이 전부인데, 그 정도로 5만원이 나갈 리가 없거든요. 텍스트 몇 줄 주고받는 게 그렇게 비싼 일은 아닙니다.

결론부터 말하면 원인은 설정 한 줄이었고, 그것 때문에 메시지 하나 보낼 때마다 15만 토큰을 보내고 있었습니다.


왜 몰랐을까 — 조용한 실패

먼저 짚고 갈 게 있습니다. 왜 일주일 동안 몰랐느냐입니다.

브리핑은 계속 왔습니다. 봇도 대답했고요. 겉으로는 아무 문제가 없었어요. 알림도, 경고도 없었습니다. 그냥 어느 날 “크레딧이 소진되었습니다”가 떴을 뿐입니다.

이게 자동화의 무서운 점입니다. 눈에 보이는 고장은 금방 고치는데, 조용히 돌아가면서 돈만 새는 건 알 수가 없습니다.

나중에 로그를 보니 이런 게 잔뜩 쌓여 있었습니다.

Quota exceeded ... limit: 2000000
Retrying API call in 2.6s (attempt 1/3)
Retrying API call in 5.3s (attempt 2/3)

분당 토큰 한도까지 초과해서 재시도를 반복하고 있었던 겁니다. 그런데 이 로그는 서버 안에만 있으니 제가 볼 일이 없었죠.


추적 과정 (헛다리 포함)

여기서부터가 이 글의 본론입니다. 원인을 어떻게 찾았는지, 그리고 어디서 헛다리를 짚었는지 순서대로 적어보겠습니다.

헛다리 1 — “무한 루프가 도는 거 아냐?”

가장 먼저 의심한 건 스케줄 설정 오류였습니다. 하루 한 번 돌아야 할 작업이 1분마다 도는 걸로 잘못 설정돼 있으면 하루 1440번 실행되니까요. 흔한 실수입니다.

서버에 들어가서 예약 작업 목록을 확인했습니다.

no crontab for root

예약 작업이 아예 없었습니다. 그럼 브리핑은 뭐가 보내는 거지?

이건 나중에 풀렸는데, 그 에이전트가 자기만의 스케줄러를 따로 갖고 있었습니다. 시스템 예약 작업이 아니라 프로그램 내부에서 시간을 재고 있었던 거죠. 그래서 시스템 쪽만 봐서는 안 보였습니다.

교훈: 프로그램이 자체 스케줄러를 갖고 있으면 시스템 쪽 예약 목록엔 안 나옵니다.

헛다리 2 — 사용량 그래프가 텅 비어 있었다

클라우드 콘솔에서 API 사용량 그래프를 봤습니다. 거의 0에 붙어 있었습니다. 일주일 내내 점 몇 개가 전부였어요.

“어? 그럼 API를 안 쓴 건데 돈은 어디서 나갔지?”

여기서 한참 헤맸습니다. 알고 보니 프로젝트를 잘못 고르고 있었습니다. 계정에 프로젝트가 4개나 있었고, 실제 키가 속한 프로젝트는 따로 있었던 거예요.

교훈: 사용량 그래프는 “어느 프로젝트를 보고 있는지” 먼저 확인해야 합니다. 텅 비어 보이면 안 쓴 게 아니라 다른 데를 보고 있는 것일 수 있습니다.

전환점 — 결제 내역을 항목별로 보기

추측을 그만두고 결제 보고서를 열었습니다. 이게 결정적이었어요.

그래프로 “얼마나 호출했나”를 추측하는 것보다, 청구서에서 “뭐에 얼마 청구됐나”를 직접 보는 게 훨씬 빠릅니다.

그룹화 기준과금 항목별로 바꾸니 이렇게 나왔습니다.

항목비용비중
일반 모델 텍스트 (입력)₩29,157
일반 모델 텍스트 (캐시 입력)₩12,949
일반 모델 텍스트 (출력)₩5,577
소계₩47,68399.8%
이미지 생성₩170.04%
경량 모델 (브리핑용)₩50.01%

헛다리 3 — “이미지 생성 때문인가?”

항목 목록에 이미지가 있는 걸 보고 “이거다!” 싶었습니다. 그 주에 이미지를 몇 장 만든 기억이 있었거든요. 이미지 생성은 텍스트보다 훨씬 비싸니까 말이 됐습니다.

그런데 금액을 확인하니 ₩17이었습니다. 전체의 0.04%. 완전히 헛다리였어요.

교훈: 항목이 있다는 것과 그게 비용을 먹었다는 건 다릅니다. 이름만 보고 지목하지 말고 금액을 봐야 합니다.

그리고 진짜 단서 — 토큰 수

범인은 일반 텍스트 모델이 확실해졌습니다. 그런데 이상했습니다.

입력 토큰      1,266만개
캐시 입력      5,624만개
출력 토큰         40만개

일주일에 입력 6,900만 토큰. 그런데 출력은 40만 토큰뿐입니다.

입력이 출력의 170배.

이 비율이 결정적이었습니다. 정상적인 대화라면 이렇게 안 나옵니다. 이건 “긴 내용을 계속 다시 보내면서 짧은 답을 받는” 패턴이에요.

그리고 캐시 입력이 5,624만으로 압도적인데, 캐시는 같은 앞부분을 반복해서 보낼 때 생깁니다. 즉 뭔가 똑같은 걸 계속 재전송하고 있다는 뜻이었죠.


진범

서버 로그를 뒤졌더니 이게 나왔습니다.

Context: 472 msgs, ~150,859 tokens

대화 하나에 메시지가 472개 쌓여서, API 호출 한 번에 150,859 토큰을 보내고 있었습니다.

그리고 바로 옆에 새로 시작한 대화의 숫자가 같이 찍혀 있었어요.

메시지 수요청당 토큰
새로 시작한 대화2개1,616
그동안 쌓인 대화472개150,859

93배 차이입니다. 똑같이 “안녕” 한 마디를 보내도 93배 비싼 값을 치르고 있었던 거예요.

원인은 설정 파일의 이 부분이었습니다.

세션 초기화:
  방식: 안 함          ← 여기
  유휴 기준: 24시간
  매일 초기화: 새벽 4시

유휴 시간 기준과 매일 초기화 시각은 멀쩡히 설정돼 있는데, “방식: 안 함” 때문에 둘 다 작동하지 않고 있었습니다. 그래서 봇을 처음 켠 날부터 6일치 대화가 통째로 쌓여 있었고, 메시지를 보낼 때마다 그 전부가 다시 전송됐습니다.

왜 매번 다시 보내나

AI는 이전 대화를 기억하지 못합니다. “기억하는 것처럼 보이는 건, 매번 지금까지의 대화를 통째로 다시 들려주기 때문” 이에요.

그래서 대화가 길어질수록 한 번 보낼 때마다 비싸집니다. 20번째 메시지는 1번째보다 20배 비싸고, 472번째는 말할 것도 없죠.

이게 대화형 AI 비용의 핵심 구조인데, 저는 이걸 모르고 있었습니다.

여기에 두 가지가 겹쳤습니다.

  • 에이전트는 한 메시지에 여러 번 호출합니다. 도구를 쓰고 결과를 보고 다시 생각하는 걸 반복하는데, 제 설정은 최대 20번이었습니다. 즉 “안녕” 한 마디에 15만 토큰 × 최대 20번.
  • 실패하면 재시도합니다. 한도를 넘겨 실패해도 3번까지 다시 시도했고, 재시도도 당연히 과금됩니다.

고친 것

설정 세 줄과 명령어 하나였습니다.

항목이유
기본 모델일반 모델경량 모델단가가 몇 배 쌈. 개인 비서 대화엔 충분
세션 초기화안 함4시간 유휴 + 매일 새벽 4시에 자동 리셋
유휴 기준24시간4시간24시간은 사실상 안 걸림
메시지당 최대 호출20회10회일상 대화엔 10회면 충분

그리고 이미 쌓여 있던 대화를 텔레그램에서 새 대화 명령으로 강제 초기화했습니다. 설정을 바꿔도 기존 대화는 그대로 살아있었거든요.

결과:

전:  472개 메시지 / 약 150,859 토큰
후:    2개 메시지 / 약   5,175 토큰

요청당 토큰이 29분의 1로 떨어졌습니다. 여기에 모델도 더 싼 걸로 바꿨으니 효과가 곱해집니다.

세션 초기화 = 기억상실이 아닙니다

“매일 대화를 지우면 봇이 나를 잊는 거 아냐?”라고 걱정했는데, 아니었습니다.

그 에이전트는 대화 원문기억을 따로 관리하고 있었어요.

무엇리셋하면
세션주고받은 대화 원문 전체지워짐
메모리중요한 사실만 추려서 저장그대로 남음

즉 “내가 누구고 뭘 좋아하는지”는 계속 기억합니다. 지워지는 건 “어젯밤에 나눈 잡담 원문” 같은 거고, 그건 매번 다시 보낼 필요가 없는 것이죠.


교훈

1. 조용히 도는 것에 알림을 걸어라

가장 큰 실수는 예산 알림을 안 걸어둔 것이었습니다. 걸어뒀으면 첫날 ₩11,000이 나갔을 때 바로 알았을 텐데, 일주일 뒤 다 떨어지고 나서야 알았습니다.

클라우드 서비스는 대부분 예산 알림 기능이 있습니다. 월 한도를 정하고 50%·90%에서 메일이 오게 해두세요. 5분이면 됩니다.

2. 추측하지 말고 청구서를 봐라

저는 “루프가 도나?” “이미지 때문인가?” 하며 한참을 추측했습니다. 그런데 결제 내역을 항목별로 열어보니 5분 만에 답이 나왔습니다.

사용량 그래프는 해석이 필요하지만, 청구서는 “뭐에 얼마”가 그냥 적혀 있습니다. 비용 문제는 청구서부터 보는 게 맞습니다.

3. 그럴듯한 가설일수록 숫자로 확인해라

“이미지 생성은 비싸다”는 맞는 말입니다. 목록에 이미지 항목도 있었고요. 그럴듯했습니다.

그런데 ₩17이었습니다.

가설이 그럴듯할수록 확인 없이 믿게 됩니다. 저는 이걸 두 번 했습니다 — 이미지도 그랬고, “스킬이 많아서 비싼가?”도 확인해보니 아니었어요.

4. 대화형 AI는 “대화가 길어질수록 비싸진다”

이 구조를 몰랐던 게 근본 원인이었습니다. AI는 매번 전체 대화를 다시 받습니다. 그래서 긴 대화 하나를 계속 이어가는 것보다, 주제가 바뀌면 새 대화를 시작하는 게 훨씬 쌉니다.

이건 서버에 띄운 봇뿐 아니라 웹 채팅창에서도 똑같이 적용됩니다. 유료 API를 쓰신다면 긴 대화를 습관적으로 끊는 것만으로 비용이 크게 줄어요.

5. 에이전트는 채팅보다 훨씬 비싸다

일반 채팅은 한 번 물으면 한 번 답합니다. 그런데 에이전트는 도구를 쓰고 결과를 보고 다시 생각하는 걸 반복해요. 제 경우 한 메시지에 최대 20번까지 호출할 수 있게 돼 있었습니다.

에이전트를 쓸 거라면 최대 반복 횟수를 반드시 확인하세요. 기본값이 관대하게 잡혀 있는 경우가 많습니다.

6. 실패도 과금된다

한도를 초과해 실패한 호출도, 그 뒤의 재시도도 전부 돈이 나갑니다. 에러가 난다고 공짜가 아닙니다.


지금 확인해볼 것

혹시 AI API를 유료로 쓰고 계시다면, 오늘 이것만 해보셔도 좋겠습니다.

  • 예산 알림이 걸려 있나? — 없으면 지금 5분 투자
  • 결제 내역을 항목별로 본 적 있나? — 뭐가 돈을 먹는지 알고 계신가요
  • 긴 대화를 계속 이어가고 있진 않나? — 주제가 바뀌면 새로 시작
  • 에이전트의 최대 반복 횟수는? — 기본값 확인
  • API 키가 코드에 그대로 적혀 있진 않나? — 별도 설정으로 분리

마무리

기술적으로 어려운 문제는 아니었습니다. 설정 한 줄이었고, 알고 나면 5분이면 고칩니다.

어려웠던 건 “어디를 봐야 하는지 모르는 상태에서 시작하는 것” 이었어요. 서버, 스케줄러, 사용량 그래프, 청구서, 로그 — 볼 곳은 많은데 어디에 답이 있는지 몰랐습니다. 헛다리도 세 번 짚었고요.

돌아보면 순서가 있었습니다.

청구서(항목별)  →  뭐가 돈을 먹었나

로그(토큰 수)   →  왜 그렇게 많이 썼나

설정            →  어디를 고치나

추측은 맨 마지막에 해도 됩니다. 데이터가 먼저입니다.

그리고 5만원은 아까웠지만, 덕분에 대화형 AI의 비용 구조를 제대로 이해하게 됐습니다. 앞으로 훨씬 더 아낄 테니 수업료라고 생각하려고요.

사례

텔레그램 AI 비서 만들기 A to Z — 브리핑은 공짜로, 대화는 서버로

2026-07-28

시작하기 전에 — 지난 한 주 배운 것

이 프로젝트는 맨땅에서 시작한 게 아니라, 아루나 AI 온보딩 과정의 1주차 커리큘럼을 따라오면서 쌓은 것들 위에 서 있습니다. 하루씩 이런 걸 배우고 저장해뒀어요.

일차배운 것이 프로젝트에 쓰인 곳
1일차나의 학습메이트 만들기AI에게 역할과 말투를 정해주는 법 → 비서 봇의 성격 설정
2일차4주 로드맵과 랜딩 페이지”브리핑 비서 만들기”를 4주 목표로 확정
3일차내 사이트를 인터넷에 올리기 (배포)“배포”라는 개념 — 이 글 후반의 웹앱 배포에서 재등장
4일차되돌릴 수 있는 안심 (커밋과 버전관리)코드를 고치다 망쳐도 되돌릴 수 있다는 자신감
5일차내 프로젝트 PRD 만들기브리핑에 뭘 담을지 문서로 먼저 정리
6일차세부계획 세우고 MVP 첫 조각 만들기밤 브리핑 첫 버전이 여기서 나옴
7일차한 주 돌아보기 (회고일지와 종합사례글)지금 이 글

특히 5~6일차가 컸습니다. 만들기 전에 “뭘 만들 건지”를 글로 정리해두니, 실제 만들 때 헤매는 시간이 확 줄었어요.


완성된 모습

매일 아침 8시쯤, 텔레그램으로 이런 게 옵니다.

☀️ 7/29(수) 아침 브리핑

━ 오늘 일정 ━
· 10:00 ○○ 미팅
· 15:30 △△ 약속

━ 오늘 날씨 ━
· 최고 32℃ / 최저 25℃
· 강수확률 32%
· 접이식 우산 하나 넣어두시면 좋겠어요 🌂
· 많이 더워요. 물 자주 드세요 💧

오늘도 무리하지 마시고 차분하게 보내시길 바라요.

밤 10시에는 그날 쓴 돈이 정리돼서 옵니다.

🌙 오늘의 가계부

━ 오늘 지출 ━
· ○○카페 5,600원 (식비/카페)
· △△마트 32,000원 (생활비)
합계 37,600원 · 2건

━ 이번달 ━
예산 대비 ○○% 소진

💡 오늘의 카드 팁
• A카드 커피 할인(월 2회) 다 썼어요 → 다음은 B카드로

잘 자요 😴

그리고 낮에 궁금한 게 생기면 봇에게 그냥 물어봅니다.

나: 배달앱 시킬 건데 무슨 카드로 할까?
봇: B카드가 좋아요. 배달앱 10% 할인이 월 한도까지 됩니다.

직접 입력하는 건 없습니다. 카드를 긁으면 알아서 기록되고, 정해진 시간에 알아서 정리돼서 옵니다.


1부. 먼저 알아야 할 5가지

기술 용어를 모르면 설명서를 읽어도 무슨 말인지 모릅니다. 딱 5개만 알고 가면 됩니다.

① AI는 “기억하지 못한다”

챗봇과 대화하면 이전 얘기를 기억하는 것처럼 보이죠. 사실은 매번 지금까지의 대화 전체를 다시 들려주기 때문입니다.

내가 "안녕" 이라고 보내면
→ 실제로는 "(지금까지 나눈 대화 전부) + 안녕" 이 통째로 전송됨

이걸 알아야 하는 이유가 있습니다. 대화가 길어질수록 요금이 비싸집니다. 뒤에서 이것 때문에 사고 난 얘기가 나옵니다.

② API = 프로그램이 쓰는 출입구

우리가 챗봇 화면에서 대화하듯, 프로그램도 AI에게 말을 걸 수 있습니다. 그 통로가 API예요.

중요: 챗봇 구독료(월 정액)와 API 요금은 완전히 별개입니다. 구독한다고 API가 열리지 않아요.

③ API 키 = 출입증

API를 쓰려면 “나는 누구다”를 증명해야 하는데, 그게 API 키입니다. 긴 문자열로 된 비밀번호라고 생각하시면 돼요. 남에게 보이면 그 사람이 내 요금으로 AI를 쓸 수 있습니다.

④ 서버 = 24시간 켜져 있는 남의 컴퓨터

봇이 내 질문에 언제든 답하려면, 누군가 24시간 듣고 있어야 합니다. 내 노트북은 자니까 안 되죠. 서버는 인터넷 어딘가에서 24시간 켜져 있는 컴퓨터를 월 몇 천 원에 빌리는 겁니다. 모니터도 키보드도 없어서 글자로만 조작해요.

⑤ 앱스크립트 = 구글이 공짜로 빌려주는 미니 서버

구글 시트에는 Apps Script라는 기능이 붙어 있습니다. 구글 안에서 코드를 돌릴 수 있고, “매일 밤 10시에 실행” 같은 예약도 되고, 무료입니다.

이게 이 글의 핵심 재료입니다. 정해진 시간에 정해진 일을 하는 것은 전부 이걸로 해결됩니다.


2부. 전체 설계 — 일을 둘로 나눈다

처음엔 서버의 AI 비서가 브리핑까지 전부 만들게 했습니다. 그런데 쓰다 보니 일의 성격이 두 가지라는 걸 알게 됐어요.

성격맡길 곳
아침·밤 브리핑정해진 걸 정해진 시간에앱스크립트 (무료, 코드가 계산)
질문에 답하기무엇을 물을지 모름서버의 AI (유연하지만 유료)
【 브리핑 — 서버 필요 없음, 전부 무료 】

  구글 캘린더 ─┐
  날씨 서비스  ─┼→ 앱스크립트가 모아서 → 텔레그램 발송 (아침)
  폰 결제알림 → 구글 시트 → 앱스크립트가 정리 → 텔레그램 발송 (밤)

【 대화 — 서버의 AI 비서 】

  내 질문 → 텔레그램 → 서버의 AI가 생각하고 → 답변

이렇게 나누면 좋은 점이 세 가지입니다.

  1. 브리핑 비용이 0원 — AI를 안 거치니까요
  2. 숫자를 지어낼 수 없음 — 합계·날씨·일정은 코드가 계산합니다. AI에게 시키면 그럴듯한 거짓 숫자가 나올 수 있어요 (실제로 겪었습니다)
  3. 서버가 죽어도 브리핑은 옴 — 두 축이 독립적이라

3부. 준비물과 비용

항목비용어디에 필요
텔레그램 + 구글 계정무료전부
안드로이드 폰-가계부 자동기록 (아이폰은 알림 접근 불가)
AI API 키무료 한도로 충분대화 봇
서버월 몇 천 원대화 봇에만

브리핑만 원하시면 돈이 한 푼도 안 듭니다. 서버는 대화 기능을 원할 때만 빌리면 돼요.


4부. 텔레그램 봇 만들기 (공통 준비, 10분)

봇은 “내 프로그램이 쓰는 텔레그램 계정”입니다.

  1. 텔레그램에서 @BotFather 검색 → 대화 시작
  2. /newbot 입력 → 봇 이름과 아이디 정하기 (_bot으로 끝나야 함)
  3. 토큰이라는 긴 문자열이 나옴 → 봇의 비밀번호. 어디에도 공개 금지
  4. 만든 봇을 찾아 아무 말이나 걸어두기 (내가 먼저 말을 걸어야 봇이 나에게 보낼 수 있음)
  5. @userinfobot 에게 말 걸면 나오는 내 id 적어두기 (봇이 “누구에게” 보낼지)

5부. 밤 가계부 브리핑 (서버 없이, 구글만으로)

흐름

카드 결제 → 폰에 알림 → 자동화 앱이 낚아챔 → 구글 시트에 기록
                                → 밤 10시에 앱스크립트가 정리 → 텔레그램

1단계 — 구글 시트와 앱스크립트

시트를 만들고 거래내역 탭을 준비합니다. 그리고 상단 메뉴 확장 프로그램 → Apps Script로 코드 편집기를 엽니다.

여기서 만든 코드가 하는 일은 문자열 해석입니다.

"[○○카드 승인] 5,600원 07/26 21:14 △△카페"

날짜 / 시각 / 금액 / 상점 / 분류(키워드 규칙으로)

이 코드를 웹앱으로 배포(배포 → 새 배포 → 웹 앱, 액세스 “모든 사용자”)하면 인터넷 주소가 생기고, 폰이 그 주소로 알림을 쏘는 구조입니다. 3일차에 배운 그 “배포”가 여기서 다시 나옵니다.

⚠️ 제일 많이 헤매는 지점: 코드를 고치고 저장해도 웹앱에는 반영이 안 됩니다. 웹앱은 배포한 순간의 코드를 씁니다. 고칠 때마다 배포 → 배포 관리 → ✏️ 수정 → 「새 버전」 → 배포. (“새 배포”를 누르면 주소가 바뀌어 폰 설정을 다시 해야 하니 주의)

2단계 — 폰 알림 낚아채기

안드로이드 자동화 앱 MacroDroid(무료 버전 충분)에서:

  • 트리거: 알림 수신 (카드사·은행 앱)
  • 액션: HTTP 요청으로 알림 내용을 웹앱 주소로 전송

🔒 주소만 알면 누구나 내 시트에 데이터를 넣을 수 있으니, 요청에 비밀 단어를 같이 보내고 코드에서 맞을 때만 처리하게 했습니다.

3단계 — 예외 처리 (작업량의 절반)

알림이 생각보다 지저분합니다. 하나씩 발견해서 막았어요.

  • “이번 달 누적 ○○원” 이 같이 옴 → 지출로 세면 안 됨
  • 카드값 납부 알림 → 개별 결제가 이미 기록됐으니 두 번 세면 안 됨
  • 내 통장끼리 이체 → 돈이 나간 게 아님
  • 취소·환불 → 마이너스로 상쇄
  • 간편결제는 알림이 2개 옴 (페이 앱 + 카드사) → 처음엔 뒤에 온 걸 버렸다가 카드 정보가 사라지는 문제 발생 → 두 알림을 하나로 합치는 방식으로 해결

압권은 이거였습니다. 어느 날 배달앱 결제가 ‘저축’으로 분류돼 있더군요. 알림 속 “누적금액은?” 이라는 안내 문구에서 “적금” 두 글자가 저축 키워드에 걸린 거예요. 키워드 분류는 이런 함정이 있으니, 원문(메모)을 같이 저장해두고 이상하면 대조하는 게 중요합니다.

4단계 — 밤 10시 예약

브리핑 코드를 만들고, 왼쪽 시계 아이콘 → 트리거 추가로 “매일 22시”를 겁니다. 트리거로 도는 코드는 웹앱과 달리 저장만 하면 바로 반영됩니다.


6부. 아침 브리핑 (이것도 서버 없이!)

처음엔 서버의 AI가 만들었는데, 지금은 앱스크립트로 옮겼습니다. 무료이고, 숫자가 정확하고, 오히려 만들기도 쉬웠어요.

재료가 전부 공짜입니다

정보어디서비고
오늘 일정구글 캘린더같은 구글 계정이라 인증도 필요 없음
날씨Open-Meteo (무료 API)API 키 불필요
증시 지수시트의 GOOGLEFINANCE 함수API 키 불필요

캘린더가 특히 놀라웠는데, 서버에서 붙이려면 인증 파일을 만들어 옮기는 복잡한 과정이 필요했던 게 앱스크립트에서는 코드 한 줄입니다. 구글 것끼리는 그냥 통해요.

💡 자동화하고 싶은 서비스가 있다면 API(프로그램용 통로)가 있는지 먼저 확인하세요. 저는 원래 다른 캘린더를 쓰다가 이것 때문에 구글 캘린더로 옮겼습니다. 도구를 바꾸는 게 코드로 우회하는 것보다 빠릅니다.

여기서 겪은 시행착오 4가지

① 지역 이름이 엉뚱한 곳으로 인식됨 — 날씨 서비스에 동네 이름을 넣었더니 전혀 다른 지역이 나왔습니다. 위도·경도 좌표로 지정하니 정확해졌어요. 지명은 서비스마다 해석이 달라서, 좌표가 확실합니다.

①-2 무료 서비스가 구글 서버를 차단함 — 내 컴퓨터에서는 잘 되던 날씨 서비스(wttr.in)가 앱스크립트에서만 “접속 불가”로 실패했습니다. 개인용 무료 서비스는 구글 서버처럼 여럿이 공유하는 IP의 접속을 막는 경우가 있어요. 프로그램용으로 설계된 무료 API(Open-Meteo) 로 바꾸니 해결됐습니다. 무료 재료를 고를 땐 “브라우저용”이 아니라 “프로그램용”인지를 보세요.

② 증시 수식이 계속 실패 — 앱스크립트가 시트에 수식을 쓰고 곧바로 같은 실행에서 결과를 읽으려 했더니, 계산이 비동기라 아무리 기다려도 안 나왔습니다. 해결은 발상 전환이었어요. 수식을 시트에 계속 살려두면, 다음 실행 때는 이미 계산된 값을 바로 읽습니다. 첫 실행만 실패하고 그다음부터는 항상 성공.

③ 브리핑이 엉뚱한 시간에 옴 — 예약 시간이 세계표준시 기준이었습니다. 한국은 9시간 빠르니 계산해서 넣어야 해요. 그리고 앱스크립트 트리거는 정각이 아니라 ±15분 안에서 실행됩니다. 이건 구글 사양이라 받아들여야 합니다.

숫자를 행동으로 바꿔주기

날씨 섹션에서 제일 신경 쓴 부분입니다.

  • ❌ “강수확률 60%”
  • ✅ “오후에 비 소식이 있어요. 우산 챙기세요 ☂️”

숫자를 그대로 주면 아침에 해석할 여유가 없어요. if 문 몇 개로 기온·강수확률을 준비물 문장으로 바꿨습니다. AI 없이도 충분히 “비서 같은” 문장이 나옵니다.


7부. 대화 봇 — 여기서만 서버가 등장합니다

“물어보면 답하는” 기능은 앱스크립트로는 어렵습니다. 언제 올지 모르는 메시지를 계속 듣고 있어야 하고, AI가 여러 단계로 생각할 시간도 필요하거든요. 그래서 AI 에이전트 프로그램을 서버에 올렸습니다.

1단계 — 서버 빌리기

클라우드 업체(Vultr, DigitalOcean 등)에서 제일 작은 사양을 고릅니다. 월 몇 천 원.

  • 운영체제: Ubuntu 최신
  • 위치: 한국 또는 일본
  • 만들면 IP 주소비밀번호가 나옴 → 메모

2단계 — 서버 접속

윈도우 검색창에 powershell → 파란 창에서:

ssh root@서버IP주소

처음엔 계속할지 물으면 yes, 그다음 비밀번호 입력.

비밀번호를 쳐도 화면에 아무것도 안 보이는 게 정상입니다. 별표도 안 뜹니다. 그냥 끝까지 치고 엔터.

업체 홈페이지의 웹 콘솔도 있지만 한글 키보드에서 특수문자가 깨지는 일이 잦습니다. PowerShell 접속이 훨씬 안정적이에요.

root@서버이름:~# 으로 바뀌면 성공.

3단계 — AI 에이전트 설치

저는 오픈소스 에이전트(헤르메스)를 썼습니다. 프로그램마다 명령이 다르니 공식 문서를 따라가되, 어떤 단계인지 알면 훨씬 수월합니다.

  1. 설치 스크립트 실행 — 문서의 설치 명령 복사·실행
  2. 초기 설정 — 어떤 AI를 쓸지(제공자)와 API 키 입력
  3. 텔레그램 연결 — 4부에서 만든 봇 토큰 입력 → 봇에게 말을 걸어 내 계정과 페어링
  4. 상시 실행 등록 — 서버가 재부팅돼도 자동으로 켜지게 (systemd 서비스)

여기까지 하면 텔레그램에서 봇에게 말을 걸 때마다 서버의 AI가 답합니다.

4단계 — AI API 키는 무료로

Google AI Studio에서 키를 받는데, 결제 수단을 연결하지 않으면 무료입니다. 하루 사용량 제한이 있지만 개인 비서 용도로는 충분했어요.

무료 티어는 입력 내용이 서비스 개선에 쓰일 수 있다는 조건이 보통 붙습니다. 민감한 내용을 다루신다면 약관을 확인하고 결정하세요. 그리고 무료의 숨은 장점 — 한도가 안전장치가 됩니다. 뭔가 잘못돼도 한도에서 멈추니까요.

5단계 — 봇이 지어내지 못하게 울타리 치기

써보니 AI는 모르는 걸 물으면 그럴듯하게 지어냅니다. 제가 안 가진 카드를 추천한 적도 있어요.

해결은 참고 문서였습니다. 카드 혜택 정리를 서버에 파일로 두고, 봇에게 이렇게 기억시켰어요.

카드 질문을 받으면 이 파일을 먼저 읽고 답해.
내 카드는 파일에 적힌 4장뿐이야. 다른 카드는 절대 추천하지 마.
마땅한 게 없으면 지어내지 말고 "없다"고 말해.

AI에게는 판단 재료를 주고, 지어낼 여지를 막는 것 — 이게 대화 봇을 쓸 만하게 만드는 핵심이었습니다.


8부. 5만원이 사라진 사건, 그리고 안전장치

대화 봇을 올리고 일주일 뒤, API 선불 크레딧 5만원이 다 떨어졌습니다. 브리핑은 멀쩡히 오고 있었는데요.

원인은 설정 한 줄 — 대화 기록이 한 번도 초기화되지 않는 설정이었습니다. 1부에서 말한 “AI는 매번 전체 대화를 다시 받는다” 때문에, 6일치 대화가 통째로 쌓인 채 메시지 하나에 15만 토큰씩 전송되고 있었어요. 설정을 고치니 요청당 토큰이 29분의 1로 떨어졌습니다.

이 사건에서 나온 안전장치 4가지:

  1. 예산 알림 — 클라우드 결제 설정에서 월 한도 + 50%·90% 메일. 5분이면 겁니다. 제일 먼저 하세요.
  2. 세션 리셋 — 대화 기록을 매일 자동 초기화. 중요한 기억은 별도 메모리에 남아서 봇이 나를 잊지 않습니다
  3. 키는 코드 밖으로 — 앱스크립트 “스크립트 속성” 같은 보관함에 두면, 코드를 남에게 보여줘도 키가 안 딸려감
  4. 실패를 보이게 — 실패하면 브리핑에 ”(○○ 불러오지 못했어요)” 한 줄이 남게. 조용한 실패가 제일 위험합니다. 눈에 보이는 고장은 금방 고치는데, 조용히 도는 문제는 다 새고 나서야 압니다

9부. 자주 막히는 곳

증상원인해결
서버 비밀번호 쳐도 반응 없음원래 화면에 안 보임그냥 치고 엔터
웹 콘솔 글자 깨짐한글 입력기/특수문자PowerShell로 접속
코드 고쳤는데 그대로웹앱은 재배포 필요배포 관리 → 수정 → 새 버전
브리핑이 엉뚱한 시간에세계표준시9시간 계산 / 시간대 설정 확인
시트 수식이 스크립트에서 안 읽힘비동기 계산수식을 상주시키고 다음 실행에 읽기
날씨가 엉뚱한 지역지명 해석 오류좌표로 지정
내 PC에선 되는데 스크립트에선 접속 실패무료 서비스가 구글 서버 차단프로그램용 API로 교체
AI가 없는 걸 지어냄모르는데 답하려 함참고 문서 + “모르면 모른다고 해”
명령어 command not found앞 줄까지 같이 복사됨명령어 한 줄만 복사

마무리 — 시작한다면 이 순서로

1주차   텔레그램 봇 + 밤 가계부      (구글만으로, 무료)
          └ 여기서 "폰 알림이 자동으로 시트에 쌓이는" 재미를 느끼면 계속하게 됩니다
2주차   아침 브리핑 추가             (역시 무료)
그다음  대화 봇이 필요해지면 서버     (월 몇 천 원 + API 무료 한도)

한 번에 다 만들려 하지 마세요. 저도 6일차에 MVP 한 조각부터 시작했고, 지금 모습은 만들고 → 틀린 걸 발견하고 → 고치는 반복의 결과입니다. 완벽하지 않아도, 매일 밤 “오늘 얼마 썼는지” 알려주는 것만으로 충분히 쓸모 있었어요.

사례

클로드로 발견한 깃허브 스킬 소개 — plannotator/effective-html

2026-08-02

와이어프레임, 프로토타입, 다이어그램 등 결과물을 HTML로 만들 때 참고할 수 있는 에이전트 스킬 모음입니다. html, design-artifact, html-wireframe, html-prototype, html-plan, html-diagram 여섯 개 스킬로 나뉘어 있는데, 예를 들어 wireframe 스킬은 일부러 저해상도로 미완성 느낌을 유지해서 리뷰어가 디테일이 아니라 구조에 집중하게 만들고, prototype 스킬은 실제 동작하는 하나의 플로우와 관련 상태들을 구현하는 식으로 스킬마다 역할이 명확히 나뉘어 있는 점이 인상적이었습니다.

npx skills add plannotator/effective-html로 전체 설치가 가능하고, --skill html-diagram처럼 필요한 스킬만 골라 설치할 수도 있어서 프로젝트 성격에 맞게 가볍게 가져다 쓰기 좋아 보입니다. 저는 이 스킬을 활용해서 이번 지피터스 종합사례글 발표자료도 HTML로 만들어봤는데, 확실히 구조 잡기가 한결 수월했습니다.

HTML로 보고서·설명자료·아키텍처 다이어그램까지 만들어야 하는 작업이 잦으신 분들은 한 번쯤 살펴보시길 추천드립니다!

참고 링크

계획

브리핑 봇 세부 실행계획 (하루 단위)

2026-07-27

이 문서는 뭔가요

PRD에서 정한 기능들을, 하루에 하나씩 손댈 수 있는 크기로 잘게 쪼갠 실행계획입니다. 로드맵의 2~4주차를 이 문서로 실제로 실행합니다.

✅ 2026-08-01 기준 — 2주차·3주차 제작은 전부 완료. 열흘치로 쪼개뒀던 일이 실제로는 7/29~7/30 이틀에 끝났습니다. 모닝·밤이 둘 다 앱스크립트로 통일돼 있어서 같은 패턴을 두 번 쓰면 됐던 게 컸어요. 남은 건 4주차(회고·발표·전자책)뿐입니다.

조사 결과 — 요즘 제일 쉬운 방법 (2026-07 기준)

이미 갖춘 조합(Vultr 서버 + cron + 텔레그램 봇 + Google AI Studio/Gemini)을 그대로 쓰는 게 지금도 제일 쉬운 길로 확인됨. 새로 배울 필요 없음.

  • 에러 알림: 파이썬에서 try/except로 감싸고, except 블록 안에서 텔레그램 Bot API로 메시지 하나 보내는 방식이 지금도 제일 흔하고 간단한 방법. (참고: python-telegram-bot 에러 핸들링 위키)
  • 가계부 카테고리 자동분류: 별도 AI 모델을 새로 학습시킬 필요 없이, Google Apps Script에서 Gemini API를 호출해 “이 지출을 [카테고리 목록] 중 하나로 분류해줘”라고 프롬프트로 시키는 게 2026년 기준으로도 가장 쉬운 방법으로 확인됨. (참고: Gemini Pro API + Apps Script 카테고리 분류 예제)

→ 결론: PRD에서 이미 잡은 방향이 맞음. 아래 계획은 이 방식 그대로 하루 단위로 쪼갠 것.

2주차 · 모닝 브리핑 완성 — ✅ 완료

일차할 일상태
1일차모닝 스크립트를 try/catch로 감싸고, 실패 시 텔레그램 알림✅ 완료
2일차보유 종목 리스트 확정 + 모닝에 고정 반영✅ 완료
3일차지수 요약 확인 + 일정 파트 미니멀 포맷으로 정리✅ 완료
4일차증시시황 파트도 미니멀 포맷으로 통일✅ 완료
5일차3일 연속 테스트 (매일 아침 받아보며 확인)✅ 매일 수신 중

3주차 · 밤 브리핑 완성 — ✅ 완료 (2주차와 같이 끝냄)

2026-07-27 업데이트: 가계부 웹훅·밤브리핑 코드를 직접 확인한 결과, 카테고리 자동분류·월별 합계·AI 분석·텔레그램 발송이 이미 다 구현돼 있었음. 2026-08-01 업데이트: 아래 항목이 7/29~7/30에 2주차 항목과 함께 전부 완료됨. 모닝·밤이 둘 다 앱스크립트라 같은 패턴을 두 번 쓰면 돼서, 주차를 나눠둘 이유가 없었음.

일차할 일상태
1일차노출됐던 텔레그램 토큰·Gemini 키 재발급 + Script Properties로 이전✅ 완료
2일차밤브리핑()try/catch로 감싸고 실패 시 텔레그램 알림✅ 완료
3일차메시지 포맷을 미니멀 스타일로 다듬기 (이모지 줄이기)✅ 완료
4일차카테고리 합계를 “이번주” 기준으로도 표시✅ 완료 (월 누적 + 주간)
5일차밤 브리핑 연속 테스트 + 에러 알림 동작 확인✅ 매일 수신 중

4주차 · 완성·회고·발표

일차오늘 할 일완료 기준
1일차지난주부터 매일 받아본 모닝·밤 브리핑 중 어색하거나 틀린 부분 메모 모으기고칠 목록이 정리됨
2일차메모한 것 중 쉬운 것부터 하나씩 수정어제 메모한 것 중 절반 이상 해결
3일차4주 전(자동화 하나 없던 나) vs 지금(매일 브리핑 받는 나) 비교 회고 정리회고 초안 한 편 완성
4일차실제로 받은 브리핑 메시지 캡처 모으기 + 발표자료·강사 제출용 요약 정리캡처 3개 이상 + 요약 초안 완성
5일차전자책용 정리 마무리전자책 파트 초안 완성

아직 안 정해진 것 (TBD) — 전부 닫힘

  • 실제 보유 종목 리스트 → 확정·반영 완료
  • 기존 앱스크립트가 정확히 뭘 하는지 → 확인 완료
  • 카테고리 이름 목록 확정 → 확인 완료 (이미 고정돼 있었음)

다음 (2026-08-01 시점)

제작이 계획보다 열흘 가까이 앞서 끝나서, 남은 2주를 다시 계획해야 하는 상태입니다. 후보:

  • 4주차 항목(회고·발표자료·전자책)을 앞당겨 시작하기
  • 로드맵에서 시간 부족으로 뺐던 트랙2(글 검증) 를 다시 넣기
  • 브리핑 내용이 실제 데이터와 맞는지 스스로 점검하는 절차 붙이기 (PRD “여유되면” 항목)
PRD

브리핑 봇 PRD

2026-07-27

이 문서는 뭔가요

로드맵에서 “2주차·3주차에 만들기”로 정한 걸, 실제로 손댈 수 있게 구체화한 문서입니다.

✅ 2026-08-01 기준 — 여기 적힌 기능은 전부 구현 완료. 에러 알림, 관심 종목 고정, 미니멀 포맷 통일, 주간 카테고리 합계, 그리고 아래 보안 메모의 키 재발급·속성 저장 이전까지 모두 끝났습니다. 아래 본문은 “무엇을 왜 만들기로 했는지”의 기록으로 남겨둡니다.

뭘 · 왜 · 누구를 위해 (로드맵에서 그대로)

  • : 텔레그램 모닝/밤 브리핑 봇(헤르메스) 고도화 — 종목 리스트 고정, 가계부 카테고리 자동분류, 포맷 통일, 에러 알림 추가
  • : 지금은 브리핑에 필요한 정보가 빠지거나 지저분하게 나올 때가 있어서, 매일 실제로 믿고 받아볼 수 있게
  • 누구를 위해: 부기부기 본인 (매일 아침·밤 받아보는 개인 비서봇)

주차별 목표

  • 2주차 목표: 모닝 브리핑 완성
  • 3주차 목표: 밤 브리핑 완성

기능 명세

1. 관심 종목 리스트 (모닝 · 주가시황) — ✅ 완료

  • 보유 종목 기준 고정 리스트 사용
  • TBD — 나중에 실제 보유 종목 알려주기로 함확정·반영 완료. 모닝 브리핑에 매번 같은 종목이 빠짐없이 나옴
  • 기존처럼 나스닥/S&P500 등 지수 요약도 유지

2. 가계부 카테고리 자동분류 (밤 · 가계부) — ✅ 완료

  • 확정: 이미 구현되어 있음 — 새로 만들 필요 없음. 가계부 자동기록 웹훅(Apps Script)에 키워드 기반 RULES로 카테고리 자동분류가 이미 동작 중. 카테고리 목록도 고정돼 있음 (저축/대출/보험비/교통비/사치비/취미비/의료/주거비·통신비/일반생활비/식비·카페/기타 등).
  • 밤브리핑 스크립트에서 카테고리별 월 누적 합계(catSum)도 이미 메시지에 포함되고 있음.
  • 남은 실제 작업: (1) 밤 브리핑에 에러 알림 추가, (2) 메시지 포맷을 미니멀 스타일로 다듬기둘 다 완료.
  • 추가로, 월 누적만 있던 카테고리 합계에 주간 기준 합계도 넣었음.

3. 메시지 포맷 (모닝+밤 공통) — ✅ 완료

  • 확정: 미니멀 스타일 — 이모지 최소화, 섹션 구분은 굵은 글씨나 짧은 구분선 정도로 담백하게
  • 예시 (모닝 일정 파트):
    일정
    - 09:00 미팅
    
    증시
    나스닥 +0.8%
    

4. 에러 알림 — ✅ 완료 (모닝·밤 둘 다)

  • 확정: 실패했을 때만 텔레그램 메시지로 알림 (평소엔 조용히, 문제 생겼을 때만 “오늘 모닝 브리핑 실패했어요” 식으로)
  • 구현 방식: 브리핑 스크립트를 try/except로 감싸서, 실패 시 텔레그램 봇 API로 알림 메시지 전송 — 새 서비스 없이 지금 스크립트에 몇 줄만 추가하면 됨

완성 기준

  • 2주차: 3일 연속 모닝 브리핑에서 종목 리스트·포맷·에러 알림이 모두 정상 작동
  • 3주차: 3일 연속 밤 브리핑에서 카테고리 자동분류·포맷·에러 알림이 모두 정상 작동

어떻게 만드는 게 제일 쉬운가 (조사 결과, 2026-07 기준)

지금 있는 조합(Vultr 서버 + cron + Google AI Studio/Gemini)을 그대로 쓰는 게 제일 쉬움. 새 기술을 배울 필요 없음.

  • 에러 알림: 스크립트를 try/except로 감싸고, 실패 시 텔레그램 Bot API로 메시지 하나 보내는 게 지금 가장 흔하고 간단한 방식. 별도 모니터링 서비스 불필요.
  • 카테고리 자동분류: 별도 ML 모델을 학습시킬 필요 없이, 이미 쓰는 Gemini에게 프롬프트로 “이 지출을 [카테고리 목록] 중 하나로 분류해줘”라고 시키는 게 제일 쉽고 요즘 제일 많이 쓰는 방법. 카테고리 추가/수정도 프롬프트 문구만 고치면 됨.

아직 안 정해진 것 (열린 질문) — 전부 닫힘

  • 실제 보유 종목 리스트 → 확정·반영 완료
  • 기존 앱스크립트가 정확히 뭘 하는지 → 확인 완료: 수집+분류 다 함
  • 카테고리 이름 목록 확정 → 확인 완료: 이미 고정돼 있음 (위 항목 참고)

보안 메모 (2026-07-27 발견 → ✅ 조치 완료)

  • 가계부 웹훅/밤브리핑 Apps Script에 텔레그램 봇 토큰과 Gemini API 키가 코드에 직접 하드코딩돼 있었음.
  • 조치: (1) 두 키 재발급 완료, (2) Apps Script의 속성 서비스(Script Properties)로 이전 완료 — 이제 코드에 평문으로 남지 않음.
Chapter

3주차

사례

테스트는 다 통과하는데 실제로는 안 되는 이유 — 알림 한 줄이 알려준 것

2026-08-04

가계부 자동기록에서 며칠째 안 풀리던 문제가 있었어요. 원인을 찾고 보니 코드가 아니라 제가 데이터를 안 보고 있었던 게 문제였습니다. 같은 실수를 피하시라고 과정을 그대로 적어둡니다.

만들어둔 것

카드로 결제하면 폰에 알림이 오죠. 그 알림을 MacroDroid라는 앱이 가로채서 구글 스프레드시트로 보냅니다. 앱스크립트가 그걸 받아서 날짜·금액·카드·상점을 뜯어내 가계부에 한 줄씩 쌓아요.

여기에 칸이 하나 더 있습니다. “간편결제” 칸이요. 카카오페이로 결제했는지, 네이버페이로 했는지 기록하는 곳입니다.

이게 왜 필요하냐면, 제가 쓰는 카드 하나가 간편결제로 결제할 때만 10% 할인을 해주거든요. 그것도 아무 페이나 되는 게 아니라 카카오·네이버·토스 같은 정해진 것만요. 삼성페이는 대상이 아닙니다.

그러니 “이 결제를 어떤 페이로 했는가”를 기록해야 할인을 제대로 계산할 수 있습니다.

증상

카카오페이로 결제를 해도 그 칸이 계속 비어 있었습니다.

날짜도 금액도 카드도 다 잘 들어가는데 간편결제 칸만 텅 비어요. 그래서 브리핑에서 할인 계산이 안 됐습니다.

1차 가설 — 그리고 헛다리

처음엔 병합 로직을 의심했습니다.

페이로 결제하면 알림이 여러 개 옵니다. 카드사에서 하나, 페이 앱에서 하나. 이걸 각각 다른 줄로 기록하면 지출이 두 배로 잡히니까, 90초 안에 같은 금액이 또 들어오면 같은 결제로 보고 한 줄에 합치는 코드를 짜뒀어요.

“이 병합 과정에서 페이 정보가 날아가는구나” 싶었습니다.

그래서 테스트를 만들었습니다. 알림이 오는 순서를 바꿔가며(카드사 먼저 / 페이 먼저) 8가지 상황을 돌려봤어요.

✅ 카드사가 먼저 와도 카드=신한 기록됨
✅ 카드사가 먼저 와도 뒤이은 페이 알림이 간편결제 칸을 채움
✅ 페이가 먼저 와도 뒤이은 카드사 알림이 카드 칸을 채움
...
✅ 전부 통과

전부 통과했습니다. 코드는 멀쩡했어요.

그런데 실제로는 안 됩니다. 여기서 한참 막혔습니다.

실제 알림을 열어보다

결국 폰 알림창을 캡처해서 들여다봤습니다. 카카오페이로 결제한 순간 알림이 세 개 와 있더군요.

보낸 앱내용
1하나Pay(결제) 59,100원 / ○○상회 / 신용(일시불) / 08.04 00:20 / 누적이용금액 …
2하나카드59,100원 결제 / 하나카드 | ○○상회(일시불)
3카카오페이결제가 완료되었어요 / ○○상회에서 59,100원을 결제했어요.

3번을 다시 보세요.

“카카오페이”라는 글자가 어디에도 없습니다.

“카카오페이”는 알림 목록에서 앱 이름 자리에만 표시되고, 실제 제목·본문에는 안 들어갑니다. MacroDroid가 제목과 본문만 보내면, 앱스크립트 입장에서는 이게 카카오페이가 보낸 건지 알 방법이 없어요.

제 코드는 이렇게 찾고 있었거든요.

if(/카카오페이|kakaopay/i.test(s)) return '카카오페이';

이름으로 찾는데 이름이 없으니 못 찾는 게 당연했습니다.

왜 테스트는 통과했나

여기가 제일 뼈아픈 부분입니다.

테스트를 만들 때 알림 문구를 제가 상상해서 적었어요. “카카오페이 결제 59,100원” 뭐 이런 식으로요. 당연히 “카카오페이”가 들어 있었고, 그러니 통과했습니다.

테스트가 통과했다는 건 “내가 가정한 상황에서 코드가 맞게 돈다”는 뜻이지, “현실에서 돈다”는 뜻이 아닙니다.

가정이 틀리면 테스트도 같이 틀립니다. 오히려 “테스트 통과”라는 초록불 때문에 코드를 더 오래 의심하지 않았어요.

해결

이름이 없으면 말투로 찾으면 됩니다.

카카오페이 알림은 문장이 독특해요. “결제가 완료되었어요”, “○○에서 N원을 결제했어요” — 다른 카드사 알림은 이렇게 안 씁니다.

// ① 이름이 직접 적혀 있으면 그걸로 (가장 확실)
if(/카카오\s?페이|kakao\s?pay/i.test(s)) return '카카오페이';
// ... 다른 페이들 ...

// ② 이름이 없으면 앱 특유의 문구로
if(/결제가\s*완료되었어요/.test(s) || /에서\s*[\d,]+\s*원을\s*결제했어요/.test(s))
  return '카카오페이';

이름을 먼저 보고, 없을 때만 문구로 판단하는 순서로 뒀습니다. 이름이 확실하니까요.

그리고 이번엔 실제 알림 원문 세 개를 그대로 넣어서 다시 테스트했습니다.

✅ 본문에 "카카오페이" 가 없어도 문구로 판별
✅ 하나Pay(카드사 앱) 알림은 간편결제로 오인하지 않음
✅ 3개 알림이 한 행으로 합쳐짐 → 카드=하나 / 간편결제=카카오페이
✅ 누적이용금액을 결제액으로 잘못 잡지 않음

지어낸 문구가 아니라 진짜 알림으로 검증하니 그제서야 마음이 놓였습니다.

덤으로 알게 된 것

알림 1번을 보면 누적이용금액 284,885원이라는 숫자가 있죠. 결제액은 59,100원인데요.

숫자만 뽑으면 이 둘이 섞입니다. 그래서 코드에는 이런 장치가 있어요.

// 숫자 앞 8글자를 보고 '누적·잔액·한도' 같은 말이 있으면 건너뜀
var b = t.substring(Math.max(0, m.index-8), m.index);
if(/누적|누계|잔액|한도|합계||이용금액/.test(b)) continue;

예전에 비슷한 사고가 한 번 있었어요. 어떤 은행 알림에 “누적금액은?” 이라는 문구가 있었는데, 거기 들어간 적금 두 글자가 저축 분류 규칙에 걸려서 그 카드 결제가 전부 ‘저축’으로 잡힌 적이 있습니다. 지출에서도 빠지고 카드 실적에서도 빠졌죠.

알림 문구는 사람이 읽으라고 만든 것이지, 프로그램이 읽으라고 만든 게 아닙니다. 안내 문구 한 줄이 통계를 통째로 망가뜨릴 수 있어요.

남는 교훈

1. 안 될 때는 코드보다 데이터를 먼저 보세요.

저는 코드를 세 번 읽는 동안 실제 알림은 한 번도 안 봤습니다. 알림창을 캡처한 순간 5분 만에 끝났어요.

2. 테스트 데이터는 상상하지 말고 실물을 쓰세요.

지어낸 입력으로 만든 테스트는 내 가정을 다시 확인해줄 뿐입니다. 실제 문구를 한 번만 복사해 넣었어도 첫날 찾았을 거예요.

3. 화면에 보이는 것과 프로그램이 받는 것은 다릅니다.

사람 눈에는 “카카오페이”라고 크게 적혀 있죠. 하지만 그건 앱 이름 자리고, 프로그램에 넘어가는 텍스트에는 없었습니다. 눈에 보인다고 데이터에 있는 게 아닙니다.

아직 안 끝난 것

네이버페이는 아직 못 고쳤어요. 알림 문구를 아직 못 봤거든요. 이름으로만 찾고 있어서 카카오페이와 같은 문제가 있을 수 있습니다.

다음에 네이버페이를 쓰게 되면 그때 알림을 캡처해서 같은 방식으로 보강할 생각입니다. 이번엔 상상해서 짜지 않으려고요.

회고

이번 주엔 새로 만든 게 없다 — 고장 난 것들이 알려준 여섯 가지

2026-08-05

3주차엔 새 기능을 거의 안 만들었습니다. 대신 이미 돌아가던 것들이 하나씩 어긋나서, 그걸 쫓아다녔어요.

처음엔 다 따로 노는 문제인 줄 알았습니다. 글씨가 굵어지지 않고, 날씨를 못 가져오고, 예산이 이상하게 계산되고, 캘린더 일정이 반만 나오고. 그런데 다 고치고 나서 목록을 보니 공통점이 하나 있었습니다.

전부 에러가 안 났습니다. 프로그램은 멀쩡히 끝났고, 브리핑도 제시간에 왔어요. 다만 내용이 틀렸을 뿐입니다.

이번 주에 고친 것

증상제가 의심한 것실제 원인
브리핑 제목이 안 굵어짐태그를 잘못 썼나다른 파일에 같은 이름 함수가 있어서 서로 덮어씀
예산이 말도 안 되게 계산됨AI 프롬프트가 부실한가AI에게 나눗셈을 시킨 것 자체가 잘못
고쳤는데 그대로임저장이 안 됐나배포를 다시 안 함 (해당 파일과 무관해 보였는데도 필요)
카드 혜택 안내가 자꾸 빠짐조건식이 틀렸나흩어진 if문 — 조건 안 맞으면 조용히 사라짐
날씨 칸이 비어서 옴내 코드 문제인가외부 날씨 서버가 잠깐 죽음. 대비가 없었음
일정이 일부만 나옴캘린더 권한 문제인가”전부 읽는다”고 믿었던 함수가 기본 캘린더 하나만 읽고 있었음

하나씩 풀어볼게요.


1. AI에게 계산을 시키지 마세요

밤 브리핑 맨 아래에 “오늘 하루 총평”을 AI가 써줍니다. 이번 주에 이걸 좀 더 적극적으로 바꿨어요. 예산만 보지 말고 캘린더의 다음 주 일정까지 보고 조언하도록요.

“남은 예산은 넉넉하지만 다음 주에 모임이 두 건 있으니 지금 속도는 조금 빠릅니다” 같은 말을 해주면 좋겠다 싶었거든요.

그런데 결과물에 이런 문장이 나왔습니다.

예산 대비 62% 소진하셨네요!

숫자가 완전히 틀렸습니다. 예산 금액을 단위째 잘못 읽었어요. 쉼표가 들어간 금액을 앞자리만 보고 자릿수를 통째로 올려버린 겁니다. 예를 들면 이런 식입니다.

1,500,000원  →  AI가 읽기를 "1,500만 원"

처음엔 프롬프트에 “금액은 그대로 옮겨 적고 단위를 바꾸지 마세요”라고 못 박았습니다. 좀 나아졌지만 가끔 또 틀렸어요.

그러다 생각을 바꿨습니다. 왜 AI에게 계산을 시키고 있지?

지금은 이렇게 합니다. 나눗셈·퍼센트·남은 일수 같은 건 코드가 전부 미리 계산해서, AI에게는 완성된 문장으로 건네줍니다.

// 코드가 계산해서 문장으로 만들어 건넴
var 하루가능 = 남은일수 > 0 ? Math.max(0, 예산 - 누적) / 남은일수 : 0;
if (남은일수 > 0) lines.push('남은 예산을 남은 일수로 나누면 하루 ' + won(하루가능) + ' 꼴');

// 캘린더 일정도 목록으로 정리해서 건넴
if (events.length) {
  lines.push('앞으로 7일 안에 예정된 일정 ' + events.length + '건:');
  events.forEach(function (e) { lines.push('  · ' + e); });
}

AI는 이 문장들을 받아서 말투를 입히고 맥락을 엮는 일만 합니다. 숫자를 만들지 않아요.

잘하는 걸 시키세요. AI는 “다음 주에 약속이 있으니 지금 속도가 빠르다”는 판단은 잘합니다. 하지만 자릿수 큰 나눗셈은 계산기가 훨씬 잘해요. 계산은 코드, 해석은 AI.

이건 1주차에 쓴 역할 분담 이야기와 같은 결론인데, 그땐 머리로만 알았고 이번엔 데여서 배웠습니다.


2. 파일을 나눠도, 이름은 한 방에 삽니다

브리핑에서 ━ 오늘 지출 ━ 같은 제목 줄을 굵게 만들고 싶었습니다. 텔레그램은 <b> 태그를 지원하니까 간단할 줄 알았어요.

코드를 넣었습니다. 안 굵어집니다. 태그를 확인했습니다. 맞습니다. 테스트했습니다. 통과합니다. 그런데 실제 메시지는 안 굵어져요.

원인은 다른 파일에 있었습니다.

제 앱스크립트 프로젝트에는 파일이 여러 개 있습니다. 아침 브리핑, 밤 브리핑, 카드 추적… 파일을 나눠놨으니 서로 독립적이라고 생각했어요.

아닙니다. 앱스크립트는 프로젝트 안의 모든 파일이 하나의 이름표 공간을 씁니다. 파일이 달라도 같은 이름의 함수가 두 개 있으면, 하나가 조용히 다른 하나를 덮어씁니다. 경고도 에러도 없습니다.

아침브리핑.gs  →  function _볼드적용(text) { ... }   ← 아침용 규칙
밤브리핑.gs    →  function _볼드적용(text) { ... }   ← 밤용 규칙

결과: 둘 중 하나만 살아남음. 밤 브리핑이 아침용 규칙으로 돌아감.

고치는 건 5초였습니다. 밤 브리핑 쪽 이름을 _밤_볼드적용으로 바꿨어요. 문제는 원인을 찾는 데 한참 걸렸다는 겁니다. 저는 계속 밤 브리핑 파일만 들여다보고 있었거든요.

그 뒤로는 파일을 고칠 때마다 프로젝트 전체에서 같은 이름이 또 있는지 훑는 습관을 들였습니다.

폴더로 나눠놨다고 격리된 게 아닙니다. 그 도구가 실제로 이름을 어떻게 취급하는지를 확인해야 합니다.


3. “고쳤는데 왜 그대로지” — 배포는 프로젝트 전체를 찍습니다

텔레그램으로 ”○○ 다 했어”라고 보내면 일정 시트에서 그 항목을 완료 처리하는 기능이 있습니다. 이게 갑자기 못 알아듣기 시작했어요.

관련 파일을 고쳤습니다. 저장했습니다. 그대로입니다.

이때 제가 잘못 판단했습니다. “이 파일에는 외부 요청을 받는 입구(doPost)가 없으니 재배포는 필요 없다”고 생각했어요. 입구가 있는 파일만 다시 배포하면 되는 줄 알았습니다.

틀렸습니다.

웹앱을 배포하면 그 순간의 프로젝트 전체가 통째로 사진처럼 찍힙니다. 외부에서 요청이 들어오면 지금 편집기에 있는 코드가 아니라 찍혀 있는 그 사진 속 코드가 돌아갑니다. 입구는 한 파일에 있지만, 그 입구가 불러 쓰는 다른 파일들도 전부 사진에 같이 들어가 있는 거예요.

확인 방법은 단순했습니다. 실제로 요청을 한 번 보내보니 제가 이미 지운 옛날 문구가 그대로 돌아왔습니다. 편집기의 코드와 실제로 도는 코드가 다르다는 증거였죠.

편집기에서 저장  →  트리거로 도는 기능(정해진 시각에 실행)은 바로 반영 ✅
                 →  웹앱으로 도는 기능(외부 요청 처리)은 재배포해야 반영 ❗

앱스크립트 기준으로는 배포 관리 → 수정(연필) → 버전을 “새 버전”으로 → 배포까지 해야 합니다. 새 배포를 만들면 주소가 바뀌니, 반드시 기존 배포를 수정해야 하고요.

1주차에 로컬·깃허브·배포 3단계를 헷갈렸던 글을 썼는데, 도구만 바뀌었지 같은 함정에 또 빠진 셈입니다. “저장했다”와 “돌아가고 있다”는 다릅니다.


4. 매달 코드를 고쳐야 한다면, 설계가 틀린 겁니다

카드마다 “전달에 얼마 이상 썼으면 이번 달 혜택을 준다”는 조건이 있습니다. 그래서 브리핑이 전월 실적을 알아야 해요.

처음엔 코드에 숫자를 적어뒀습니다.

var 전월실적 = { 'A카드': 800000, 'B카드': 1200000 };   // ← 매달 손으로 고쳐야 함

이걸 보고 바로 말씀하신 게 있었습니다.

“그럼 다음 달엔 다시 앱스크립트를 고쳐야 하는 거잖아. 다른 방법 없어?”

맞는 지적이었습니다. 매달 코드를 여는 건 언젠가 반드시 까먹을 일이고, 까먹으면 브리핑이 조용히 틀린 소리를 하게 됩니다.

그래서 「카드설정」이라는 시트 탭을 하나 만들었습니다. 거기에 월별로 숫자를 적어두면 코드가 읽어가요.

년월A카드B카드메모
2026-07800,0001,200,000카드앱에서 확인
2026-08

숫자를 바꾸는 건 시트에 타이핑이고, 코드는 손대지 않습니다. 값이 비어 있으면 알아서 자동 집계로 넘어가게도 해뒀어요.

자주 바뀌는 값은 코드가 아니라 데이터입니다. 코드에 박아넣으면 값을 바꿀 때마다 프로그램을 고쳐야 하고, 그건 매번 새로 망가질 기회가 생긴다는 뜻입니다.


5. 흩어진 if문은 반드시 빠집니다

카드 혜택 안내가 계속 불완전했습니다. 분명히 조건을 만족했는데 브리핑에 그 얘기가 없어요.

코드를 보니 이렇게 생겼습니다.

if (조건A) 팁.push('A 혜택 이렇게 쓰세요');
if (조건B) 팁.push('B 혜택 남았어요');
if (조건C) 팁.push('C 혜택 곧 끝나요');
// … 이런 게 여기저기 흩어져 있음

문제가 보이시나요? 조건이 안 맞으면 아무 말도 안 합니다. “조건이 안 맞아서 못 알려드립니다”조차 안 나와요. 그냥 사라집니다.

게다가 혜택이 늘어날 때마다 if문을 새로 붙여야 하는데, 붙이는 걸 깜빡하면 그 혜택은 영원히 안내되지 않습니다. 그리고 깜빡했다는 사실조차 모르죠.

그래서 방식을 바꿨습니다. 혜택을 표 하나에 전부 적어놓고, 코드가 그 표를 처음부터 끝까지 훑게 했어요.

var 혜택표 = [
  { 이름: '온라인쇼핑 5%', 종류: '횟수', 쓴것: 2, 한도: 2,
    조건: '건당 2만원 이상',
    대안: '같은 결제는 B카드 야간 할인으로 돌리세요' },
  // … 스무 개 남짓
];

혜택표.forEach(function (b) {
  if (다썼는지(b))      끝난것.push(b);      // 소진 → 대안까지 같이 안내
  else if (임박한지(b)) 임박한것.push(b);    // 한 번 남음 → 미리 알림
});

바뀐 점이 세 가지입니다.

  1. 빠지지 않습니다. 표에 있는 건 전부 검사받습니다.
  2. 다 쓴 혜택도 알려줍니다. “이건 이번 달 끝났어요”라고 말해주고, 대신 쓸 카드까지 같이요.
  3. 혜택 추가가 표에 한 줄 넣는 일이 됐습니다. 코드 로직은 안 건드립니다.

여기서 재밌었던 건 대안끼리 서로 참조하게 만든 부분입니다. “A가 끝났으니 B를 쓰세요”라고 안내하려는데 B도 이미 다 썼으면 소용없잖아요. 그래서 대안으로 지목한 혜택이 살아있는지도 같이 확인하게 했습니다.

조건문이 흩어져 있으면 “빠졌다”는 사실이 안 보입니다. 목록으로 만들어 전부 순회하면, 빠질 수가 없어요.


6. 하나뿐인 연결은 언젠가 끊깁니다

어느 날 아침 브리핑에 날씨가 비어서 왔습니다. “연결하지 못했습니다”라는 문구만요.

제 코드 문제가 아니었습니다. 쓰던 무료 날씨 서버가 그 시각에 잠깐 응답을 안 한 거예요. 제가 어쩔 수 있는 게 아닙니다.

어쩔 수 없다는 게 대비하지 않을 이유는 아니었습니다. 두 가지를 넣었어요.

1) 재시도  — 실패하면 1.5초 쉬고 다시, 최대 3번
             (일시적 끊김은 대부분 여기서 해결됩니다)

2) 대체 경로 — 3번 다 실패하면 다른 기관의 날씨 서버로

대체 서버는 주는 정보가 조금 달랐습니다. 강수 확률이 없고 강수 만 있어요. 그래서 무리해서 맞추지 않고, 가진 것만 정직하게 표시하게 했습니다. 없는 값을 지어내는 것보다 낫다고 봤어요.

외부 서비스는 언젠가 반드시 실패합니다. 그게 이상한 게 아니라 정상입니다. 재시도와 대안, 두 겹만 있어도 대부분의 아침은 무사합니다.


덤: 모르면 버리지 말고, 짐작하되 표시를 남기기

가계부가 상호명을 못 알아보면 전부 ‘기타지출’로 던져놨습니다. 나중에 보면 뭔지 알 수 없는 덩어리만 남죠.

두 가지를 넣었습니다.

첫째, 법인명 사전. 카드 명세서엔 브랜드가 아니라 법인명이 찍힙니다. 우리가 아는 이름과 달라요. 그래서 규칙에 하나도 안 걸렸던 겁니다.

var 상호별칭 = [
  { 법인: '○○○형제들',   브랜드: '배달앱' },
  { 법인: '○○모빌리티',   브랜드: '택시' },
  { 법인: '○○리테일',     브랜드: '편의점' },
];

판정 전에 법인명이 보이면 브랜드명을 슬쩍 덧붙여줍니다. 규칙은 그대로 두고 입력만 보강하는 방식이라, 기존 규칙을 하나도 안 건드렸어요.

둘째, 시간대로 짐작하기. 그래도 모르는 상호는 결제 시각으로 추측합니다. 점심·저녁 시간대면 식사일 가능성이 높으니까요. 평일과 주말은 다르게 잡았고요.

중요한 건 짐작한 티를 남긴 것입니다.

memo: (확실히_모름 ? '[분류확인] ' : (짐작했음 ? '[추정] ' : '')) + 원본알림

[추정]이 붙어 있으면 나중에 시트에서 걸러서 확인할 수 있습니다. 조용히 짐작하는 게 제일 나쁩니다. 맞았는지 틀렸는지 확인할 방법이 없어지니까요.


관통하는 하나

다시 처음 표로 돌아가면, 여섯 개가 전부 이렇습니다.

아무것도 터지지 않았습니다.

  • 볼드가 안 먹어도 → 메시지는 정상 발송
  • 배포가 안 돼도 → 웹앱은 200 응답
  • 혜택 안내가 빠져도 → 브리핑은 멀쩡히 도착
  • 예산 숫자가 틀려도 → 문장은 아주 자연스러움
  • 캘린더가 반만 읽혀도 → 일정 목록은 그럴듯함

에러 메시지가 뜨는 버그는 친절한 버그입니다. 어디가 아픈지 알려주니까요. 진짜 골치 아픈 건 정상처럼 보이는 틀린 결과입니다. 몇 주씩 모르고 지나갈 수 있어요.

그래서 이번 주엔 테스트를 많이 만들었습니다. 지금 22개가 돌아갑니다. 코드를 고칠 때마다 전부 한 번씩 실행해요.

passed=22  failed=0

재밌는 건, 테스트가 실패했을 때 원인이 코드가 아니라 제 테스트였던 적이 여러 번이라는 겁니다. 기댓값을 잘못 적었거나, 예전 이름으로 검사하고 있었거나. 그때마다 “코드를 고칠까, 테스트를 고칠까”를 매번 따져봐야 했습니다.

이것도 배운 것 중 하나였어요. 초록불이 떴다고 맞는 게 아니고, 빨간불이 떴다고 코드가 틀린 것도 아닙니다.

이번 주 남은 숙제

  • 다른 간편결제 수단은 아직 안 고쳤습니다. 카카오페이 때와 같은 이유일 수 있는데, 실제 알림을 아직 못 봤어요. 이번엔 상상해서 만들지 않으려고 기다리는 중입니다.
  • 모르는 상호 짐작 규칙은 이제 막 넣어서, [추정] 표시가 실제로 얼마나 맞는지 한 달쯤 지켜봐야 합니다.

3주차를 한 줄로 줄이면 이렇습니다.

“돌아가는 것처럼 보인다”와 “제대로 돌아간다” 사이에는 생각보다 넓은 강이 있다.

Chapter

4주차

사례

실제로 이렇게 옵니다 — 모닝·나이트 브리핑 데모

2026-08-13

한 줄 요약

매일 아침·밤으로 실제 받아보고 있는 텔레그램 브리핑을, 개인정보를 뺀 버전으로 공개해요.

⚠️ 모든 금액·카드사명·업무 세부내용은 예시/비공개 처리된 것이에요. 실제 거래처명·의뢰인명·사건 정보·특정 카드사명은 전부 가리거나 일반화했습니다.

🌙 밤 브리핑 — 가계부 정리

밤 브리핑 예시

카드를 긁으면 자동으로 기록되고, 밤 10시에 카테고리별 분류·카드 실적·놓친 혜택까지 정리돼서 와요. 실제 은행·카드사 이름은 “카드A~D”로, 거래처명은 “거래처 A”로 가렸어요.

☀️ 모닝 브리핑 — 하루 준비

모닝 브리핑 예시

일정·날씨·증시를 챙겨주는데, 업무 관련 세부 내용(의뢰인·사건 정보 등)은 민감한 개인정보라 통째로 비공개 처리하고 “진행 중인 업무 N건”으로만 요약해서 보여드려요.

왜 이렇게까지 가렸나

이 봇은 실제 업무·재무 데이터를 다루기 때문에, 브리핑 원문에는 거래처명·특정 금융사명·업무 관련 인명 등이 그대로 찍혀 나와요. 학습허브는 누구나 볼 수 있는 공개 사이트라, 공개하기 전에 아래 원칙으로 걸러냈어요.

  • 금액·비율·카테고리 같은 구조와 숫자는 그대로 두고
  • 실명·회사명·특정 기관명은 전부 익명화하거나 통째로 가리고
  • 업무 관련 항목은 “몇 건 진행 중”처럼 건수만 남기고 세부는 비공개

배운 것

  • 봇이 실제로 매일 잘 동작하고 있다는 걸 보여주는 것과, 그 안의 민감정보를 지키는 것은 별개의 문제예요. 둘 다 챙기려면 “공개용 버전”을 따로 만들어야 해요.
  • 이미지 하나를 공개하기 전에도 “이 안에 남 이름이나 회사명이 있나?”를 한 번 더 체크하는 습관이 필요하다는 걸 느꼈어요.
사례

돌아가는 코드를 갈아엎는 법 — 출력을 얼려놓고 구조만 바꾸기

2026-08-13

브리핑 봇을 3주 동안 키우다 보니 코드가 뒤엉켰습니다. 예산 구분을 추가하려고 함수를 열었더니, 그 함수가 시트를 읽고 → 계산하고 → 문장을 만들고 → 텔레그램으로 보내는 것까지 혼자 다 하고 있더군요. 어디를 고쳐야 할지 찾는 시간이 고치는 시간보다 길어졌습니다.

그래서 AI에게 물었습니다. “이거 클린 아키텍처로 바꾸고 싶어.”

교과서 그대로는 답이 아니었다

돌아온 첫 대답이 뜻밖이었어요. “교과서식 클린 아키텍처는 여기엔 안 맞습니다.”

정식 클린 아키텍처는 계층 4개에 의존성 규칙까지 갖춘 구조인데, 제가 쓰는 앱스크립트에는 그걸 지탱할 기능(모듈 분리 같은 것)이 없답니다. 억지로 흉내 내면 이득 없이 코드만 세 배가 된다고요.

대신 핵심 아이디어 하나만 가져왔습니다.

계산하는 코드와 바깥세상(시트·API·메신저)을 만지는 코드를 섞지 않는다.

식당에 비유하면 이렇습니다. 예전 코드는 한 사람이 주문받고 장 보고 요리하고 서빙까지 하는 가게였어요. 바뀐 구조는 역할을 나눈 가게입니다.

① 설정    = 레시피 수첩   (예산 금액, 분류 키워드 — 숫자만 고치는 곳)
② 코어    = 주방          (재료를 받아 요리만. 바깥 접촉 금지)
③ 어댑터  = 장보기·배달   (시트 읽기, API 호출, 메시지 전송)
④ 진입점  = 홀 매니저     ("장 봐와 → 주방에 → 배달 보내" 지시만)

실제 흐름으로 그리면 이렇습니다. 밤 브리핑 하나가 도는 순서예요.

시간 트리거 아침 8시 · 밤 10시 웹훅 요청 결제 알림 · 완료 처리 ④ 진입점 — 조립만 한다 밤브리핑() · doPost() · route() ③ 어댑터 — 읽기 시트 · 캘린더 · 날씨 API ① 설정 예산 · 키워드 카드 혜택표 ② 코어 — 순수 계산 집계 · 분류 판정 · 문장 만들기 시트·인터넷 접촉 금지 ③ 어댑터 — 쓰기·전송 sendTG() · 시트 기록 · 응답 텔레그램 도착 다섯 파일 모두 같은 구조 — 카드추적 · 밤브리핑 · 카드파트 · 모닝브리핑 · 업무일정 보라 = 순수 계산 · 청록 = 바깥 접근 · 회색 = 구조

이득은 명확합니다. 고장 나면 볼 곳이 반으로 줄어요. 숫자가 틀렸다 → 주방 문제. 메시지가 안 왔다 → 배달 문제. 예전엔 한 함수 안에서 둘 다 의심해야 했습니다.

진짜 문제: “고치다가 망가뜨리면?”

구조를 바꾼다는 건 결국 잘 돌아가는 코드를 갈아엎는다는 뜻입니다. 그리고 이 봇은 매일 아침저녁으로 실제로 쓰는 물건이에요.

더 무서운 건, 이런 개편에서 생기는 버그는 에러 없이 조용히 틀리는 종류라는 겁니다. 코드를 옮기다 한 줄이 미묘하게 달라져도 브리핑은 멀쩡히 도착합니다. 숫자만 슬쩍 다를 뿐이죠. 며칠씩 모를 수 있습니다.

여기서 배운 게 이번 사례의 핵심입니다. 골든 테스트라는 방법이에요.

골든 테스트: 출력을 얼려놓고 시작한다

순서가 중요합니다. 코드를 고치기 전에:

  1. 가짜 입력(거래 기록 배열, 가짜 캘린더 일정)을 준비하고
  2. 지금 코드가 만드는 출력(브리핑 전문)을 파일로 저장합니다 — 이게 “정답지”
  3. 그다음에 구조를 갈아엎고
  4. 같은 입력을 넣어 글자 하나까지 같은지 대조합니다
바꾸기 전:  입력 → [옛 코드] → 출력 저장 (정답지)
바꾼 후:    입력 → [새 코드] → 출력 = 정답지 ?
                                ├─ 같다  → 구조만 바뀐 것 ✅
                                └─ 다르다 → 어딘가 실수함, 몇 번째 줄인지 표시

실제로 대조는 “대충 비슷”이 아니라 바이트 단위로 했습니다. 공백 하나, 쉼표 하나 달라도 실패로 처리했어요. 리팩터링의 성공 기준은 “더 좋아졌다”가 아니라 “달라진 게 없다” 이니까요.

파일 다섯 개를 이 방식으로 하나씩 갈아탔고, 다섯 번 모두 정답지와 일치했습니다.

부수입: 테스트가 정직해졌다

구조를 나누고 나니 예상 못 한 이득이 따라왔습니다.

예전에는 계산 코드가 시트 읽기와 붙어 있어서, 테스트하려면 구글 시트 전체를 흉내(mock) 내야 했습니다. 그 흉내 장치가 진짜 코드보다 자주 깨졌어요.

지금은 주방(코어)이 배열만 받으니, 배열을 넣고 답을 확인하면 끝입니다. 흉내 낼 게 없어요. 심지어 이런 덫도 놓을 수 있게 됐습니다:

// 코어를 테스트할 때 시트 접근을 '지뢰'로 바꿔둔다
SpreadsheetApp = { getActiveSpreadsheet: () => { throw new Error('코어가 시트를 만지면 안 됨!'); } };

코어가 규칙을 어기고 몰래 시트를 만지면 테스트가 그 자리에서 터집니다. “분리했다”는 말을 믿는 게 아니라 어기면 터지게 만든 거예요.

남는 교훈

1. 리팩터링은 “정답지 저장”부터 시작하세요.

바꾸기 전 출력을 얼려두지 않으면, 바꾼 후에 “잘 된 것 같다”는 느낌만 남습니다. 느낌은 조용한 버그를 못 잡습니다.

2. 교과서 구조는 번역해서 쓰는 겁니다.

제 환경(앱스크립트, 혼자 쓰는 자동화)에 4계층은 과했습니다. 핵심 원리 하나만 가져와 축소판을 만드는 게, 형식을 다 갖추는 것보다 나았습니다. AI에게 “이 교과서 개념, 내 상황에는 어디까지가 적당해?”라고 묻는 게 유효했어요.

3. 한 번에 갈아엎지 마세요.

파일 다섯 개를 한꺼번에 바꿨다면 뭐가 잘못됐는지 못 찾았을 겁니다. 하나 바꾸고 → 정답지 대조 → 통과하면 다음. 지루하지만 이게 제일 빨랐습니다.

한계도 적어둡니다

골든 테스트는 정답지를 만들 때 밟은 길만 보증합니다. 가짜 입력이 안 지나간 구석의 실수는 못 잡아요. 그래서 자주 쓰는 기능 위주로 가짜 입력을 짰고, 나머지는 실제 브리핑을 받아보며 확인했습니다. “전부 검증했다”와 “주요 경로를 검증했다”를 구분해서 말하는 것도 이번에 배운 것 중 하나입니다.