구글시트 운송사 점수 등급 자동 계산하기 — KPI 3편
도입: 운송사 KPI, 감으로 보지 말고 점수로 관리하기
구글시트로 운송사 점수·등급 자동 계산을 찾는 분들은 보통 비슷한 고민을 갖고 있습니다. 정시율, 노쇼, 예약 변경 횟수 같은 KPI는 쌓이고 있지만, 막상 “어느 운송사가 좋은 파트너냐”를 이야기할 때는 여전히 현장 느낌과 기억에 기대는 경우가 많습니다. 이 글은 입고 예약 KPI 시리즈 3편으로, Google Sheets와 Apps Script로 운송사별 정시율·노쇼·변경 데이터를 가중치 점수로 바꾸고 A~D 등급까지 자동으로 계산하는 방법을 단계별로 정리합니다.
앞 편인 구글시트 예약 KPI 집계 방법: 정시율·체류시간 자동 계산에서는 날짜별 KPI(정시 도착 비율, 평균 체류 시간 등)를 계산하는 함수들을 만들었습니다. 이어서 구글시트 KPI 자동 집계 방법: 야간 트리거로 이력 쌓기 (완성 코드 포함)에서는 이 일별 KPI를 KPI_HISTORY 시트에 야간 배치로 쌓는 구조까지 완성했고요. 이번 편에서는 같은 KPI_HISTORY 데이터를 기반으로 “운송사별” 성적표를 만들고, 점수·등급을 자동 산출해 시트에 정리하는 부분을 구현합니다.
참고로 KPI 2편에서 만든 getKpiHistory() 가 돌려주는 객체에는 daily 배열이 있고, 각 원소에는 { date, summary, carriers } 같은 형태로 운송사별 KPI가 들어 있습니다. carriers 는 [{ carrier, total, onTime, noShow, changed, ... }] 꼴의 배열입니다. 이번 편 코드는 이 구조를 그대로 활용합니다.
또한 앞 편의 getKpiHistory()는 startDate와 endDate를 Date 객체로 받는 함수입니다. 호출할 때는 다음처럼 { startDate: 시작일, endDate: 종료일 } 형태의 객체를 인자로 전달해야 합니다.
const history = getKpiHistory({
startDate: new Date('2026-06-01'),
endDate: new Date('2026-08-31')
});이 글에서 사용하는 모든 코드도 이 호출 규격을 전제로 작성되어 있습니다.
운송사 KPI를 점수·등급으로 나누는 기준부터 정하기
운송사 KPI의 목적은 단순히 “누가 자주 늦는다”를 가려내는 게 아닙니다. 실제 현장에서는 선박 지연, 해외 출발 지연, 교통 상황처럼 운송사가 통제하기 어려운 사유와, 반복되는 예약 관리·연락 문제 같은 운영상 지연을 구분해야 합니다. 이 구분이 되지 않으면 KPI가 책임을 잘못 돌리는 지표가 되기 쉽습니다.
그래서 이 프로젝트에서는 예약·체크인 단계에서 지연 사유를 별도 필드로 기록해 두고, 운송사 점수 계산에서는 “창고에서 개선하고 싶은 행동”에 관한 지표만 선택해서 반영하는 쪽으로 설계했습니다. 여기서는 이해를 돕기 위해 기본 구조를 예시로 제시하지만, 각 항목의 비중은 현장에 맞게 조정해서 쓰시면 됩니다.
이 글에서 사용할 기본 구조는 다음과 같습니다.
- 표본 기간:
KPI_HISTORY시트에서 최근 N일(예: 90일) 데이터 사용 - 운송사별 집계 항목
· 총 예약 건수(total)
· 정시 도착 건수(onTime)
· 노쇼 건수(noShow)
· 예약 변경 건수(changed)
- 운송사별 비율 지표
· 정시율(%) = 정시 도착 / 총 예약 × 100
· 노쇼율(%) = 노쇼 / 총 예약 × 100
· 변경률(%) = 변경 / 총 예약 × 100
- 가중치 점수(예시)
· 정시율 비중 70%
· 노쇼율 비중 20% (많을수록 감점)
· 변경률 비중 10% (많을수록 소폭 감점)
- 점수 → 등급 매핑(예시)
· 90점 이상: A
· 75점 이상 90점 미만: B
· 60점 이상 75점 미만: C
· 60점 미만: D
· 단, 표본 3건 미만이면 등급 대신 “표본 부족”
특히 “경계값을 포함하는지”를 코드와 설명에서 일관되게 맞추는 게 중요합니다. 이 글에서는 “90점 이상은 A 등급”으로 고정해 설명하고, Apps Script 코드도 그 기준에 맞춰 구현합니다.
운송사별 정시율·노쇼율 계산 함수 설계
이제 KPI_HISTORY 에 쌓인 운송사별 데이터를 다시 묶어서, “최근 N일 동안 각 운송사가 어떤 패턴을 보였는지”를 집계해야 합니다. 이 단계가 구글시트 운송사 등급 자동 계산의 기초입니다. 여기서 수치가 잘못 들어가면 뒤 단계 점수·등급도 같이 왜곡되기 때문에, 숫자 변환과 검증을 명시적으로 넣어 둡니다.
1단계 — 설정 상수 및 운송사별 집계 함수 정의
이 코드는 운송사 KPI 계산에 쓸 설정(표본 일수, 등급 경계, 가중치)을 한곳에 모아 두고, KPI_HISTORY에서 운송사별 정시·노쇼·변경 통계를 집계합니다.
- 붙여넣을 위치:
구글시트 → 확장 프로그램 → Apps Script → KPI 1·2편 코드 바로 아래
- 붙여넣은 뒤 할 일:
저장 후 KPI_testComputeCarrierPunctuality_() 를 한 번 실행해 권한을 허용합니다.
// 운송사 KPI용 설정입니다. 여기만 본인 상황에 맞게 조정하세요.
const KPI_CARRIER_CONFIG = { // → 운송사 KPI 설정 객체
LOOKBACK_DAYS: 90, // → 최근 90일 표본 사용
MIN_SAMPLES_FOR_GRADE: 3, // → 등급 매기기 최소 건수
SCORE_WEIGHTS: { // → 가중치 설정
PUNCTUALITY: 0.7, // → 정시율 비중 70%
NO_SHOW: 0.2, // → 노쇼율 비중 20%
CHANGE: 0.1 // → 변경률 비중 10%
},
GRADE_BOUNDARIES: [ // → 등급 경계 설정
{ min: 90, grade: 'A' }, // → 90점 이상 A
{ min: 75, grade: 'B' }, // → 75점 이상 B
{ min: 60, grade: 'C' }, // → 60점 이상 C
{ min: 0, grade: 'D' } // → 0점 이상 D
]
};
// 특정 값이 유효한 0 이상 숫자인지 확인합니다.
function KPI_toNonNegativeNumber_(value, fieldName) { // → 숫자 변환 도우미
const n = Number(value); // → 숫자로 변환
if (!Number.isFinite(n) || n < 0) { // → 음수·NaN 차단
throw new Error('잘못된 숫자 값입니다: ' + fieldName); // → 오류 메시지
}
return n; // → 검증된 숫자
}
// 운송사별 정시·노쇼·변경 통계를 계산합니다.
function computeCarrierPunctuality_() { // → 내부 도우미 함수
const TZ = 'America/New_York'; // 시간대 명시
const today = new Date(); // 앱스크립트 런타임이 TZ로 자동 변환
const lookbackMs = KPI_CARRIER_CONFIG.LOOKBACK_DAYS * 24 * 60 * 60 * 1000; // → 일수→ms
const startDate = new Date(today.getTime() - lookbackMs); // → 시작일 계산
// 앞 편 [KPI 2편](/posts/20260827-q239-b4fddd)에서 만든 getKpiHistory() 를 그대로 씁니다.
const history = getKpiHistory({ // → KPI 이력 가져오기
startDate: startDate, // → 최근 N일 시작
endDate: today // → 오늘까지
});
const carrierStats = {}; // → 운송사별 집계용
history.daily.forEach(day => { // → 일별 KPI 순회
const carriers = day.carriers || []; // → 운송사 KPI 배열
carriers.forEach(c => { // → 운송사 하나씩
const name = c.carrier || 'UNKNOWN'; // → 운송사 이름 키
if (!carrierStats[name]) { // → 첫 등장 시 초기화
carrierStats[name] = { // → 집계 객체 생성
carrier: name, // → 운송사명
total: 0, // → 총 예약 수
onTime: 0, // → 정시 건수
noShow: 0, // → 노쇼 건수
changed: 0 // → 변경 건수
};
}
const s = carrierStats[name]; // → 참조 단축
// 숫자 검증 후 누적합니다.
s.total += KPI_toNonNegativeNumber_(c.total || 0, 'total'); // → 총 예약 누적
s.onTime += KPI_toNonNegativeNumber_(c.onTime || 0, 'onTime'); // → 정시 누적
s.noShow += KPI_toNonNegativeNumber_(c.noShow || 0, 'noShow'); // → 노쇼 누적
s.changed += KPI_toNonNegativeNumber_(c.changed || 0, 'changed'); // → 변경 누적
});
});
const result = Object.values(carrierStats) // → 객체→배열
.filter(s => s.total > 0) // → 예약 0건 제외
.map(s => { // → 비율 계산 추가
const punctuality = (s.onTime / s.total) * 100; // → 정시율%
const noShowRate = (s.noShow / s.total) * 100; // → 노쇼율%
const changeRate = (s.changed / s.total) * 100; // → 변경률%
return { // → 새 객체 반환
carrier: s.carrier, // → 운송사명
total: s.total, // → 총 예약수
onTime: s.onTime, // → 정시 건수
noShow: s.noShow, // → 노쇼 건수
changed: s.changed, // → 변경 건수
punctuality, // → 정시율
noShowRate, // → 노쇼율
changeRate // → 변경률
};
});
return result; // → 운송사별 배열 반환
}
// 운송사 정시율 계산을 테스트합니다.
function KPI_testComputeCarrierPunctuality_() { // → 테스트 함수
const stats = computeCarrierPunctuality_(); // → 집계 실행
Logger.log(JSON.stringify(stats, null, 2)); // → 결과 로그 출력
}확인 방법:
- Apps Script 편집기에서 함수 목록에서
KPI_testComputeCarrierPunctuality_선택 후 실행. Execution log에 운송사별carrier,total,onTime,punctuality등이 포함된 JSON 배열이 찍히면 성공입니다.- 만약
잘못된 숫자 값입니다: ...오류가 뜨면KPI_HISTORY시트 숫자 열에 텍스트나 기호가 섞여 있는지 먼저 확인합니다.
운송사별 점수 계산: 가중치와 표본 기준 적용
이제 운송사별 집계 결과를 가지고 실제 “점수”를 계산할 차례입니다. 이 단계가 구글시트 KPI 운송사 등급 계산의 핵심입니다. 동시에, 표본 수가 너무 적은 운송사는 등급을 매기지 않고 “표본 부족”으로 표시해 평가 왜곡을 막습니다. 특정 운송사가 월 1~2회만 들어오는 경우까지 A/B/C 안에서 같이 줄 세우는 건 의미가 떨어지기 때문입니다.
2단계 — 가중치 점수 및 등급 부여 함수
이 코드는 운송사별 통계를 가중치 점수로 변환하고, A~D 등급 또는 “표본 부족”을 결정합니다.
- 붙여넣을 위치:
computeCarrierPunctuality_() 아래
- 붙여넣은 뒤 할 일:
KPI_testCalcCarrierScore_() 와 KPI_testScoreToGrade_() 를 실행해 각각 점수·등급 로직을 확인합니다.
// 운송사별 하나의 점수를 계산합니다.
function calcCarrierScore_(stat) { // → 운송사 통계→점수
const w = KPI_CARRIER_CONFIG.SCORE_WEIGHTS; // → 가중치 참조
if (!stat || !Number.isFinite(stat.total) || stat.total <= 0) { // → 유효성 검사
throw new Error('유효하지 않은 운송사 통계입니다.'); // → 오류 처리
}
const punctuality = stat.punctuality; // → 정시율%
const noShowRate = stat.noShowRate; // → 노쇼율%
const changeRate = stat.changeRate; // → 변경률%
if (![punctuality, noShowRate, changeRate] // → 모든 지표 검사
.every(v => Number.isFinite(v) && v >= 0)) { // → 숫자·0이상 확인
throw new Error('지표 값이 잘못되었습니다.'); // → 잘못된 값 차단
}
// 정시율은 비율 그대로 가중 적용합니다.
const punctualityScore = punctuality * w.PUNCTUALITY; // → 정시 점수
// 노쇼·변경률은 0이 최상, 많을수록 감점입니다.
const noShowScore = Math.max(0, (100 - noShowRate)) * w.NO_SHOW; // → 노쇼 점수
const changeScore = Math.max(0, (100 - changeRate)) * w.CHANGE; // → 변경 점수
const rawScore = punctualityScore + noShowScore + changeScore; // → 합산 점수
const finalScore = Math.round(rawScore * 10) / 10; // → 소수1자리 반올림
return finalScore; // → 점수 반환
}
// 점수를 A~D 등급으로 변환합니다.
function scoreToGrade_(score, sampleCount) { // → 점수→등급
const minSamples = KPI_CARRIER_CONFIG.MIN_SAMPLES_FOR_GRADE; // → 최소 표본
if (!Number.isFinite(score)) { // → 숫자 확인
throw new Error('점수 값이 잘못되었습니다.'); // → 오류 처리
}
if (!Number.isFinite(sampleCount) || sampleCount < 0) { // → 표본 검증
throw new Error('표본 수가 잘못되었습니다.'); // → 오류 처리
}
if (sampleCount < minSamples) { // → 표본 부족일 때
return '표본 부족'; // → 등급 대신 문구
}
const boundaries = KPI_CARRIER_CONFIG.GRADE_BOUNDARIES; // → 경계 참조
for (let i = 0; i < boundaries.length; i++) { // → 경계 순회
const b = boundaries[i]; // → 단일 경계
if (score >= b.min) { // → min 이상이면 등급
return b.grade; // → 해당 등급 반환
}
}
return 'D'; // → 안전장치(실제론 도달X)
}
// 전체 운송사에 점수·등급을 붙입니다.
function KPI_getCarrierScores() { // → 외부용 함수
const stats = computeCarrierPunctuality_(); // → 운송사별 통계
const withScores = stats.map(s => { // → 점수·등급 추가
const score = calcCarrierScore_(s); // → 점수 계산
const grade = scoreToGrade_(score, s.total); // → 등급 계산
return Object.assign({}, s, { // → 새 객체 생성
score, // → 점수
grade // → 등급/표본 부족
});
});
return withScores; // → 배열 반환
}
// 점수 계산을 테스트합니다.
function KPI_testCalcCarrierScore_() { // → 점수 테스트
const sample = { // → 예시 통계
carrier: 'TEST', // → 이름
total: 10, // → 총 10건
onTime: 9, // → 정시 9건
noShow: 0, // → 노쇼 0건
changed: 1, // → 변경 1건
punctuality: 90, // → 정시율 90%
noShowRate: 0, // → 노쇼율 0%
changeRate: 10 // → 변경률 10%
};
const score = calcCarrierScore_(sample); // → 점수 계산
const grade = scoreToGrade_(score, sample.total); // → 등급 계산
const exp = { score: 92.0, grade: 'A' }; // → 기대값 예시
const ok = Math.abs(score - exp.score) < 0.1 && grade === exp.grade;
Logger.log(
(ok ? '✓' : '✗') +
' score=' + score + ' (기대 ' + exp.score + '), ' +
'grade=' + grade + ' (기대 ' + exp.grade + ')'
);
}
// 등급 경계를 테스트합니다.
function KPI_testScoreToGrade_() { // → 경계 테스트
const testCases = [
{ score: 92, samples: 10, expected: 'A' },
{ score: 80, samples: 10, expected: 'B' },
{ score: 65, samples: 10, expected: 'C' },
{ score: 50, samples: 10, expected: 'D' },
{ score: 95, samples: 2, expected: '표본 부족' }
];
let pass = true;
testCases.forEach(tc => {
const r = scoreToGrade_(tc.score, tc.samples);
const ok = r === tc.expected;
if (!ok) pass = false;
Logger.log(
(ok ? '✓' : '✗') +
' ' + tc.score + 'pt, ' + tc.samples + '건 → ' + r +
' (기대: ' + tc.expected + ')'
);
});
Logger.log('결과: ' + (pass ? '통과' : '실패'));
}확인 방법:
KPI_testScoreToGrade_실행 → 로그에서
· 92pt, 10건 → A
· 80pt, 10건 → B
· 65pt, 10건 → C
· 50pt, 10건 → D
· 95pt, 2건 → “표본 부족”
로 표시되는지 확인합니다.
KPI_testCalcCarrierScore_실행 → 로그에
score=92.0 (기대 92.0), grade=A (기대 A)
형식으로 출력되면 의도대로 동작하는 것입니다.
운송사 점수·등급을 KPI_CARRIER 시트에 저장하기
Apps Script 로그에서만 결과를 확인하면 실제 운영에서는 활용도가 떨어집니다. 실무에서는 운송사 KPI용 별도 시트를 하나 만들어, 운송사별 점수·등급을 정기적으로 덮어써 두는 식이 관리·공유 측면에서 훨씬 낫습니다. 이때 여러 사용자가 동시에 구글시트 운송사 점수 계산 버튼을 누르거나 시간 기반 트리거가 겹칠 수 있기 때문에, 저장 함수에는 LockService 로 동시 실행을 막는 처리를 넣어 두는 게 안전합니다.
3단계 — 운송사 점수·등급을 시트에 반영하는 함수
이 코드는 KPI_CARRIER 시트를 만들고, 운송사별 점수·등급을 매번 새로 써 줍니다.
- 붙여넣을 위치:
위의 KPI 관련 함수들 아래
- 붙여넣은 뒤 할 일:
KPI_updateCarrierScoreSheet() 또는 KPI_testUpdateCarrierScoreSheet() 를 실행해 시트를 확인합니다.
// 운송사 점수를 적어둘 시트를 가져오거나 만듭니다.
function KPI_getOrCreateCarrierSheet_() { // → 시트 확보
const ss = SpreadsheetApp.getActiveSpreadsheet(); // → 현재 통합 문서
const name = 'KPI_CARRIER'; // → 시트 이름
let sheet = ss.getSheetByName(name); // → 시트 찾기
if (!sheet) { // → 없으면 생성
sheet = ss.insertSheet(name); // → 새 시트
const headers = [ // → 헤더 목록
'운송사', '총예약', '정시', '노쇼', '변경',
'정시율(%)', '노쇼율(%)', '변경률(%)', '점수', '등급'
];
sheet.getRange(1, 1, 1, headers.length) // → 1행 범위
.setValues([headers]); // → 헤더 쓰기
}
return sheet; // → 시트 반환
}
// 운송사 점수·등급을 시트에 반영합니다.
function KPI_updateCarrierScoreSheet() { // → 외부 실행 함수
const lock = LockService.getScriptLock(); // → 잠금 객체
lock.waitLock(30000); // → 최대 30초 대기
try { // → 잠금 안에서 실행
const sheet = KPI_getOrCreateCarrierSheet_(); // → 시트 확보
const data = KPI_getCarrierScores(); // → 점수 계산
// 기존 데이터 영역을 지웁니다(헤더 제외).
const lastRow = sheet.getLastRow(); // → 마지막 행 번호
if (lastRow > 1) { // → 데이터가 있으면
sheet.getRange(2, 1, lastRow - 1, 10).clearContent();// → 본문만 비우기
}
if (data.length === 0) { // → 데이터 없을 때
return; // → 더 할 일 없음
}
// 배열로 변환해 한 번에 씁니다.
const rows = data.map(d => [ // → 행 배열 생성
d.carrier, // → 운송사
d.total, // → 총예약
d.onTime, // → 정시
d.noShow, // → 노쇼
d.changed, // → 변경
Math.round(d.punctuality * 10) / 10, // → 정시율 소수1자리
Math.round(d.noShowRate * 10) / 10, // → 노쇼율
Math.round(d.changeRate * 10) / 10, // → 변경률
d.score, // → 점수
d.grade // → 등급/표본 부족
]);
sheet.getRange(2, 1, rows.length, rows[0].length) // → 데이터 범위
.setValues(rows); // → 값 쓰기
} finally { // → 예외 여부와 무관
lock.releaseLock(); // → 잠금 해제
}
}
// 운송사 점수 시트 생성을 테스트합니다.
function KPI_testUpdateCarrierScoreSheet() { // → 테스트 함수
KPI_updateCarrierScoreSheet(); // → 시트 업데이트
Logger.log('KPI_CARRIER 시트가 갱신되었습니다.'); // → 완료 로그
}확인 방법:
- Apps Script에서
KPI_testUpdateCarrierScoreSheet실행. - 스프레드시트로 돌아가 “KPI_CARRIER” 시트가 새로 생기거나 갱신되었는지 확인.
- 운송사별로 한 줄씩,
총예약·정시·노쇼·변경·정시율·점수·등급이 채워져 있으면 정상입니다. - 예약 건수가 1~2건뿐인 운송사는 “등급” 열에 “표본 부족”이라고 표시되는지도 함께 봅니다.
실무 팁: 산식 조정과 오류 대처, 활용 방향
가중치·등급 경계는 한 번에 크게 바꾸지 않기
KPI_CARRIER_CONFIG 에 가중치와 등급 경계를 한곳에 모아 둔 이유는, 시간이 지나면서 운영 목표가 바뀔 수 있기 때문입니다. 예를 들어 특정 고객사가 노쇼에 특히 민감하다면 NO_SHOW 가중치를 0.3~0.4까지 높이고 정시율 비중을 조금 낮출 수 있습니다. 반대로 “변경은 어느 정도 허용하지만 노쇼는 절대 허용하지 않는다”는 정책이라면, 변경률 가중치는 0.05처럼 줄이고 노쇼 비중을 더 키우는 식으로 조정할 수 있습니다.
경험상 기준을 자주 바꾸면 현장과 운송사가 모두 혼란을 느낍니다. 한 분기 정도는 같은 가중치·등급 경계로 돌려 본 뒤, 실제 분포와 체감을 동시에 보고 조정하는 쪽이 안정적이었습니다. 변경 전·후 기준은 간단히라도 기록을 남겨 둬야 나중에 “언제부터 기준을 바꿨는지” 설명할 수 있습니다.
자주 나오는 오류와 해결 방법
앱스 스크립트로 운송사 평가 자동화를 붙이다 보면 몇 가지 패턴의 오류가 반복됩니다. 많이 나오는 사례와 대응 방법을 정리하면:
getKpiHistory is not a function
- 원인: 이 글만 먼저 붙여넣고, KPI 1·2편 코드가 아직 프로젝트에 없는 경우입니다.
- 해결:
· 구글시트 예약 KPI 집계 방법: 정시율·체류시간 자동 계산
· 구글시트 KPI 자동 집계 방법: 야간 트리거로 이력 쌓기 (완성 코드 포함)
두 편의 코드를 같은 Apps Script 프로젝트에 먼저 붙여넣습니다.
- 숫자 형식 오류로 인한 예외
- 현상:
잘못된 숫자 값입니다: total같은 메시지와 함께 함수가 중단됩니다. - 원인:
KPI_HISTORY시트에서 숫자여야 할 열이 텍스트나 “10건”처럼 단위가 붙은 상태로 저장된 경우입니다. - 해결:
· KPI_HISTORY 에서 해당 열의 셀 서식을 숫자로 맞춥니다.
· 숫자 칸에는 숫자만 쓰고, 단위는 별도 열로 분리합니다.
코드 쪽에서는 Number 변환·검증을 명시적으로 하고 있기 때문에, 잘못된 입력을 조용히 합산해 버리지 않고 바로 알려 주는 역할을 합니다.
- 권한·트리거 관련 오류
- 현상:
Exception: You do not have permission...형태의 메시지가 뜹니다. - 해결:
· 먼저 편집기에서 KPI_testComputeCarrierPunctuality_ 또는 KPI_testUpdateCarrierScoreSheet 를 직접 실행해 권한을 한 번 승인합니다.
· 야간 트리거를 추가하고 싶다면, KPI_updateCarrierScoreSheet 에 시간 기반 트리거를 걸어 두면 됩니다. (KPI 2편에서 nightlyRollup() 에 트리거를 건 것과 같은 방식입니다.)
KPI_CARRIER 시트 활용 아이디어
KPI_CARRIER 시트가 만들어지면, 구글시트 기반 운송사 평가 구조를 여러 방향으로 확장할 수 있습니다.
- 필터 뷰로 A·B 등급만 따로 모아서 “선호 운송사 리스트” 뼈대를 만드는 용도
- 피벗 테이블로 월별 평균 점수 변화를 다른 시트에 그려 보는 용도
(이 글에서는 단일 스냅샷 시트만 다루지만, 필요하면 기간별 스냅샷을 따로 둘 수도 있습니다.)
- 정시율 기준으로 상위 5개·하위 5개 운송사를 뽑아 주간/월간 회의 자료의 한 페이지로 쓰는 용도
다만 이 점수가 계약이나 패널 선정의 유일한 기준이 되어서는 안 됩니다. 다만 정량적인 지표가 있으면 고객사나 내부 팀과 논의할 때 “체감” 대신 “데이터”를 근거로 대화할 수 있습니다.
정리: 오늘 바로 운송사 성적표 시트를 만들어 보기
이번 글에서는 앞 편에서 만든 KPI_HISTORY 기반 구조 위에, 운송사별 정시율·노쇼·변경률을 가중치 점수로 바꾸고 A~D 등급을 매기는 로직을 올렸습니다. 흐름을 다시 정리하면 다음과 같습니다.
- KPI 2편에서 만든
getKpiHistory()로 최근 N일 데이터를 읽고,computeCarrierPunctuality_()에서 운송사별 정시·노쇼·변경 통계를 집계합니다. 이때{ startDate: Date, endDate: Date }형태의 객체를 인자로 넘겨주어야 합니다. calcCarrierScore_()와scoreToGrade_()로 각 운송사의 점수·등급을 계산합니다.scoreToGrade_()는 표본 수가 3건 미만이면 “표본 부족”이라고만 표시해, 평가 왜곡을 줄입니다.KPI_updateCarrierScoreSheet()로KPI_CARRIER시트에 운송사별 성적표를 자동으로 기록합니다. 시간 기반 트리거를 걸어 두면 매일 새벽 최신 점수·등급으로 갱신할 수 있습니다.
이렇게 만들어 둔 구글시트 운송사 점수·등급 자동 계산 구조는, 전사 WMS와는 별개로 담당 창고에서 바로 꺼내 쓸 수 있는 보완 도구 역할을 합니다. 오늘 할 수 있는 행동을 하나만 꼽자면, 먼저 KPI_CARRIER_CONFIG 의 가중치와 등급 경계를 본인 창고 상황에 맞게 한 줄씩 검토한 뒤, 이 글의 코드를 프로젝트에 붙여넣고 KPI_updateCarrierScoreSheet 를 실행해 “KPI_CARRIER” 시트를 한 번 만들어 보는 것입니다. 실제 숫자로 나온 운송사별 성적표를 한눈에 보면, 지금까지의 인상과 데이터 사이에 어떤 차이가 있는지 바로 확인할 수 있습니다.