// 활용 사례
Slack에서 자연어로 어제 매출 묻기
Slack 채널에 "어제 매출?"을 물으면 봇이 입금 합계로 답합니다. headless 거래내역 API와 Slack 봇을 잇는 방법을 정리했습니다.
월요일 아침마다 누군가 은행 사이트에서 주말 입금을 더한 뒤, 그 숫자를 Slack 채널에 옮겨 적습니다. 숫자를 묻는 사람과 확인하는 사람이 다르면 같은 확인이 하루에 여러 번 반복됩니다.
봇은 거래내역을 받아 채널의 질문에 답하고, 담당자는 수치의 기준과 예외를 정합니다. 이 글은 팀 채널 안에서 매출을 확인하는 흐름을 만들 때 필요한 연결과 운영 규칙을 설명합니다.
어떤 상황을 풀고 있나
- 매출을 매일 확인하는 사람: 대표, 재무 담당, 이커머스 운영자
- 빈도: 매일~매주. 정산일이 몰리면 하루에도 여러 번
- 현재 방식: 은행 사이트 로그인 → 거래내역 다운로드 → 엑셀에서 입금만 필터 → 슬랙에 복사
마지막 단계만 자동화하는 게 아니라 처음부터 끝까지 봇에게 맡깁니다. 받은 숫자는 어디로도 보내지 않고, 묻는 사람이 보는 슬랙 채널 안에서 답으로 마무리됩니다.
결과 - 한 화면
슬랙 채널에서:
@매출봇 어제 매출?
# 잠시 후 …
2026-05-26 입금 18건. 합계 ₩7,420,000.
가장 큰 입금은 ㈜에이클라이언트 ₩2,200,000.@매출봇 지난주 매출? 처럼 기간을 바꿔 물어도 같은 흐름으로 응답합니다.
1. 준비물
| 준비물 | 역할 | 비용 |
|---|---|---|
| headless API key | 거래내역 수집 | 무료로 시작 |
| 은행 자격증명 1건 | 입금이 찍히는 계좌 | - |
| Slack 앱 (봇 토큰) | 메시지 송수신 | 무료 |
| Node 18+ 런타임 | 봇 프로세스 | 로컬·서버 무관 |
자격증명은 한 번만 등록합니다. headless가 봉투 암호화로 보관하고, 수집 시점에만 메모리에서 복호화합니다.
curl -X POST https://api.h6s.ai/api/v1/credentials \
-H "Authorization: Bearer $H6S_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"providerCode": "CB_KB",
"authMethod": "ID_PW",
"credentials": { "loginId": "...", "loginPw": "..." }
}'
# → { "id": "cred_...", "credentialHealth": "UNKNOWN" }2. 핵심 코드
슬랙 Bolt 앱 하나가 멘션을 받아 headless에 거래내역을 요청하고, 입금만 더해 응답합니다.
import pkg from "@slack/bolt";
const { App } = pkg;
const API = "https://api.h6s.ai/api/v1";
const headers = {
Authorization: `Bearer ${process.env.H6S_API_KEY}`,
"Content-Type": "application/json",
};
// data-job 1건을 만들고 끝날 때까지 폴링한 뒤 결과를 돌려준다
async function fetchTransactions(start, end) {
const job = await fetch(`${API}/data-jobs`, {
method: "POST",
headers,
body: JSON.stringify({
providerCode: "CB_KB",
schema: "bank.transactions.cb.v1",
params: { dateRangeStart: start, dateRangeEnd: end },
}),
}).then((r) => r.json());
// kind 가 종료(Succeeded/Failed)가 될 때까지 폴링. 예제는 30초 가드만 - 프로덕션은 타임아웃·재시도를 더 둔다
let current = job;
for (let i = 0; current.kind === "InProgress" && i < 15; i++) {
await new Promise((r) => setTimeout(r, 2000));
current = await fetch(`${API}/data-jobs/${job.id}`, { headers }).then((r) => r.json());
}
if (current.kind !== "Succeeded") throw new Error(`job ${job.id} 미완료: ${current.kind}`);
const { data } = await fetch(`${API}/data-jobs/${job.id}/results`, { headers }).then((r) => r.json());
return data;
}
const app = new App({
token: process.env.SLACK_BOT_TOKEN,
signingSecret: process.env.SLACK_SIGNING_SECRET,
});
app.event("app_mention", async ({ event, say }) => {
const day = /지난주/.test(event.text) ? 7 : 1;
const end = new Date();
const start = new Date(Date.now() - day * 86400000);
const iso = (d) => d.toISOString().slice(0, 10);
const records = await fetchTransactions(iso(start), iso(end));
const deposits = records.filter((r) => r.amount > 0);
const total = deposits.reduce((s, r) => s + r.amount, 0);
const top = deposits.sort((a, b) => b.amount - a.amount)[0];
await say(
`${iso(start)}~${iso(end)} 입금 ${deposits.length}건. 합계 ₩${total.toLocaleString()}.\n` +
(top ? `가장 큰 입금은 ${top.description} ₩${top.amount.toLocaleString()}.` : "입금 없음."),
);
});
await app.start(3000);bank.transactions.cb.v1의 amount는 입금이 양수, 출금이 음수입니다. 어느 은행에서 받아도 identifier를 포함한 9개 필드가 같으므로 위 필터를 그대로 씁니다.
3. 배포
봇은 멘션을 기다리는 상주 프로세스입니다. cron이 아니라 늘 떠 있어야 합니다.
- 가장 단순한 길: 작은 서버나 컨테이너에
node bot.js상주 - secret 3개를 환경변수로:
H6S_API_KEY,SLACK_BOT_TOKEN,SLACK_SIGNING_SECRET - 첫 확인: 채널에 봇을 초대하고
@매출봇 어제 매출?한 줄
기본 Bolt 구성에서는 이벤트를 받자마자 200을 응답하고 핸들러를 비동기로 돌리므로 폴링이 길어도 슬랙이 재시도하지 않습니다. 다만 ack를 핸들러 완료 뒤로 미루는 환경(예: AWS Lambda 리시버, processBeforeResponse: true)이라면 이벤트를 먼저 ack 하고 결과를 client.chat.postMessage로 따로 보내는 편이 안전합니다.
4. 운영 - 한 달 뒤 무엇을 보는가
- 스크래핑 단계: 하루 한두 번 물으면 월 50단계 안쪽입니다. 무료 플랜의 월 수집 요청 한도(20건)를 넘기면 스탠다드로 올립니다
- 자격증명 만료: 응답이 비거나
failureCategory: CREDENTIAL이 보이면 콘솔에서 자격증명을 갱신합니다 - 타임존:
dateRangeStart/End는 날짜 단위입니다. "어제"의 경계가 KST 기준인지 한 번 확인합니다
5. 다른 곳에 연결하기
같은 골격에서 데이터 형식과 집계만 바꾸면 다른 자동화가 됩니다.
- 세금계산서 합계 →
hometax.tax-invoices.sales.v1 - 매출 추세를 시트에 누적 → 데이터베이스/스프레드시트로 append
- 정기 리포트 → 멘션 대신 cron으로 매일 아침 자동 전송
더 깊이
같은 골격을 다른 채널로 옮기려면 표준 MCP 클라이언트 연동부터 보면 됩니다. MCP 연동 문서에 설치와 인증이 정리돼 있습니다. 봇·Notion·Gmail처럼 받은 데이터를 내 화면이나 팀 채널에서 바로 확인하는 로컬 리포팅 패턴은 팀에 자동으로 리포트 돌리기에 모아 두었습니다.
봇이 한 번 응답하기 시작하면, 다음 단계는 묻기 전에 매일 아침 먼저 알려주는 쪽입니다. 멘션 핸들러를 cron으로 바꾸면 그대로 됩니다.