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

파이썬으로 급여 계산 자동화하기 – 엑셀 수식 대신 파이썬

by jbparkbill 2025. 11. 2.
반응형

파이썬으로 급여 계산 자동화하기 – 엑셀 수식 대신 파이썬
파이썬으로 급여 계산 자동화하기 – 엑셀 수식 대신 파이썬

 

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. 통상시급 계산: 기본급 / 월 소정근로시간
  2. 연장수당: 통상시급 × 연장시간 × 1.5
  3. 야간수당: 통상시급 × 야간시간 × 0.5 (연장과 중복 시 가산분만 별도 더하기)
  4. 휴일수당: 통상시급 × 휴일시간 × 2.0
  5. 결근 공제: 통상시급 × 결근시간
  6. 고정 수당 합산: 식대·직책수당 등
  7. 세전급여: 기본급 + 고정수당 + 연장/야간/휴일수당 − 결근공제
  8. 법정 공제: 국민연금·건강보험·고용보험 등 개인부담
  9. 소득세(데모): flat 또는 단계식. 실제는 간이세/누진세에 맞게 외부표 또는 함수화 권장
  10. 실수령액: 세전 − 공제 합계
연장과 야간이 겹치는 시간은 중복계산을 주의한다. 본 예시는 단순화를 위해 야간 가산분(0.5)만 추가로 더하는 구조를 사용한다.

실전 코드: 월별 급여 자동 계산

# 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로 첨부 발송하면 된다. 배포 로그와 반송 처리(메일 실패 재시도)도 함께 설계하자.

다음 단계

다음 편에서는 휴가·지각·조퇴·무급휴일 등 근태 세부 규정과 비과세 항목, 누진세 적용, 개인별 특수 수당까지 확장한 프로덕션 급여 파이프라인을 구현한다.

다음 글 보러 가기

이 글의 예제는 pandas·openpyxl·pyyaml 기반으로 작성되었다. 실제 조직의 규정과 세율은 설정 파일로 분리해 관리하고, 배포 전 충분한 샘플 검증을 권장한다.

반응형