- MNG 급여관리 시스템 → API 이식 3단계 계획 수립 - Phase 1: 핵심 계산 엔진 (소득세, 4대보험, 공제 오버라이드) - Phase 2: 상태 관리 + 일괄 처리 (unconfirm, unpay, bulkGenerate) - Phase 3: 문서 생성 (PDF 명세서, 전표 변환, 엑셀 내보내기) - INDEX.md에 문서 등록
13 KiB
13 KiB
급여관리 API 구현 계획
작성일: 2026-03-11 상태: 계획 수립 참조: MNG 급여관리 시스템 (
mng/app/Services/HR/PayrollService.php)
1. 개요
1.1 목적
MNG에서 운영 중인 급여관리 시스템의 핵심 비즈니스 로직을 API 서버에 구현한다. React 프론트엔드에서 급여 관리 기능을 사용할 수 있도록 완전한 REST API를 제공한다.
1.2 배경
- MNG 급여관리: 완성도 100% (CRUD, 자동계산, 일괄생성, PDF 명세서, 전표변환)
- API 급여관리: 완성도 ~50% (기본 CRUD만 구현, 핵심 계산 로직 누락)
- React에서 급여관리 화면을 구현하려면 API에 동일한 비즈니스 로직이 필요하다
1.3 원칙
- MNG의 검증된 로직을 API 컨벤션에 맞게 이식한다
- API 프로젝트의 Service-First 아키텍처, i18n, FormRequest 패턴을 준수한다
- 기존
payrolls테이블 스키마를 그대로 사용한다 (추가 마이그레이션 최소화)
2. 현황 분석 (GAP)
2.1 기능 비교
| 기능 | MNG | API | GAP |
|---|---|---|---|
| 급여 CRUD | ✅ | ✅ | - |
| 급여 설정 CRUD | ✅ | ✅ | - |
| 목록 조회 (필터/페이지네이션) | ✅ | ✅ | - |
| 월별 통계 | ✅ | ✅ | - |
확정 (confirm) |
✅ | ✅ | - |
지급 처리 (pay) |
✅ | ✅ | - |
일괄 확정 (bulkConfirm) |
✅ | ✅ | - |
| 소득세 자동 계산 | ✅ | ❌ | 간이세액표 기반 계산 로직 전체 누락 |
| 4대보험 자동 계산 | ✅ | ⚠️ | 설정값만 존재, calculateAmounts() 미구현 |
| 공제 오버라이드 | ✅ | ❌ | 수동 공제 수정 후 재계산 미지원 |
확정 취소 (unconfirm) |
✅ | ❌ | 상태 복구 불가 |
지급 취소 (unpay) |
✅ | ❌ | 슈퍼관리자 기능 누락 |
일괄 생성 (bulkGenerate) |
✅ | ❌ | 재직사원 기반 신규 생성 미구현 |
전월 복사 (copyFromPrevious) |
✅ | ❌ | 이전 월 데이터 복사 미구현 |
| 급여명세서 PDF 생성 | ✅ | ❌ | 데이터 조회만 가능, PDF 미생성 |
| 급여명세서 이메일 발송 | ✅ | ❌ | 이메일 발송 미구현 |
| 전표 자동 생성 | ✅ | ❌ | generateJournalEntry() 미구현 |
| 엑셀 내보내기 | ✅ | ❌ | export 미구현 |
| 공제대상가족수 자동 산출 | ✅ | ❌ | 피부양자 기반 가족수 미산출 |
2.2 API 기존 코드 현황
| 파일 | 상태 | 비고 |
|---|---|---|
Controllers/Api/V1/PayrollController.php |
기본 CRUD 구현 | 누락 엔드포인트 추가 필요 |
Services/PayrollService.php |
기본 CRUD + 제한적 계산 | 핵심 로직 이식 필요 |
Models/Tenants/Payroll.php |
모델 정의 완료 | 상태 헬퍼 메서드 보강 필요 |
Models/Tenants/PayrollSetting.php |
설정 모델 완료 | - |
Requests/V1/Payroll/ |
FormRequest 5개 존재 | 추가 Request 필요 |
routes/api/v1/finance.php |
기본 라우트 정의 | 누락 엔드포인트 추가 |
3. 구현 범위
Phase 1: 핵심 계산 엔진 (필수)
목표: 급여 자동 계산이 동작하도록 핵심 비즈니스 로직을 이식한다.
| # | 작업 | 참조 (MNG) | 대상 파일 (API) |
|---|---|---|---|
| 1-1 | calculateAmounts() 메서드 구현 |
PayrollService:529-590 |
Services/PayrollService.php |
| 1-2 | calculateIncomeTax() 소득세 계산 |
PayrollService:592-670 |
Services/PayrollService.php |
| 1-3 | 4대보험 개별 계산 메서드 | PayrollService:672-720 |
Services/PayrollService.php |
| 1-4 | applyDeductionOverrides() 공제 수동 수정 |
PayrollService:722-760 |
Services/PayrollService.php |
| 1-5 | resolveFamilyCount() 가족수 산출 |
PayrollService:762-800 |
Services/PayrollService.php |
| 1-6 | IncomeTaxBracket 모델 생성 |
Models/HR/IncomeTaxBracket.php |
Models/Tenants/IncomeTaxBracket.php |
| 1-7 | income_tax_brackets 마이그레이션 실행 확인 |
이미 존재 확인 필요 | database/migrations/ |
| 1-8 | store()/update() 에서 자동 계산 적용 |
PayrollService:150-250 |
Services/PayrollService.php |
계산 흐름:
입력: base_salary, overtime_pay, bonus, allowances, deductions
│
├─ Step 1: 총 지급액 = base_salary + overtime_pay + bonus + Σ(allowances)
├─ Step 2: 과세표준 = 총 지급액 - bonus (비과세)
├─ Step 3: 4대보험 = 과세표준 × 요율 (PayrollSetting 참조)
│ ├─ 건강보험 = 과세표준 × 3.545%
│ ├─ 장기요양 = 건강보험 × 0.9082%
│ ├─ 국민연금 = clamp(min, max, 과세표준) × 4.5%
│ └─ 고용보험 = 과세표준 × 0.9%
├─ Step 4: 근로소득세 = 간이세액표 조회 (가족수 반영)
│ ├─ < 770천원: 0원
│ ├─ 770~10,000천원: DB 간이세액표
│ └─ > 10,000천원: 소득세법 시행령 별표2 공식
├─ Step 5: 지방소득세 = 근로소득세 × 10%
├─ Step 6: 총 공제액 = 4대보험 + 세금 + Σ(deductions)
└─ Step 7: 실수령액 = 총 지급액 - 총 공제액
※ 모든 금액: 10원 단위 절삭 (floor)
Phase 2: 상태 관리 + 일괄 처리
| # | 작업 | 참조 (MNG) | 비고 |
|---|---|---|---|
| 2-1 | unconfirm() 확정 취소 |
PayrollService:340-360 |
confirmed → draft |
| 2-2 | unpay() 지급 취소 |
PayrollService:380-400 |
paid → draft (슈퍼관리자) |
| 2-3 | bulkGenerate() 재직사원 일괄 생성 |
PayrollService:442-521 |
Employee 연봉 기반 |
| 2-4 | copyFromPreviousMonth() 전월 복사 |
PayrollService:402-440 |
soft-delete 처리 포함 |
| 2-5 | Payroll 모델에 상태 헬퍼 메서드 추가 | Models/HR/Payroll.php |
isEditable(), isConfirmable() 등 |
일괄 생성 로직:
bulkGenerate(year, month)
│
├─ 1. PayrollSetting 조회
├─ 2. 활성 재직사원 전체 조회
├─ 3. 각 사원별:
│ ├─ 이미 존재 → skip
│ ├─ soft-deleted 존재 → forceDelete 후 재생성
│ ├─ 기본급 = 연봉 / 12
│ ├─ calculateAmounts() 호출
│ └─ Payroll 생성 (status: draft)
└─ 4. 결과: {created: N, skipped: M}
Phase 3: 문서 생성 + 내보내기
| # | 작업 | 참조 (MNG) | 비고 |
|---|---|---|---|
| 3-1 | sendPayslip() 급여명세서 PDF + 이메일 |
PayrollService:820-920 |
DomPDF + Pretendard |
| 3-2 | generateJournalEntry() 전표 자동 생성 |
PayrollController:900-1088 |
분개 구조 동일 |
| 3-3 | export() 엑셀 내보내기 |
PayrollService:100-140 |
동적 열 포함 |
| 3-4 | 급여명세서 Blade 뷰 생성 | emails/payslip.blade.php |
PDF 폰트 정책 준수 |
| 3-5 | PayslipMail Mailable 생성 | Mail/PayslipMail.php |
4. API 엔드포인트 설계
4.1 추가 엔드포인트
기존 라우트(routes/api/v1/finance.php)에 추가할 엔드포인트:
| Method | URI | 설명 | Phase |
|---|---|---|---|
| POST | /v1/payrolls/{id}/unconfirm |
확정 취소 | 2 |
| POST | /v1/payrolls/{id}/unpay |
지급 취소 (슈퍼관리자) | 2 |
| POST | /v1/payrolls/bulk-generate |
재직사원 일괄 생성 | 2 |
| POST | /v1/payrolls/copy-from-previous |
전월 복사 | 2 |
| POST | /v1/payrolls/{id}/send-payslip |
급여명세서 이메일 발송 | 3 |
| POST | /v1/payrolls/generate-journal-entry |
전표 자동 생성 | 3 |
| GET | /v1/payrolls/export |
엑셀 내보내기 | 3 |
4.2 기존 엔드포인트 수정
| URI | 변경 내용 | Phase |
|---|---|---|
POST /v1/payrolls |
calculateAmounts() 자동 적용 |
1 |
PUT /v1/payrolls/{id} |
공제 오버라이드 지원 | 1 |
POST /v1/payrolls/calculate |
소득세 포함 전체 계산으로 개선 | 1 |
4.3 요청/응답 예시
급여 등록 요청 (POST /v1/payrolls):
{
"user_id": 15,
"pay_year": 2026,
"pay_month": 3,
"base_salary": 3500000,
"overtime_pay": 500000,
"bonus": 200000,
"allowances": [
{"name": "교통비", "amount": 100000}
],
"deductions": [
{"name": "대출상환", "amount": 300000}
],
"deduction_overrides": {
"pension": 180000,
"health_insurance": null
}
}
자동 계산 응답 (POST /v1/payrolls/calculate):
{
"success": true,
"data": {
"gross_salary": 4300000,
"taxable_base": 4100000,
"pension": 184500,
"health_insurance": 145345,
"long_term_care": 13200,
"employment_insurance": 36900,
"income_tax": 78340,
"resident_tax": 7830,
"total_deductions": 766115,
"net_salary": 3533885,
"family_count": 2
}
}
5. 데이터베이스
5.1 기존 테이블 (변경 불필요)
payrolls— 이미 모든 필드 존재 (options JSON 컬럼 포함)payroll_settings— 설정 테이블 완비
5.2 확인 필요
| 테이블 | 상태 | 조치 |
|---|---|---|
income_tax_brackets |
마이그레이션 존재 확인 필요 | 없으면 생성 + 2024 간이세액표 시딩 |
payrolls.long_term_care |
2026-02-27 추가 완료 | - |
payrolls.options |
2026-03-10 추가 완료 | - |
5.3 간이세액표 시딩
income_tax_brackets 테이블에 2024년 국세청 간이세액표 데이터가 필요하다.
- 770천원 ~ 10,000천원 구간
- 가족수 1~11명별 세액
- MNG에 이미 시더 존재 → API로 이관
6. 추가 생성 파일
6.1 Phase 1
| 파일 | 설명 |
|---|---|
app/Models/Tenants/IncomeTaxBracket.php |
간이세액표 모델 |
app/Http/Requests/V1/Payroll/BulkGenerateRequest.php |
일괄 생성 요청 |
app/Http/Requests/V1/Payroll/CopyFromPreviousRequest.php |
전월 복사 요청 |
6.2 Phase 3
| 파일 | 설명 |
|---|---|
app/Mail/PayslipMail.php |
급여명세서 Mailable |
resources/views/emails/payslip.blade.php |
급여명세서 PDF 뷰 |
resources/views/emails/payslip-notification.blade.php |
이메일 본문 |
app/Exports/PayrollExport.php |
엑셀 내보내기 |
7. 주의사항
7.1 필수 준수
- ✅ 마이그레이션은 API 프로젝트에서만 생성 (CLAUDE.md 규칙)
- ✅ PDF 생성 시 Pretendard 폰트 +
ensureKoreanFont()적용 (폰트 정책) - ✅ 모든 응답 메시지는 i18n 키 사용 (
__('message.xxx')) - ✅
ApiResponse::handle()패턴 사용 - ✅ FormRequest로 입력 검증
7.2 MNG 코드 이식 시 변환 규칙
| MNG 패턴 | API 패턴 |
|---|---|
auth()->id() |
$this->apiUserId() |
session('tenant_id') |
$this->tenantId() |
| 직접 JSON 응답 | ApiResponse::success() / ApiResponse::handle() |
| 하드코딩 한글 메시지 | __('message.payroll.xxx') |
| HTMX 부분 렌더링 | JSON 응답 전용 |
Payroll::query() |
Payroll::query()->forTenant($this->tenantId()) |
7.3 Salary 모델과의 관계
Payroll= 상세 급여 관리 (세금/보험 자동 계산, MNG 연동)Salary= React용 간소화 급여 현황 (별도 유지)- 두 모델은 독립적으로 운영하며, 추후 통합 여부 검토
8. 작업 순서 (권장)
Phase 1 (핵심 계산) ─────────────────────────────────────
1-6. IncomeTaxBracket 모델 생성
1-7. 간이세액표 마이그레이션/시딩 확인
1-1. calculateAmounts() 구현
1-2. calculateIncomeTax() 구현
1-3. 4대보험 계산 메서드 구현
1-4. applyDeductionOverrides() 구현
1-5. resolveFamilyCount() 구현
1-8. store()/update()에 자동 계산 적용
─── 테스트: 급여 등록 → 자동 계산 검증 ───
Phase 2 (상태 + 일괄) ──────────────────────────────────
2-5. Payroll 모델 상태 헬퍼 추가
2-1. unconfirm() 구현 + 라우트
2-2. unpay() 구현 + 라우트
2-3. bulkGenerate() 구현 + 라우트
2-4. copyFromPreviousMonth() 구현 + 라우트
─── 테스트: 상태 전이, 일괄 생성 검증 ───
Phase 3 (문서 + 내보내기) ──────────────────────────────
3-4. 급여명세서 Blade 뷰 생성
3-1. sendPayslip() PDF + 이메일 구현
3-2. generateJournalEntry() 전표 생성 구현
3-3. export() 엑셀 내보내기 구현
─── 테스트: PDF 생성, 이메일 발송, 전표 검증 ───
관련 문서
- 급여관리 기능 문서 — MNG 급여관리 상세
- API 개발 규칙 — Service-First, FormRequest 패턴
- DB 스키마 — 인사 — payrolls 테이블 구조
- PDF 폰트 정책 — DomPDF 한글 폰트
- options 컬럼 정책 — JSON 확장 필드
최종 업데이트: 2026-03-11