가계부 자동기록에서 며칠째 안 풀리던 문제가 있었어요. 원인을 찾고 보니 코드가 아니라 제가 데이터를 안 보고 있었던 게 문제였습니다. 같은 실수를 피하시라고 과정을 그대로 적어둡니다.
만들어둔 것
카드로 결제하면 폰에 알림이 오죠. 그 알림을 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. 화면에 보이는 것과 프로그램이 받는 것은 다릅니다.
사람 눈에는 “카카오페이”라고 크게 적혀 있죠. 하지만 그건 앱 이름 자리고, 프로그램에 넘어가는 텍스트에는 없었습니다. 눈에 보인다고 데이터에 있는 게 아닙니다.
아직 안 끝난 것
네이버페이는 아직 못 고쳤어요. 알림 문구를 아직 못 봤거든요. 이름으로만 찾고 있어서 카카오페이와 같은 문제가 있을 수 있습니다.
다음에 네이버페이를 쓰게 되면 그때 알림을 캡처해서 같은 방식으로 보강할 생각입니다. 이번엔 상상해서 짜지 않으려고요.