
IT·디지털 자동화 블로그 · 인사·급여 자동화
파이썬으로 급여 계산 자동화하기 – 엑셀 수식 대신 파이썬
왜 파이썬 급여 자동화인가
급여 계산은 규칙이 명확하고 반복적이다. 엑셀 수식은 시트가 복잡해질수록 유지보수가 어려워진다. 파이썬을 사용하면 규칙을 코드와 설정으로 분리해 한 번 표준화하고, 근태·수당·공제 로직을 재사용할 수 있다. 팀 규모가 커지거나 제도 변경이 있어도 설정값만 바꿔 재배포하면 된다.
데이터 모델과 파일 구조
권장 폴더 구조 예시는 다음과 같다.
📁 payroll_project/
├─ config.yaml # 수당/공제율/근무시간 규칙
├─ employees.xlsx # 사번, 이름, 부서, 직급, 기본급, 고정수당
├─ timesheet_2025-10.xlsx # 일자별 근무시간(연장/야간/휴일)
├─ run_payroll.py # 메인 스크립트
└─ outputs/ # 산출물(엑셀, 개별 영수증, 로그)
employees.xlsx 예시:
| 사번 | 이름 | 부서 | 기본급 | 식대 | 직책수당 |
|---|---|---|---|---|---|
| E001 | 홍길동 | 영업 | 2,500,000 | 200,000 | 150,000 |
| E002 | 김리나 | 관리 | 2,200,000 | 200,000 | 0 |
timesheet_YYYY-MM.xlsx 예시(합산 단위 시간):
| 사번 | 연장근로 | 야간근로 | 휴일근로 | 결근시간 |
|---|---|---|---|---|
| E001 | 12 | 6 | 0 | 0 |
| E002 | 8 | 0 | 4 | 2 |
config.yaml 예시:
overtime:
rate: 1.5 # 연장(통상시급 × 1.5)
night:
rate: 0.5 # 야간 가산(통상시급 × 0.5) - 연장과 중복 시 추가 가산
holiday:
rate: 2.0 # 휴일(통상시급 × 2.0)
base:
monthly_hours: 209 # 통상 시급 = 기본급 / 월 소정근로시간
deduction:
pension_rate: 0.045 # 국민연금 개인부담 예시
health_rate: 0.035 # 건강보험 개인부담 예시(예: 장기요양 별도 반영 가능)
employment_rate: 0.009 # 고용보험 개인부담 예시
income_tax_rule: "flat" # 예시: 데모용 고정세율. 실제는 누진/간이세 적용.
income_tax_flat: 0.03 # 데모용 3% 고정. 실제 조직 규정/법규에 맞춰 변경.
환경 설정
pip install pandas openpyxl pyyaml
pandas로 데이터를 합치고, openpyxl로 엑셀 산출물에 서식을 적용한다. pyyaml은 설정 파일 로딩에 사용한다.
급여 계산 로직 설계
- 통상시급 계산: 기본급 / 월 소정근로시간
- 연장수당: 통상시급 × 연장시간 × 1.5
- 야간수당: 통상시급 × 야간시간 × 0.5 (연장과 중복 시 가산분만 별도 더하기)
- 휴일수당: 통상시급 × 휴일시간 × 2.0
- 결근 공제: 통상시급 × 결근시간
- 고정 수당 합산: 식대·직책수당 등
- 세전급여: 기본급 + 고정수당 + 연장/야간/휴일수당 − 결근공제
- 법정 공제: 국민연금·건강보험·고용보험 등 개인부담
- 소득세(데모): flat 또는 단계식. 실제는 간이세/누진세에 맞게 외부표 또는 함수화 권장
- 실수령액: 세전 − 공제 합계
실전 코드: 월별 급여 자동 계산
# file: run_payroll.py
import pandas as pd, yaml, os
from datetime import datetime
CONFIG = "config.yaml"
EMP_FILE = "employees.xlsx"
TS_FILE = "timesheet_2025-10.xlsx"
def load_cfg():
with open(CONFIG, "r", encoding="utf-8") as f:
return yaml.safe_load(f)
def compute_row(row, cfg):
# 통상시급
hourly = row["기본급"] / cfg["base"]["monthly_hours"]
# 수당 계산
overtime_pay = hourly * row["연장근로"] * cfg["overtime"]["rate"]
night_add = hourly * row["야간근로"] * cfg["night"]["rate"]
holiday_pay = hourly * row["휴일근로"] * cfg["holiday"]["rate"]
# 결근 공제
absence_deduct = hourly * row.get("결근시간", 0)
# 고정 수당
fixed_allow = row.get("식대", 0) + row.get("직책수당", 0)
gross = row["기본급"] + fixed_allow + overtime_pay + night_add + holiday_pay - absence_deduct
# 공제(데모 규칙)
pension = gross * cfg["deduction"]["pension_rate"]
health = gross * cfg["deduction"]["health_rate"]
employ = gross * cfg["deduction"]["employment_rate"]
if cfg["deduction"]["income_tax_rule"] == "flat":
income_tax = gross * cfg["deduction"]["income_tax_flat"]
else:
income_tax = 0 # 필요 시 누진 구현
total_deduct = pension + health + employ + income_tax
net = gross - total_deduct
return pd.Series({
"통상시급": round(hourly, 0),
"연장수당": round(overtime_pay, 0),
"야간가산": round(night_add, 0),
"휴일수당": round(holiday_pay, 0),
"결근공제": round(absence_deduct, 0),
"고정수당합": round(fixed_allow, 0),
"세전급여": round(gross, 0),
"국민연금": round(pension, 0),
"건강보험": round(health, 0),
"고용보험": round(employ, 0),
"소득세(데모)": round(income_tax, 0),
"공제합계": round(total_deduct, 0),
"실수령액": round(net, 0)
})
def main():
cfg = load_cfg()
emp = pd.read_excel(EMP_FILE) # 사번, 이름, 부서, 기본급, 식대, 직책수당
ts = pd.read_excel(TS_FILE) # 사번, 연장근로, 야간근로, 휴일근로, 결근시간
df = emp.merge(ts, on="사번", how="left").fillna(0)
results = df.apply(lambda r: compute_row(r, cfg), axis=1)
out = pd.concat([df[["사번","이름","부서","기본급","식대","직책수당","연장근로","야간근로","휴일근로","결근시간"]], results], axis=1)
stamp = datetime.now().strftime("%Y%m")
os.makedirs("outputs", exist_ok=True)
out_path = f"outputs/급여명세_집계_{stamp}.xlsx"
out.to_excel(out_path, index=False)
print("급여 집계 저장:", out_path)
if __name__ == "__main__":
main()
이 스크립트는 직원·근태 데이터를 병합해 세전·세후 급여를 포함한 집계표를 생성한다. 실제 세법·사규에 따른 상세 규칙은 config.yaml로 관리하면 변경 대응이 쉽다.
엑셀 산출물·영수증 자동 생성
개별 급여명세서(영수증) 시트까지 자동 생성하면, 직원별 배포가 간단해진다.
# file: make_receipts.py
import pandas as pd
from openpyxl import Workbook
from openpyxl.styles import Alignment, Font, PatternFill, Border, Side
df = pd.read_excel("outputs/급여명세_집계_202510.xlsx") # 위에서 생성된 집계 파일
wb = Workbook()
ws = wb.active
ws.title = "요약"
# 요약 시트 테이블
ws.append(list(df.columns))
for _, row in df.iterrows():
ws.append(list(row.values))
# 개별 영수증 시트 생성
for _, r in df.iterrows():
name = str(r["이름"])
ss = wb.create_sheet(title=name)
ss.append(["항목","금액"])
items = [
("기본급", r["기본급"]),
("고정수당합", r["고정수당합"]),
("연장수당", r["연장수당"]),
("야간가산", r["야간가산"]),
("휴일수당", r["휴일수당"]),
("결근공제", -r["결근공제"]),
("세전급여", r["세전급여"]),
("국민연금", -r["국민연금"]),
("건강보험", -r["건강보험"]),
("고용보험", -r["고용보험"]),
("소득세(데모)", -r["소득세(데모)"]),
("공제합계", -r["공제합계"]),
("실수령액", r["실수령액"])
]
for k,v in items:
ss.append([k, v])
# 간단 서식
bold = Font(bold=True)
thin = Side(style="thin", color="94a3b8")
border = Border(top=thin, bottom=thin, left=thin, right=thin)
for row in ss.iter_rows(min_row=1, max_row=ss.max_row, max_col=2):
for c in row:
c.border = border
ss["A1"].font = bold; ss["B1"].font = bold
ss.column_dimensions["A"].width = 18
ss.column_dimensions["B"].width = 16
ss["A{}".format(ss.max_row)].font = bold
ss["B{}".format(ss.max_row)].font = bold
for i in range(2, ss.max_row+1):
ss[f"B{i}"].number_format = "#,##0"
wb.save("outputs/개별_급여명세서_202510.xlsx")
print("개별 영수증 저장 완료")
요약 시트와 개별 시트를 함께 저장해 내부 검토와 개인 배포 모두를 충족한다. 필요하면 PDF로 변환해 이메일 자동 발송까지 연결할 수 있다.
검증·로깅·스케줄링
- 검증 규칙 예시: 필수 컬럼 존재 여부, 음수 급여 방지, 부서·사번 포맷 검사
- 로깅: 처리 시작/종료 시각, 직원 수, 평균/합계, 오류 행 내역 기록
- 스케줄링: Windows 작업 스케줄러 또는 cron으로 매월 말일 저녁 실행
# cron 예시: 매월 말일 20:30
30 20 28-31 * * [ $(date +\%d -d tomorrow) = 01 ] && /usr/bin/python3 /path/run_payroll.py >> /path/payroll.log 2>&1
자주 묻는 질문
야간과 연장이 겹치는 시간은 어떻게 처리하나
본 예시는 연장(1.5) + 야간 가산(0.5)을 더해 2.0 수준이 되도록 단순화했다. 조직 규정에 맞게 중복 시간 구간을 분리 계산하는 함수를 별도 구현해 적용하자.
소득세 계산은 실제와 다를 수 있지 않나
맞다. 데모에서는 간단한 고정세율을 사용했다. 실무에서는 간이세/누진세 표를 참조해 함수화하고, 연말정산·비과세 항목 등도 추가 반영해야 한다. 설정 파일로 외부표를 읽어오는 구조가 유지보수에 유리하다.
개별 명세서 PDF 발송까지 자동화할 수 있나
가능하다. openpyxl로 엑셀을 만든 뒤 ReportLab 또는 Excel/LibreOffice 내보내기로 PDF를 생성하고, EmailMessage로 첨부 발송하면 된다. 배포 로그와 반송 처리(메일 실패 재시도)도 함께 설계하자.
다음 단계
다음 편에서는 휴가·지각·조퇴·무급휴일 등 근태 세부 규정과 비과세 항목, 누진세 적용, 개인별 특수 수당까지 확장한 프로덕션 급여 파이프라인을 구현한다.