잔업수당은 어떻게 계산할까? 시급(기본급÷209시간)의 150%가 기본 구조입니다. 이 글에서는 계산 원리와 실제 예시, 그리고 기본급만 넣으면 월별·연간 정산이 자동으로 되는 PWA 계산기를 직접 만드는 방법까지 정리합니다.
매달 급여명세서를 받을 때마다 "이번 달 잔업수당이 제대로 계산된 건가?"라는 의문이 듭니다. 급여명세서에는 최종 금액만 적혀 있을 뿐, 이 숫자가 어떤 기준으로 나왔는지는 알 수 없습니다. 이 글에서는 ① 잔업수당의 계산 원리를 이해하고, ② 실제 숫자로 직접 계산해 보고, ③ 매일 잔업시간만 기록하면 월별·연간 정산을 자동으로 해주는 계산기(PWA)를 직접 만들어 봅니다.
잔업수당 계산은 생각보다 단순합니다. 세 가지만 알면 됩니다.
시급 = 기본급 ÷ 209시간
잔업단가 = 시급 × 1.5
월 잔업수당 = 일별 잔업시간 합 × 잔업단가
왜 209시간인가. 한 달 소정근로시간은 주 40시간 기준 약 174시간입니다. 여기에 주휴수당에 해당하는 시간(약 35시간)을 더하면 209시간이 됩니다. 즉 "기본급 ÷ 209"는 주휴가 이미 포함된 시급을 구하는 공식입니다.
기본급 250만 원, 이번 달 잔업 10시간이라고 가정합니다.
시급 = 2,500,000 ÷ 209 = 11,962원
잔업단가 = 11,962 × 1.5 = 17,943원
잔업수당 = 17,943 × 10 = 179,430원
이 계산 결과와 급여명세서의 잔업수당 항목을 대조하면 회사 계산이 맞는지 바로 확인할 수 있습니다. 매달 이걸 엑셀로 반복하는 게 번거롭다면, 다음 섹션처럼 웹앱으로 자동화할 수 있습니다.
순수 HTML/CSS/JavaScript로 만듭니다. 서버가 필요 없고, 데이터는 브라우저에 저장됩니다.
const MONTHLY_HOURS = 209; // 월 소정근로시간 (주휴 포함)
const OVERTIME_RATE = 1.5; // 연장근로 가산 50%
function calcRates(baseSalary) {
const hourly = baseSalary / MONTHLY_HOURS;
return { hourly, overtime: hourly * OVERTIME_RATE };
}
// 일별 잔업 기록을 받아 월 정산금액을 계산
function monthlySettlement(logs, baseSalary) {
const { overtime } = calcRates(baseSalary);
const totalHours = logs.reduce((sum, l) => sum + l.hours, 0);
return { totalHours, amount: Math.round(totalHours * overtime) };
}
정산의 정확성은 결국 정확한 잔업시간 기록에서 나옵니다. 그래서 계산기보다 "기록 도구"에 가깝게 설계했습니다. 달력 셀을 탭하면 30분씩 추가되고, 다시 탭하면 초기화되는 방식입니다. 모바일에서 가장 많이 쓸 것을 예상해 430px 기준 앱형 레이아웃으로 만들었습니다.
// 날짜별 잔업시간 상태. 예: { '2026-07-15': 2.5 }
const logs = JSON.parse(localStorage.getItem('overtime-logs') || '{}');
function addLog(date) {
logs[date] = (logs[date] || 0) + 0.5;
save();
render();
}
function save() {
localStorage.setItem('overtime-logs', JSON.stringify(logs));
}
캘린더에 기록된 잔업시간을 월별로 모아 월 정산 요약과 연간 정산 표를 자동으로 그려줍니다. 1~12월 각 행에 잔업시간·수당·총 수령액이 나오고, 하단에 연간 합계가 정리됩니다. 월마다 기본급이 다르면(승급·이직) 해당 월 값을 따로 입력할 수 있게 설계했습니다.
// 월별 잔업시간 합 → 연간 정산 표 생성
function renderAnnual() {
let tH = 0, tOt = 0, tFin = 0;
for (let m = 1; m <= 12; m++) {
const hourly = getSalary(m) / 209;
const h = getMonthHours(m); // 해당 월 잔업시간 합
const ot = Math.floor(h * hourly * 1.5);
const fin = getSalary(m) + ot;
tH += h; tOt += ot; tFin += fin;
// tbody에 "m월 | h | ot | fin" 행 추가
}
// 합계 행 갱신
}
급여명세서와 대조할 때도 유용합니다. 예를 들어 4월에 잔업 18시간이라면 표에는 18h → 322,966원으로 표시되고, 명세서의 잔업수당 항목과 바로 비교할 수 있습니다.
잔업 기록·기본급·연차 데이터는 별도 서버 없이 브라우저 LocalStorage에 저장됩니다. 리로드해도, 다음 날 다시 열어도 그대로 유지되죠. 다만 LocalStorage는 브라우저를 지우면 사라지기 때문에, 기기 변경·초기화에 대비한 백업 기능을 넣었습니다. 하단의 💾 데이터 버튼을 누르면 내보내기 / 불러오기 / 초기화 3가지가 있는 데이터 관리 화면이 열립니다.
내보내기는 잔업·연차 전체 데이터를 JSON 파일(잔업정산_데이터_20260814.json 형식)로 다운로드하고, 불러오기는 그 파일을 다시 읽어 복원합니다. 파일 형식을 검증한 뒤에만 덮어쓰도록 안전장치도 있습니다.
// 내보내기: 전체 데이터를 JSON 파일로 저장
function exportData() {
const exportObj = {
version: '1.0',
exportedAt: new Date().toISOString(),
common: { salary, joinDate, leaveUses },
hours: hourData,
};
const blob = new Blob([JSON.stringify(exportObj, null, 2)],
{ type: 'application/json' });
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = `잔업정산_데이터_${dateStr}.json`;
a.click();
}
// 불러오기: 파일을 읽어 데이터 복원
function importData(e) {
const file = e.target.files[0];
const reader = new FileReader();
reader.onload = (ev) => {
const data = JSON.parse(ev.target.result);
if (!data.common || !data.hours) throw new Error('형식 오류');
// common(기본급·입사일·연차)과 hours(잔업기록) 복원
Object.assign(hourData, data.hours);
localStorage.setItem('ot3-hours', JSON.stringify(hourData));
buildAll(); // 화면 전체 다시 그리기
};
reader.readAsText(file);
}
잔업과 함께 챙겨야 할 게 연차입니다. 입사일만 입력하면 근속 기간에 따른 발생 연차를 자동으로 계산하고, 사용한 날을 기록해서 남은 연차를 실시간으로 보여줍니다. 입사 1년 미만은 월 만근 시 1일씩(최대 11일), 1년 이상은 15일이 발생하는 법정 기준을 반영했습니다.
// 입사일 기준 발생 연차 계산 (법정 최소 기준)
function calcLeave(joinDate, now = new Date()) {
const months = diffMonths(joinDate, now);
const accrued = months < 12
? Math.min(months, 11) // 1년 미만: 월 1일, 최대 11일
: 15; // 1년 이상: 15일
const used = leaveLogs.reduce((s, l) => s + l.days, 0);
return { accrued, used, remaining: accrued - used };
}
연차 사용 기록도 날짜·메모와 함께 저장하고, 목록을 내려서 전체 내역을 확인할 수 있습니다. 급여명세서의 연차 항목과 대조하면 잔여 연차가 정확한지 바로 알 수 있습니다.
manifest.json과 service worker를 추가하면 홈 화면에 설치해서 앱처럼 쓸 수 있습니다. 설치가 끝나면 홈 화면 아이콘을 누르는 것만으로 주소창 없이 바로 열리고, 한 번 열린 뒤에는 캐시된 파일로 오프라인에서도 사용할 수 있습니다.
{
"name": "잔업 정산기",
"short_name": "잔업정산기",
"description": "기본급만 넣으면 잔업수당이 자동 계산되는 PWA",
"start_url": "/",
"display": "standalone",
"background_color": "#0b1020",
"theme_color": "#0b1020",
"icons": [
{ "src": "/icon-192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "/icon-512.png", "sizes": "512x512", "type": "image/png" }
]
}
// service-worker.js — 설치 시 파일을 캐시에 저장하고, 이후엔 캐시부터 응답
const CACHE = 'janyeop-v1';
const ASSETS = ['/', '/index.html', '/icon-192.png', '/icon-512.png'];
self.addEventListener('install', (e) => {
e.waitUntil(caches.open(CACHE).then((c) => c.addAll(ASSETS)));
self.skipWaiting();
});
self.addEventListener('activate', (e) => {
e.waitUntil(
caches.keys().then((keys) =>
Promise.all(keys.filter((k) => k !== CACHE).map((k) => caches.delete(k)))
)
);
self.clients.claim();
});
self.addEventListener('fetch', (e) => {
if (e.request.method !== 'GET') return;
e.respondWith(caches.match(e.request).then((hit) => hit || fetch(e.request)));
});
// 등록 코드 — 기준을 충족하면 브라우저가 설치 아이콘을 자동으로 보여줍니다
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/service-worker.js');
}
| 기능 | 구현 방식 |
|---|---|
| 시급·잔업단가 | 기본급 ÷ 209시간, ×1.5 자동 산출 |
| 잔업 기록 | 캘린더 UI에서 날짜별 입력 |
| 월별·연간 정산 | 시간·수당·총 수령액 테이블 |
| 데이터 보존 | LocalStorage + 내보내기/가져오기 |
| 연차 관리 | 입사일 기준 발생 연차 자동 계산 + 사용 기록 |
| 설치 | PWA — 홈 화면 설치 지원 |
처음부터 완벽하게 계산되지는 않았습니다. 실제로 만들면서 부딪힌 부분을 공유합니다.
209시간의 함정. 처음에는 주 40시간 × 4.345주 = 약 174시간만 적용했습니다. 그런데 이 값은 주휴수당이 빠진 기준이라 시급이 커져 수당이 과대계상됩니다. 주휴를 포함한 209시간이 표준이며, 이 차이를 놓치면 매달 수만 원씩 다르게 계산됩니다.
회사마다 다른 규정. 연차 발생 규정이 회사마다 달라 법정 최소 기준으로 계산하되, 사용자가 직접 조정할 수 있게 열어뒀습니다. "정답"을 강제하지 않는 게 오히려 실제 사용률을 높였습니다.
계산기보다 기록 도구. 처음에는 "기본급 넣고 잔업시간 넣으면 끝"인 계산기부터 만들었습니다. 하지만 잔업시간을 기억해서 넣는 일이 오히려 더 어려웠고, 캘린더에 매일 탭 한 번으로 기록하는 형태로 바꾼 뒤에야 실제로 쓰기 시작했습니다.
만든 뒤 가장 큰 변화는 잔업에 대한 인식이었습니다. 숫자로 매달 쌓이는 걸 보니 "적당히 하자"던 태도가 "이 시간은 내 시간"이라는 생각으로 바뀌었습니다. 그리고 급여명세서의 잔업수당을 계산 결과와 대조하다 보니 실제로 회사 계산과 다른 부분을 발견해 정정 요청한 적도 있었습니다.
매달 1분만 투자해서 직접 계산해 보세요. 기본급과 잔업시간 두 가지만 있으면 됩니다.
잔업수당 계산, 이제 직접 해보세요. 기본급과 잔업시간만 기록하면 자동으로 정산됩니다.