케이스 스터디
CASE STUDY — 운영

쇼핑몰 정산 자동화 — 맞는 건 세고, 안 맞는 건만 보여준다

도입 쇼핑몰 / 이커머스 셀러구축 6일
판매채널 정산 APIPostgresSlackn8n Schedule

채널별로 들어오는 주문·수수료·환불 데이터를 매일 밤 손으로 맞췄습니다. 숫자 하나만 어긋나도 처음부터 다시. 마감일에는 새벽 근무가 기본이었습니다.

무엇이 문제였나

데이터 자체는 모두 있었습니다. 문제는 그게 채널마다 다른 형식으로 흩어져 있었고, 맞추는 일이 온전히 사람의 몫이었다는 점입니다.

settlement.workflowLIVE
CH정산 수집MT규칙 검산DB원장 적재SL불일치만 보고

어떻게 풀었나

  • 판매 채널별 주문·정산 데이터를 매일 자동 수집
  • 수수료·환불·배송비를 규칙대로 대조 검산
  • 차이가 나는 항목만 따로 플래그 처리
  • 결과를 원장에 기록하고 요약을 슬랙으로 보고

핵심은 '사람이 전부 확인'에서 '예외만 확인'으로 바꾼 것입니다. 정상 건은 워크플로우가 처리하고, 사람은 플래그된 건만 봅니다.

검산 규칙 — 이 한 줄이 전부입니다

"규칙대로 대조"의 그 규칙이 정확히 무엇인지가 이 케이스의 핵심입니다. 채널이 무엇이든 형태는 같습니다.

판매가 − 수수료 − 배송비 − 환불액  =  우리가 계산한 지급예정액

차이(diff) = 채널이 통보한 지급예정액 − 우리가 계산한 지급예정액

  diff = 0        → 일치. 아무것도 하지 않는다
  diff > 0        → 채널이 더 준다고 함. 대개 수수료 할인·프로모션 정산
  diff < 0        → 채널이 덜 준다고 함. 반드시 사람이 봐야 하는 건

이 규칙을 SQL 한 줄로 옮기면 아래가 됩니다. 워크플로우의 Reconcile Yesterday 노드에 들어 있습니다.

SELECT channel, order_id, settled_on,
       sale_amount, fee_amount, shipping_amount, refund_amount,
       payout_reported,
       (sale_amount - fee_amount - shipping_amount - refund_amount) AS payout_expected,
       payout_reported
         - (sale_amount - fee_amount - shipping_amount - refund_amount) AS diff
FROM settlements
WHERE settled_on = (CURRENT_DATE - INTERVAL '1 day')::date
ORDER BY abs(payout_reported
         - (sale_amount - fee_amount - shipping_amount - refund_amount)) DESC;
ORDER BY abs(...) DESC가 실무에서 중요합니다. 금액 차이가 큰 순서로 정렬되므로, 시간이 없을 때 위에서부터 몇 건만 봐도 가장 큰 손실을 먼저 잡습니다. 부호와 무관하게 절댓값으로 정렬하는 이유는, 덜 받은 것뿐 아니라 더 받은 것도 나중에 회수당할 수 있기 때문입니다.

차이 임계값을 어떻게 정하나

1원 차이도 잡을지, 어느 정도는 넘길지 정해야 합니다. Flag Mismatches Only 노드 맨 위의 TOLERANCE_KRW 값입니다.

  • `0` — 1원이라도 다르면 전부 플래그합니다. 정확하지만, 채널이 원 단위로 반올림하는 경우 매일 수십 건이 뜹니다.
  • `1`~`2` (권장 시작값) — 반올림 오차만 흡수하고 나머지는 전부 잡습니다. 대부분의 셀러에게 이 범위가 맞습니다.
  • `100` 이상 — 알림이 너무 많을 때만. 이 값을 올리는 것은 '못 보고 넘어가는 금액'을 늘리는 것과 같습니다. 올리기 전에 왜 차이가 많이 나는지부터 확인하세요. 임계값 상향은 원인 해결이 아니라 증상 은폐입니다.

필요한 것

  • n8n — 자체 호스팅 또는 Cloud. 기본 노드만 씁니다.
  • Postgres 데이터베이스 하나 — 정산 원장 테이블 하나. 채널이 나중에 늘어나도 같은 테이블에 쌓입니다.
  • Postgres 자격증명 — n8n → Credentials → Postgres. Docker n8n + 호스트 DB라면 host.docker.internal.
  • Slack Bot User OAuth Tokenchat:write, channels:read.
  • 판매 채널의 정산 데이터 접근 수단 — 채널 API가 있으면 API를, 없으면 정산 엑셀을 내려받아 넣는 경로를 씁니다. 이 워크플로우는 첫 노드만 채널을 압니다.

1단계 — 정산 원장 테이블

CREATE TABLE IF NOT EXISTS settlements (
  channel         text    NOT NULL,   -- 판매 채널 이름
  order_id        text    NOT NULL,
  settled_on      date    NOT NULL,   -- 정산 기준일
  sale_amount     numeric NOT NULL DEFAULT 0,
  fee_amount      numeric NOT NULL DEFAULT 0,
  shipping_amount numeric NOT NULL DEFAULT 0,
  refund_amount   numeric NOT NULL DEFAULT 0,
  payout_reported numeric NOT NULL DEFAULT 0,  -- 채널이 통보한 지급예정액
  created_at      timestamptz NOT NULL DEFAULT now(),
  updated_at      timestamptz NOT NULL DEFAULT now(),
  PRIMARY KEY (channel, order_id, settled_on)
);
PRIMARY KEY를 (channel, order_id, settled_on) 세 개로 잡은 것이 중요합니다. 같은 날 워크플로우를 여러 번 돌려도 금액이 중복 집계되지 않고 갱신만 됩니다. 정산 데이터는 채널이 나중에 수정하는 일이 흔해서, 다시 받아 덮어쓸 수 있어야 합니다. 그리고 order_id만으로 키를 잡으면 안 됩니다 — 같은 주문이 여러 정산일에 나뉘어 잡히는 경우가 있습니다.

채널을 바꾸려면 — 첫 노드만 바꾸면 됩니다

이 워크플로우는 일부러 채널 중립적으로 설계했습니다. Fetch Channel Settlement 노드만 사용하는 채널에 맞게 교체하고, 응답을 아래 여덟 개 필드로 매핑하면 나머지는 그대로 돕니다.

channel · order_id · settled_on
sale_amount · fee_amount · shipping_amount · refund_amount · payout_reported
  • 국내 마켓플레이스 — 정산 API를 제공하는 채널이라면 그대로 붙입니다. 채널마다 인증 방식이 크게 다르므로(단순 API 키부터 서명 생성·호출 IP 등록까지) 연동 난이도는 착수 전에 반드시 확인하세요.
  • API가 없거나 접근이 안 되는 채널 — 정산 엑셀을 내려받아 시트에 붙여넣고, 첫 노드를 시트 읽기로 바꾸면 됩니다. 수집만 수동이고 검산은 자동이므로 그것만으로도 대부분의 시간이 절약됩니다.
  • 채널이 여러 개라면 — 워크플로우를 복제해 첫 노드만 각각 바꾸고 channel 값을 다르게 넣으세요. 같은 테이블에 쌓이므로 검산·보고는 자동으로 통합됩니다.
⬇︎ 워크플로우 다운로드 (settlement.json)
Postgres 자격증명 하나, Slack 자격증명 하나면 검산·플래그·보고가 그대로 돕니다. 첫 노드(채널 수집)만 각자 채널에 맞게 채우세요.
검증 범위를 정확히 밝힙니다. 검산 쿼리와 플래그 로직은 PostgreSQL 17 + Node.js에서 6건 픽스처로 실행해 확인했습니다 — 정확히 일치하는 2건, 차이 +1,000원과 −500원인 2건, 허용치 안(+1원)인 1건, 그리고 전일이 아닌 1건(집계에서 제외되어야 함). 결과는 불일치 2건 · 일치 3건, 합계 차이 501원으로 손계산과 일치했고, 금액 차이가 큰 순서로 정렬되는 것, 전부 일치할 때 합계만 보고하는 것, 정산 건이 없는 날 아무것도 발송하지 않는 것까지 확인했습니다. 노드 타입·버전은 기존 배포 워크플로우와 동일한 조합입니다. 실제 판매채널 정산 API 연동과 실제 Slack 게시는 확인하지 않았습니다. 본문의 3.5h → 15분은 이 워크플로우가 아니라 초기 도입 사례의 수치입니다. 마지막 검증: 2026-08-15.

설정 순서 (25분)

  1. 테이블 생성 — 1단계 SQL을 실행합니다.
  2. Postgres 자격증명 등록 — n8n → Credentials → PostgresTest로 확인.
  3. Slack 자격증명 등록 — api.slack.com/apps에서 앱 생성 → chat:write, channels:read → Install → xoxb- 토큰 등록. 채널에서 /invite @봇이름.
  4. 워크플로우 import — n8n → Workflows → ...Import from File.
  5. 채널 수집 노드 채우기Fetch Channel Settlement 노드에 사용하는 채널의 정산 API URL·인증을 넣고, 응답을 위 여덟 필드로 매핑합니다. API가 없다면 이 노드를 시트 읽기 노드로 교체하세요.
  6. 임계값 설정Flag Mismatches Only 노드의 TOLERANCE_KRW를 정합니다. 처음엔 1로 시작하세요.
  7. 채널명·자격증명 연결Post to #settlement의 채널명을 바꾸고 세 노드에 자격증명을 붙입니다.
  8. 손계산으로 검증이 단계를 반드시 하세요. 어제 정산 건 중 세 건을 골라 직접 계산기로 판매가 − 수수료 − 배송비 − 환불을 계산한 뒤, SELECT order_id, payout_expected, payout_reported, diff FROM ...의 값과 대조합니다. 여기서 어긋나면 필드 매핑이 잘못된 것입니다. 매핑 오류는 조용히 틀린 숫자를 만들기 때문에 가장 위험합니다.
  9. Activate — 매일 오전 7시에 전일 정산을 대조합니다.

예외 상황이 있으면 어떻게 되나

  • 전부 일치하는 날 — 건별 나열 없이 "N건 전부 일치"와 지급예정 합계만 보고합니다. 잘 돌고 있다는 확인은 되면서 읽을 것은 없습니다.
  • 어제 정산 건이 아예 없는 날 — 아무것도 발송하지 않습니다. 채널이 주말·공휴일에 정산하지 않는 경우가 많아 정상입니다. 다만 정산이 있어야 하는 날에 조용하면 그것도 이상 신호입니다 — 그건 저희 workflow-watchdog 케이스가 다루는 '무실행' 문제입니다.
  • 불일치가 20건을 넘는 날 — Slack에는 금액 차이가 큰 20건만 싣고 "…외 N건"으로 알립니다. 메시지가 잘려 읽히지 않는 것을 막기 위해서입니다. 전체는 원장 테이블에서 조회하세요.
  • 채널이 정산을 나중에 수정 — 같은 키로 다시 받아 덮어씁니다. 수정 전 값이 필요하다면 원장에 이력 테이블을 따로 두세요. 기본 구조에는 이력이 남지 않습니다.
  • 부분 환불refund_amount에 환불액이 들어가면 규칙이 그대로 적용됩니다. 다만 환불이 다음 정산일로 넘어가는 채널이 있어, 그 경우 당일 diff가 크게 뜹니다. 며칠 연속 같은 주문이 뜨면 이월 정산을 의심하세요.
  • 채널 API 스키마 변경 — 수집 노드가 실패하거나 필드가 비어 들어옵니다. 금액이 0으로 들어오면 diff가 커져 플래그되므로 눈에 띄지만, 조용히 틀리는 것보다 요란하게 틀리는 게 낫다는 설계 의도입니다.
3.5h → 15분
일일 정산 시간
야근 0
마감일
예외만
사람 확인 범위
25분
셋업 시간
이제 새벽에 정산 안 합니다. 워크플로우가 합니다.쇼핑몰 대표

당신의 업무도
여기 들어갈 수 있어요.

가장 반복적인 업무 하나만 알려주세요. 자동화 시나리오를 그려드립니다.