본문 바로가기
카테고리 없음

스마트스토어 매출 관리 자동화 툴: 수집·정산·리포트까지 원클릭 파이프라인 만들기

by richjin7285 2025. 10. 22.

스마트스토어 매출 관리 사진

스마트스토어 운영의 ‘진짜 일’은 상품을 고르고, 상세페이지를 개선하고, 광고를 조정하고, 고객 경험을 매만지는 데 있습니다. 그런데 막상 하루를 돌이켜 보면 정작 시간을 가장 많이 잡아먹는 건 데이터 정리입니다. 셀러센터에서 어제 주문/취소/환불 CSV를 내리고, 광고 리포트를 열어 비용을 확인한 뒤, 시트에 붙여 넣고, 수식을 복사하고, 피벗을 새로 돌리고, 급히 만든 스크린숏을 팀에 공유하는 사이—아침이 사라지죠. 더 큰 문제는 이 과정이 사람 손을 많이 탈수록 누락과 왜곡이 생긴다는 겁니다. 주문번호 중복으로 매출이 뻥튀기되거나, 쿠폰/포인트/배송비가 케이스마다 다르게 처리되어 ‘총매출=실매 출’ 착시가 나타납니다. 환불 반영이 늦어 어제는 흑자처럼 보였는데, 오늘 갑자기 적자처럼 튀어나오는 일도 흔하죠.

그래서 필요한 건 새 툴을 하나 더 추가하는 게 아니라, 흐름 자체를 자동화하는 일입니다. 즉, 수집(셀러센터/광고/클레임)정제(필드 표준화)정산 (실 매출/수수료/마진)리포트(대시보드)알림(이상 탐지)까지 이어지는 파이프라인을 만들어 두고, 사람은 그 결과만 보고 의사결정에 집중하는 구조로 바꾸는 거죠. 현실적으로는 Google SheetsApps Script만 있어도 시작할 수 있습니다. 외부 파일이나 메일을 자동으로 끌어오고 싶다면 Zapier/Make 같은 노코드 도구를 곁들이고, 보이는 화면은 Looker Studio(구 Data Studio)로 구성하면 됩니다. 이 조합의 장점은 두 가지입니다. 첫째, 빠르게 세팅할 수 있고 둘째, 유지보수가 쉽다는 점입니다. 담당자가 바뀌어도 “매일 같은 규칙으로 같은 결과”가 나오는 체계를 만드는 게 목적이니까요.

핵심은 표준 스키마입니다. 파일 포맷이 조금씩 달라도, 들어오는 순간 order_date(YYYY-MM-DD), order_id(문자열), qty(정수), sales_amount(정수), coupon, point, shipping, refund_flag, refund_amount, commission 같은 공통 필드로 맞춰 놓으면 이후 계산이 놀랄 만큼 단순해집니다. 그러면 실매 출 = 결제 − 쿠폰 − 포인트 − 환불 − 수수료 − 배송비, 순이익 = 실매 출 − 매입원가 − 물류비 − 광고비가 매일 자동으로 확정되고, 우리는 ‘어제 얼마나 벌었는지’를 아침 1분 만에 확인할 수 있죠. 이 한 줄이 안정되면, 가격 실험/광고 집행/재고 발주 타이밍을 훨씬 공격적으로 가져갈 수 있습니다.

왜 ‘매일 확정’이 중요하냐고요? 매출/마진 의사결정은 타이밍 게임이기 때문입니다. 어제 마진이 급락했는데 그 사실을 이틀 뒤에 알면 이미 손실을 되돌리기 어렵습니다. 반면 전일 대비 −30% 매출 하락, 환불률 급증, 광고비 대비 ROAS 급락 같은 경보를 오전에 받으면, 캠페인/가격/쿠폰 세팅을 그 자리에서 튜닝해 손실을 ‘그날’ 줄일 수 있습니다. 자동화의 가치는 바로 여기에 있어요. 알아야 할 정보를 먼저 알려주는 시스템—이게 만들어지면 운영은 “사후 대응”에서 “선제 대응”으로 바뀝니다.

현장에서 자주 마주치는 실패 패턴도 미리 끊어봅시다. 첫째, 다운로드-붙여 넣기 의존: 담당자 휴가/이탈 시 공백이 생깁니다. 둘째, 수식 분산: 여기저기 흩어진 계산식은 어느 날 갑자기 깨집니다. 셋째, 정의 불일치: 팀마다 ‘실매 출’ 정의가 다르면 회의에서 합의가 안 됩니다. 이를 해결하려면 역할을 분리하세요. raw 시트(원본 누적·수정 금지), clean 시트(스크립트 정제), fact 시트(지표·명시적 계산), dashboard(읽기 전용). 여기에 정의서(실매 출·마진·수수료 계산식)를 짧게 문서화하면 신입/외주와도 바로 합이 맞습니다.

비용도 고민될 수 있지만, 시작 장벽은 낮습니다. Sheets + Apps Script 조합은 무료로 시작할 수 있고, 노코드 커넥터(Zapier/Make)는 파일/메일 수집에만 최소로 써도 됩니다. 성장이 빠른 팀이라면 BigQuery로 옮겨 대용량·다채널(자사몰·마켓 통합) 분석을 생각해 볼 수 있지만, 매일 의사결정을 내리는 수준이라면 스프레드시트 기반 파이프라인만으로도 충분히 ‘탄탄한 자동화’를 경험할 수 있어요. 중요한 건 도구의 화려함이 아니라 데이터의 일관성알림의 정확성입니다.

그럼 어디서부터? 작게, 확실하게 시작하세요. ① 셀러센터 주문/정산 CSV를 매일 같은 폴더에 떨구고, ② Apps Script로 raw_orders/raw_settlements에 자동 누적한 뒤, ③ 표준 스키마로 실매 출/순이익을 계산하고, ④ Looker Studio로 전일 핵심 카드(매출·광고비·순이익·환불률)와 추이 차트를 만들고, ⑤ 이상 알림(전일 대비 급락/급증)을 Slack/이메일로 받으면 됩니다. 이 5단계만 끝나도 “아침 보고”는 3분이면 충분합니다. 이후엔 광고 리포트 결합, 베스트셀러 재고 연동, 주간 PDF 리포트 자동 발송을 덧붙이면 체감이 더 커집니다.

시작 전 1분 체크(복붙)
  • 표준 필드: order_date, order_id, qty, sales_amount, coupon, point, shipping, refund_flag, refund_amount, commission, sku
  • 폴더 규칙: Google Drive /smartstore/exports/YYYYMMDD/ (고정 경로)
  • 정의서: 실 매출/순이익/마진율 공식 3줄 요약 문서화(팀 공용)
  • 권한: 시트 편집 권한 최소화, raw 시트는 수정 금지
  • 알림: “전일 대비 -30% 하락”·“환불률 급증” 조건을 미리 정의(경보 채널 준비)

이 글에서는 곧바로 쓸 수 있는 수집 스크립트, 정산 수식, 광고비 결합, 대시보드 구성, 이상 알림 순서로 전 과정을 안내합니다. 목표는 간단합니다. 매일 아침, 한 화면과 한 통의 알림으로 “어제 벌고 쓴 것”과 “지금 바로 손댈 일”을 결정하는 것. 자동화가 시간을 벌어주면, 우리는 고객과 상품에 더 많은 시간을 쓸 수 있습니다.

1) 데이터 수집 자동화: 셀러센터 → 드라이브 → 시트, ‘무손실’ 파이프라인

매출 자동화의 출발점은 데이터 일원화입니다. 네이버 셀러센터(주문/정산/클레임)와 광고 리포트, 배송/물류 자료가 매일 같은 규칙으로 한 저장소(스프레드시트)에 쌓여야 이후 계산과 대시보드가 안정적으로 굴러갑니다. 핵심은 ① 파일 떨어뜨리는 폴더를 고정, ② 표준 스키마로 칼럼 정렬, ③ 중복 방지(주문키 기준)입니다. 아래 과정을 그대로 따라 하면 “사람이 끼지 않아도” 아침마다 원천데이터가 준비됩니다.

1-1) 폴더·시트 표준(복붙 템플릿)

Google Drive 폴더 구조
/smartstore/exports/           # 외부에서 떨어지는 원본 CSV
  └── YYYYMMDD/                # 날짜별 폴더(매일 신규)
      ├── orders_YYYYMMDD.csv
      ├── settlements_YYYYMMDD.csv
      └── claims_YYYYMMDD.csv
/smartstore/logs/              # 처리 로그/백업
스프레드시트 시트 구조
  • raw_orders · raw_settlements · raw_claims (원본 누적, 수정 금지)
  • clean_orders (헤더 표준화/타입 변환)
  • fact_orders (분석용 칼럼 확정: 실 매출/수수료 등은 다음 섹션)
  • _logs (수집 실행 시간, 파일명, 건수, 중복수)

1-2) 인코딩/헤더 표준화(현실 체크)

  • 인코딩: 가능하면 셀러센터 UTF-8로 내보내세요. EUC-KR/MS949로 떨어질 때는 Apps Script에서 getDataAsString('MS949')로 읽어 변환합니다.
  • 헤더: 날짜/금액/주문키 필드명은 고정(예: order_date, order_id, sales_amount). 셀러센터 원본 칼럼명을 매핑 테이블로 통일합니다.
  • 타입: 날짜 → YYYY-MM-DD, 금액 → 정수(원), 수량 → 정수, Y/N 플래그 분리(refund_flag).

1-3) Apps Script: 최신 CSV 자동 누적(중복 제거/헤더 매핑)

아래 스크립트는 Drive의 날짜 폴더에서 최신 CSV를 읽어 raw_orders에 누적하고, 주문 ID 중복을 걸러냅니다.

/** 시트/폴더 설정 */
const FOLDER_ID = 'YOUR_EXPORTS_FOLDER_ID';
const SHEET_RAW = 'raw_orders';
const SHEET_LOG = '_logs';

/** 원본 헤더 → 표준 헤더 매핑(예시) */
const HEADER_MAP = {
  '주문일자': 'order_date',
  '주문번호': 'order_id',
  '상품명': 'product_name',
  '옵션명': 'option',
  '수량': 'qty',
  '결제금액': 'sales_amount',
  '쿠폰할인': 'coupon',
  '포인트사용': 'point',
  '배송비': 'shipping'
};

function importLatestOrders() {
  const shRaw = SpreadsheetApp.getActive().getSheetByName(SHEET_RAW);
  const shLog = SpreadsheetApp.getActive().getSheetByName(SHEET_LOG);
  const latest = findLatestCsv('orders');             // 파일명에 'orders' 포함
  if (!latest) return log(shLog, 'NO_FILE', 'orders', 0, 0, '파일 없음');

  // 인코딩 변환(UTF-8 권장, 필요 시 MS949로 시도)
  const txt = latest.getBlob().getDataAsString('UTF-8') || latest.getBlob().getDataAsString('MS949');
  const rows = Utilities.parseCsv(txt);
  if (!rows.length) return log(shLog, 'EMPTY', latest.getName(), 0, 0, '빈 파일');

  // 헤더 매핑
  const srcHeader = rows[0];
  const idx = Object.fromEntries(srcHeader.map((h,i)=>[h,i]));
  const stdHeader = Object.values(HEADER_MAP);
  const existsHeader = shRaw.getLastRow() ? shRaw.getRange(1,1,1,shRaw.getLastColumn()).getValues()[0] : [];
  if (!existsHeader.length) shRaw.getRange(1,1,1,stdHeader.length).setValues([stdHeader]);

  // 기존 주문ID 인덱스(중복 방지)
  const orderIdCol = stdHeader.indexOf('order_id') + 1;
  const currentIds = new Set(
    shRaw.getLastRow() > 1 ? shRaw.getRange(2,orderIdCol,shRaw.getLastRow()-1,1).getValues().flat().filter(Boolean) : []
  );

  // 본문 변환
  const body = [];
  for (let r=1; r<rows.length; r++){
    const row = rows[r];
    const record = stdHeader.map(key => {
      const srcKey = Object.keys(HEADER_MAP).find(k => HEADER_MAP[k] === key);
      const val = srcKey in idx ? row[idx[srcKey]] : '';
      switch(key){
        case 'order_date': return normalizeDate(val); // YYYY-MM-DD
        case 'qty': return toInt(val);
        case 'sales_amount':
        case 'coupon':
        case 'point':
        case 'shipping': return toInt(val);
        default: return (val||'').toString().trim();
      }
    });
    const orderId = record[stdHeader.indexOf('order_id')];
    if (!orderId || currentIds.has(orderId)) continue; // 중복/빈키 스킵
    currentIds.add(orderId);
    body.push(record);
  }

  if (body.length){
    shRaw.getRange(shRaw.getLastRow()+1,1,body.length,stdHeader.length).setValues(body);
  }
  log(shLog, 'IMPORTED', latest.getName(), rows.length-1, body.length, `중복제거:${(rows.length-1)-body.length}`);
}

function findLatestCsv(keyword){
  const folder = DriveApp.getFolderById(FOLDER_ID);
  const files = folder.getFiles();
  let latest=null, t=0;
  while(files.hasNext()){
    const f = files.next();
    if (f.getName().toLowerCase().includes(keyword) && f.getLastUpdated().getTime() > t){
      latest = f; t = f.getLastUpdated().getTime();
    }
  }
  return latest;
}

function normalizeDate(s){
  // 2025-10-21, 2025/10/21, 20251021 모두 허용 → YYYY-MM-DD
  if(!s) return '';
  const v = s.toString().replaceAll('.','-').replaceAll('/','-');
  if (/^\d{8}$/.test(v)) return v.slice(0,4)+'-'+v.slice(4,6)+'-'+v.slice(6,8);
  if (/^\d{4}-\d{1,2}-\d{1,2}$/.test(v)) {
    const [y,m,d] = v.split('-'); return [y, ('0'+m).slice(-2), ('0'+d).slice(-2)].join('-');
  }
  return v;
}
function toInt(s){ return parseInt((s||'').toString().replace(/[^\d-]/g,''))||0; }
function log(sh, type, file, srcCnt, insCnt, note){
  const now = new Date();
  sh.appendRow([now, type, file, srcCnt, insCnt, note]);
}

실행: 트리거 > 시간 기반으로 매일 08:30 설정. 실패 시 _logs 시트에서 원인을 바로 확인합니다.

1-4) 클레임/정산/광고 리포트까지 확장(모듈화)

  • 정산(raw_settlements): 수수료/정산일자/정산금액 필드 포함. 헤더 매핑만 바꾸고 같은 함수 재사용.
  • 클레임(raw_claims): order_id, refund_amount, claim_type, processed_at. 이후 주문테이블에 증분 업데이트(두 번째 섹션에서 반영).
  • 광고 리포트(raw_ads): 날짜/소재/캠페인/비용/클릭/전환 필드. 날짜 기준 합산부터 붙이고, SKU 연결은 캠페인 네이밍 규칙으로 맵핑.

1-5) 이메일/파일 자동 수집(Zapier/Make 대안)

수동 업로드 없이 메일 → 드라이브스크립트 순으로 자동화하려면:

  1. Gmail 라벨 smartstore_exports 생성, 셀러센터 내보내기 수신 메일에 자동 라벨 부여
  2. Zapier(또는 Make): New labeled email → 첨부 저장(Drive /smartstore/exports/YYYYMMDD/)
  3. Apps Script 시간 트리거가 새 파일을 감지해 raw_* 누적

Tip. 파일명 규칙(예: orders_YYYYMMDD.csv)을 고정하면 findLatestCsv()가 견고해집니다.

1-6) 데이터 품질 체크(5가지 가드레일)

  • 필수 필드 유효성: order_id 빈값 제거, order_date 파싱 실패건 로그
  • 음수/이상치: 금액/수량 음수는 별도 태그로 마킹
  • 중복: 동일 order_id 2회 유입 시 스킵 + 로그
  • 헤더 변경 감지: 원본 헤더가 바뀌면 로그에 HEADER_CHANGED 기록(알림과 연계)
  • 감사 로그: 파일명/처리시간/원본건수/인서트건수 저장 → 주간 점검

1-7) 표준 스키마(주문) — 이대로 맞추면 계산이 쉬워집니다

필드 타입/예시 설명
order_date YYYY-MM-DD 일자 집계 기준
order_id 문자열 중복 방지 고유키
product_name / option 텍스트 상품/옵션 분석
sku 텍스트 원가/재고 매핑 키
qty 정수 판매 수량
sales_amount 정수(원) 결제 금액
coupon / point / shipping 정수(원) 할인/포인트/배송비
refund_flag / refund_amount Y/N, 정수 환불 반영
channel 스마트스토어 멀티채널 확장 대비

1-8) 알림·예외 처리(놓치면 아픈 부분)

  • 실패 알림: _logsNO_FILE/EMPTY/HEADER_CHANGED 발생 시 Slack Webhook/이메일로 경보
  • 백업: 원본 CSV를 /logs/YYYYMM/로 월별 이동(감사/되돌리기 대비)
  • 권한: raw_*는 편집 금지, clean/fact만 편집 가능 권고

요약하면, 고정 폴더 → 헤더 매핑 → 중복 제거 → 로그의 네 축만 세워도 ‘무손실 수집’이 완성됩니다. 이 상태가 되면 두 번째 섹션의 정산·수수료·광고비 자동 계산으로 바로 넘어가 “매일 아침 실매 출 확정”을 만들 수 있습니다.

2) 정산·수수료·광고비 자동 계산: “실매 출·마진”이 매일 확정되게

정산·수수료·광고비 자동 계산 사진

매출 관리는 결국 얼마를 벌고(실매 출), 얼마가 남는가(순이익·마진)로 귀결됩니다. 문제는 파일마다 쿠폰/포인트/배송비/수수료/환불의 표기가 제각각이라 사람 손으로 합치면 실수가 잦다는 점이죠. 이 섹션에서는 표준 공식을 시트에 고정하고, 광고비 결합환불 반영까지 자동화하여 “어제 기준 실매 출/순이익이 매일 아침 확정되는” 상태를 만드는 방법을 단계별로 정리합니다. 핵심은 계산을 한 시트(또는 한 열)에 모으고, 정의서를 짧게 문서화해 팀 전체가 같은 숫자를 보게 하는 것입니다.

실 매출
  • (Net Sales) = 결제금액(sales_amount) − 쿠폰 − 포인트 − 환불금액 − 수수료 − 배송비
  • 순이익(Profit) = 실 매출 − 매입원가 − 물류비(포장/택배) − 광고비
  • 마진율(%) = 순이익 ÷ 실매 출

2-1) 시트 계산식: clean_orders → fact_orders로 확정

먼저 clean_orders에 표준 필드(날짜/주문 ID/수량/결제금액/쿠폰/포인트/배송비/환불/수수료/sku 등)가 정리돼 있다고 가정하고, 분석용 계산을 넣은 fact_orders를 만듭니다. ARRAYFORMULA로 한 번에 계산해 수식 관리 포인트를 최소화하세요.

=ARRAYFORMULA(
  LET(
    d, clean_orders!A:Z,
    header, INDEX(d,1,),
    rownum, ROW(d),
    order_date, INDEX(d,,MATCH("order_date",header,0)),
    order_id,   INDEX(d,,MATCH("order_id",header,0)),
    qty,        N(INDEX(d,,MATCH("qty",header,0))),
    amt,        N(INDEX(d,,MATCH("sales_amount",header,0))),
    coupon,     N(INDEX(d,,MATCH("coupon",header,0))),
    point,      N(INDEX(d,,MATCH("point",header,0))),
    shipping,   N(INDEX(d,,MATCH("shipping",header,0))),
    refund_amt, N(INDEX(d,,MATCH("refund_amount",header,0))),
    fee,        N(INDEX(d,,MATCH("commission",header,0))),
    sku,        INDEX(d,,MATCH("sku",header,0)),
    net,        amt - coupon - point - refund_amt - fee - shipping,
    HSTACK(order_date, order_id, sku, qty, amt, coupon, point, shipping, fee, refund_amt, net)
  )
)

Tip. LETINDEX/MATCH를 쓰면 헤더 순서가 바뀌어도 견고합니다. N()은 숫자 캐스팅.

2-2) 수수료 계산 자동화(상품군·프로모션별 가중)

수수료가 CSV에 없거나 상품군별로 달라지는 경우가 많습니다. fee_rules 테이블을 만들어 SKU/카테고리별 수수료율을 매핑하세요.

설명 예시
sku 상품 식별자 ABC-001
category 대카/중카 리빙/수납
fee_rate 수수료율 0.12
/* clean_orders에 commission이 비어있다면 아래로 계산 */
=ARRAYFORMULA(
  IF(
    clean_orders!1:1="commission",
    "commission",
    N(VLOOKUP(clean_orders!F2:F, fee_rules!A:C, 3, FALSE)) * N(clean_orders!E2:E)  /* F=sku, E=sales_amount */
  )
)

프로모션 수수료(기간 한정)가 있다면 fee_rulesstart_date/end_date를 추가하고 조건부 VLOOKUP(또는 FILTER)로 기간을 함께 매칭합니다.

2-3) 원가·물류비 자동 매핑(테이블화)

원가는 SKU×단가, 물류비는 주문/박스당 고정 혹은 무게/구간별 변동으로 설정합니다. 각각 cost_table, logistics_table을 만들고 VLOOKUP으로 매핑하세요.

/* 매입원가(원) */
=ARRAYFORMULA( N(VLOOKUP(fact_orders!C2:C, cost_table!A:B, 2, FALSE)) * N(fact_orders!D2:D) )
/* 물류비(간단: 주문당 고정) */
=ARRAYFORMULA( IF(fact_orders!A2:A<>"", 2500, ) )

무게/구간별 물류는 IFs 또는 VLOOKUP(무게→요금표)로 매핑하고, 택배 업체 인상 시 logistics_table만 업데이트하면 전체 계산이 갱신됩니다.

2-4) 광고비 결합(날짜→상품군→캠페인 매칭)

광고비는 먼저 일자 단위 합계를 붙인 뒤, 필요시 상품군/캠페인까지 정교화합니다. 처음엔 일자 기준부터 안정화하는 것이 좋습니다.

/* raw_ads: date, campaign, ad_cost, clicks, conv 등 */
=LET(
  d, fact_orders!A:L,                     /* A:order_date, ... L:net */
  a, raw_ads!A:Z,
  day, INDEX(d,,1),
  costMap, QUERY(a, "select A, sum(C) group by A label sum(C)''", 0),  /* A=date, C=ad_cost */
  adCost, IFERROR(VLOOKUP(day, costMap, 2, FALSE), 0),
  HSTACK(d, adCost)
)

캠페인 네이밍에 SKU/상품군 키를 포함하면(예: CAT-리빙_ABC-001_탑), REGEXEXTRACT로 키를 빼서 SKU 레벨까지 비용 배분이 가능합니다.

2-5) 최종 지표 칼럼(순이익·마진율) 산출

여기까지 붙이면 fact_orders에서 순이익/마진율을 한 번에 산출할 수 있습니다.

/* net: 실매출, cogs: 원가, logistics: 물류비, ad: 광고비 */
=ARRAYFORMULA(
  LET(
    net,       fact_orders!L2:L,
    cogs,      fact_orders!M2:M,
    logistics, fact_orders!N2:N,
    ad,        fact_orders!O2:O,
    profit,    net - cogs - logistics - ad,
    margin,    IFERROR(profit / net, 0),
    HSTACK(profit, margin)
  )
)

2-6) 환불/취소의 ‘증분 반영’(Apps Script로 안정화)

환불 CSV(raw_claims)를 누적했다면, 주문키 기준으로 refund_flag/refund_amount를 갱신해야 합니다. 아래 스니펫은 새로 들어온 환불만 찾아 업데이트합니다.

function applyRefundsIncremental() {
  const ss = SpreadsheetApp.getActive();
  const orders = ss.getSheetByName('clean_orders');
  const claims = ss.getSheetByName('raw_claims').getDataRange().getValues();
  const oh = headerIndex(orders.getRange(1,1,1,orders.getLastColumn()).getValues()[0]);
  const map = {};
  claims.slice(1).forEach(r => { map[r[0]] = r[3]; }); // 0:order_id, 3:refund_amount 예시
  const rng = orders.getRange(2,1,orders.getLastRow()-1,orders.getLastColumn());
  const vals = rng.getValues();
  let touched = 0;
  vals.forEach(row => {
    const id = row[oh.order_id];
    if (map[id] != null && row[oh.refund_amount] !== map[id]) {
      row[oh.refund_flag]   = 'Y';
      row[oh.refund_amount] = map[id];
      touched++;
    }
  });
  rng.setValues(vals);
  Logger.log(`refund updated: ${touched}`);
}
function headerIndex(h){ const m={}; h.forEach((v,i)=>m[v]=i); return m; }

시간 기반 트리거(매일 08:40)를 걸면 수집→환불반영→계산→대시보드가 자동으로 이어집니다.

2-7) 일/주/월 집계(QUERY/피벗) — 대시보드 입력값 만들기

Looker Studio로 넘기기 전, 시트에서 kpi_daily 같은 요약 테이블을 생성해 두면 시각화가 가벼워집니다.

=QUERY(
  {fact_orders!A:A, fact_orders!L:L, fact_orders!P:P},  /* A=order_date, L=net, P=profit */
  "select Col1, sum(Col2), sum(Col3) group by Col1 label sum(Col2)'net', sum(Col3)'profit'",
  1
)

주간/월간은 EOMONTH 또는 TEXT(A, "YYYY-MM")로 그룹 키를 만든 뒤 동일한 QUERY 패턴을 적용하세요.

2-8) 자주 생기는 오류 & 빠른 처방(현장 메모)

증상 원인 해결
실매출이 총매출과 동일 쿠폰/포인트/수수료/배송비 컬럼 누락 표준 스키마에 필수 컬럼 추가, 기본값 0으로 캐스팅
마진율이 비정상 원가/광고비 매핑 오류 VLOOKUP 키 정합(소문자·공백 제거), 룰테이블 최신화
광고비 미결합 날짜 포맷/타임존 불일치 양쪽 모두 YYYY-MM-DD, Asia/Seoul로 통일
환불 누락 증분 업데이트 미작동 Apps Script 트리거 확인, _logs에 touched 카운트 기록

2-9) 운영 SOP(복붙)

[매일 08:30] raw 수집(importLatestCSV) → [08:40] 환불 반영(applyRefundsIncremental)
→ [08:42] fact 계산(ARRAYFORMULA/LET) → [08:45] kpi_daily 갱신 → [08:50] 이상 알림
[주간] 수수료/원가 룰테이블 점검, 광고비 맵핑 정상 여부 확인
[월간] 정의서 변경사항 리뷰(실매출/마진 공식), 룰테이블 버전업 기록

요약하면, 표준 공식 + 룰테이블 매핑 + 증분 환불 반영 + 광고비 결합의 네 축만 잡아도 스마트스토어의 실매 출·순이익은 매일 자동으로 확정됩니다. 이렇게 숫자가 안정되면, 다음 섹션의 대시보드·이상 알림에서 “보는 순간 이해 + 문제는 먼저 알림”을 구현하기가 훨씬 쉬워집니다.

3) 대시보드·알림 자동화: ‘보는 순간 이해’ + ‘문제는 먼저 알림’

숫자를 잘 관리하는 팀의 공통점은 두 가지입니다. 하나의 화면으로 모두가 같은 진실을 보고, 문제는 먼저 알림으로 받습니다. 스마트스토어 매출 관리에서도 마찬가지예요. Looker Studio(구 Data Studio)로 핵심 지표를 한눈에 보여주고, Apps Script/웹훅으로 특이사항을 먼저 알려주면, 아침 회의는 “무슨 일이 있었나?”가 아니라 “무엇을 바꿀까”로 시작합니다.

3-1) Looker Studio 설계 — 카드·추이·분해 ‘3단 구성’

데이터 소스
  • kpi_daily (일자·실매 출·순이익·환불률·광고비 등 요약)
  • fact_orders (SKU/옵션/채널 차원까지 상세)
  • 필요 시 raw_ads (캠페인/비용/전환)

스프레드시트 커넥터 연결 시, 첫 행=헤더 · 시트 범위를 명확히 지정

지표/차원 표준
  • 지표: 실매 출(net), 순이익(profit), 마진율, 광고비, 환불률
  • 차원: 일자, 상품군, SKU, 옵션, 캠페인, 채널
  • ① KPI 카드(상단): 전일 실매 출 · 전일 순이익 · 마진율 · 환불률 · 광고비. 전주/7일 평균 대비 증감(▲/▼) 배지 표시
  • ② 추이(중단): 일자별 실매 출/순이익/광고비 라인 차트(득실 골든라인). 이벤트(쿠폰/광고 변경일) 점형 주석 추가
  • ③ 분해(하단): 상품 Top10 바차트, SKU 피벗(수량·매출·이익), 환불 사유 도넛, 캠페인별 ROAS 히트맵
가독성 팁
  • 색상 규칙: 실매 출=파랑(#2563EB), 순이익=초록(#16A34A), 환불/감소=빨강(#DC2626)
  • 기간 컨트롤: 상단에 최근 7/30/90일 프리셋 버튼
  • 필터: 상품군·캠페인·채널 필터를 보고서 수준으로 고정(페이지 공통)

3-2) 이상 탐지(Anomaly) 로직 — ‘어제-평균-임계치’ 3단계

알림의 핵심은 과도한 소음 없이 정말 중요한 변화만 잡아내는 것입니다. 아래 기준을 추천합니다.

  • 전일 대비: 어제 실매 출이 7일 평균 대비 -30% 이하
  • 환불 급증: 환불률이 7일 평균 + 2 표준편차 이상
  • ROAS 급락: 광고비가 증가했는데 실매 출이 전일/평균 대비 동반 하락
/** kpi_daily: date, net, profit, refund_rate, ad_cost, net_avg7, net_std7 */
function alertAnomaly() {
  const sh = SpreadsheetApp.getActive().getSheetByName('kpi_daily');
  const last = sh.getLastRow();
  const h = sh.getRange(1,1,1,sh.getLastColumn()).getValues()[0];
  const ix = Object.fromEntries(h.map((v,i)=>[v,i+1]));
  const val = (col) => sh.getRange(last, ix[col]).getValue();
  const date = val('date'), net = val('net'), avg7 = val('net_avg7'), std7 = val('net_std7');
  const refund = val('refund_rate'), ad = val('ad_cost');

  let alerts = [];
  if (avg7 > 0 && net < avg7*0.7) alerts.push(`실매출 하락: ${fmt(net)} (7일평균 ${fmt(avg7)} 대비 ▼${pct(net/avg7)})`);
  if (std7 > 0 && refund > val('refund_avg7') + 2*val('refund_std7')) alerts.push(`환불률 급증: ${(refund*100).toFixed(1)}%`);
  if (ad > val('ad_avg7')*1.2 && net < avg7*0.9) alerts.push(`광고효율 의심: 광고비 ↑, 매출 ↓`);

  if (!alerts.length) return;
  const payload = { text: `📢 [스마트스토어 경보] ${formatDate(date)}\n- ` + alerts.join('\n- ') };
  UrlFetchApp.fetch('YOUR_SLACK_WEBHOOK', {
    method:'post', contentType:'application/json', payload: JSON.stringify(payload)
  });
}
function fmt(n){ return Math.round(n).toLocaleString()+'원'; }
function pct(r){ return ((1-r)*100).toFixed(0)+'%'; }
function formatDate(d){ return Utilities.formatDate(new Date(d), 'Asia/Seoul', 'yyyy-MM-dd'); }

스케줄: 매일 08:50 실행. 경보는 #ops 또는 #store-alert 전용 채널로 분리해 소음 관리.

3-3) Slack/이메일 카드형 요약 — ‘읽자마자 감’이 오게

[일일 요약 — {{날짜}}]
실매출: {{net | 통화}}  (전일 대비 {{Δnet}} / 7일평균 대비 {{Δ7}})
순이익: {{profit | 통화}}  · 마진율: {{margin | %}}
광고비: {{ad | 통화}}  · ROAS: {{roas}}
환불률: {{refund_rate | %}}

Top3 SKU ↓
1) {{sku1}} — {{net1 | 통화}} (환불률 {{refund1 | %}})
2) {{sku2}} — {{net2 | 통화}}
3) {{sku3}} — {{net3 | 통화}}

메모: {{note}}  (이벤트/쿠폰 변경/광고 조정 사항)

Slack Block Kit로 포맷하면 가독성이 좋아집니다. 이메일은 고정 템플릿 + 굵은 텍스트/색상 배지 활용.

3-4) 보고서 운영 — 주간/월간 ‘리뷰 루틴’까지 자동화

  • 주간(월 09:00): 전주 합계·증감, 베스트/워스트 SKU, 환불 Top3, 액션 아이템(3개)
  • 월간(1일 09:00): 월간 매출/이익/마진/ROAS, 프로모션 영향 분석, 다음 달 가설
  • 자동 발송: Looker Studio 링크와 함께 Apps Script로 PDF 스냅숏 첨부
function sendWeeklyReport(){
  const url = 'LOOKER_STUDIO_REPORT_LINK'; // 기간 파라미터 포함 가능
  const html = `

주간 리포트

링크: ${url}

  • 실매 출/순이익/마진/ROAS 요약
  • 베스트·워스트 SKU 및 환불 이슈
  • 이번 주 액션 3가지
`;
  GmailApp.sendEmail('team@example.com', '[스마트스토어] 주간 리포트', '', {htmlBody: html});
}

3-5) 대시보드 성능·정합성 체크리스트

영역 점검 항목 해결 가이드
성능 로드 지연/타임아웃 시트에서 kpi_daily 등 요약 테이블 생성 후 연결
정합성 총계/세부 합계 불일치 기간/필터 상태 확인, 환불/수수료 반영 순서 재검토
권한 외부 공유 오류 스프레드시트/보고서 보기 권한 일치, 링크 만료 정책

3-6) ‘문제 먼저 알림’ SOP(복붙)

[매일]
08:30 수집 → 08:40 환불 반영 → 08:42 계산 → 08:45 kpi 갱신 → 08:50 이상 알림 → 09:00 대시보드 점검(3분)
[주간]
월 09:00 주간 리포트 발송 · 경보 상위 3건 원인/조치 기록(노션/시트)
[월간]
마지막 영업일: 프로모션/광고 가설 회고 · 다음 달 실험 계획 3개 합의

정리하면, 대시보드는 보는 순간 이해가 되어야 하고, 알림은 문제를 먼저 알려야 합니다. 이 두 가지가 갖춰지면 “자료 만들기”에 쓰던 시간이 “의사결정과 실험”으로 이동합니다. 오늘은 KPI 카드/추이/분해의 3단 레이아웃을 만들고, 실매 출 -30%·환불률 +2σ 경보만 먼저 붙이세요. 내일부터 아침이 한결 가벼워집니다.

 

결론: 툴보다 ‘흐름’이 먼저 — 매일 아침, 같은 자리에서 같은 품질의 숫자를 본다

매일 아침, 같은 자리에서 같은 품질 사진

스마트스토어 매출 관리는 화려한 대시보드나 비싼 설루션의 문제가 아닙니다. 핵심은 수집 → 정제 → 정산 → 리포트 → 알림이 끊김 없이 이어지는 한 줄의 파이프라인을 만드는 일입니다. 원본 CSV가 매일 같은 폴더에 들어오고, Apps Script가 표준 스키마로 정리하며, 시트 한 곳에서 실매 출·순이익이 자동으로 확정되고, Looker Studio 한 화면에서 모두가 같은 지표를 보며, 이상 징후는 Slack/이메일로 먼저 울리는 상태—이 흐름이 만들어지는 순간, “자료 만들기”는 0분이 되고 의사결정(가격·광고·재고)은 더 빨라집니다. 특히 전일 대비 급락/급증 경보가 아침에 잡히면, 같은 날 안에 손을 댈 수 있어 손실을 크게 줄일 수 있습니다. 결국 자동화는 시간을 벌어 주고, 그 시간은 상품 개선과 고객 경험에 재투자됩니다.

이번 주 실행 체크리스트(복붙)
  • 수집: Drive /smartstore/exports/YYYYMMDD/ 고정 · raw_* 시트 자동 누적(중복 방지)
  • 정제/정산: 표준 스키마(날짜/금액/주문키) 확정 · 실매 출/마진 공식 문서화 · 환불 증분 반영
  • 광고 결합: 일자 합계부터 붙이고, 캠페인/상품군은 네이밍 규칙으로 단계적 확대
  • 대시보드: KPI 카드(전일 실매 출·순이익·마진·환불률·광고비) + 7/30일 추이 + SKU 분해
  • 알림: 전일 실매 출 < 7일평균 × 0.7, 환불률 > 평균 + 2σ 경보 → Slack
  • 운영: _logs로 실패/헤더변경 감지 · 권한 최소화(raw 수정 금지) · 주 1회 스냅샷 백업

시작은 작게, 그러나 반드시 매일 돌아가게 만드세요. 오늘은 수집 스크립트와 표준 스키마, 실매 출 계산까지만. 내일은 대시보드 카드와 알림 두 가지를 얹고, 주말에 광고비 결합과 룰테이블(수수료·원가)을 정비해도 충분합니다. 일주일이면 “아침 3분 보고 체계”가 자리를 잡습니다. 흐름이 자동이면, 팀은 더 자주 실험하고 더 빨리 학습합니다. 그게 매출을 지속적으로 키우는 가장 현실적인 방법입니다.