Skip to content

HBHR280_법정공제 시트 (4대보험·소득세 월별) ​

(Hereby) HR 급여 관리 > 시트 워크스페이스 > 법정공제 시트

앱HR 급여 관리 (hr-system, 포트 4000)
대상 사용자공제 시트 조회·편집·확정 capability 보유자(실제 RBAC 토큰 TBU)
프로토타입mockup-deduction-v2.2.html 장면 ⓪→① — HBHR280 법정공제 시트
관련 정책[[POL_공제_경계]] · [[POL_공제_항목·요율]] · [[POL_공제_산정기초·비과세]] · [[POL_공제_판정]] · [[POL_공제_계산·확정]] · [[POL_공제_잠정·정산]]
Statusdraft
Owner카나
부모[[HBHR100_시트 워크스페이스 (공통 Shell)]]
하위 또는 관련[[HBHR281_판정 상세·수동 판정 모달]] · [[HBHR282_계산 진단 패널 드로어]] · [[HBHR290_병행운영 대사 리포트]] · [[HBAD271_법정 요율 안내]] · [[HBAD272_공제 산정 모드]]

개요 ​

  • 정책상 법정공제 시트는 재직자 전원 × 상시 6종(국민연금·건강보험·장기요양보험·고용보험·소득세·지방소득세)의 판정과 계산을 확인하는 작업공간이다. 판정·계산 규칙은 이 문서가 재정의하지 않고 위 정책과 FIG 설계를 따른다.
  • 구현 상태를 구분한다. feat/statutory-deduction-poc에는 계산·API·커밋 PoC 근거가 있으나 dev 미배포다. 판정 배지, 요율 hover, 진단·대사 진입을 포함한 본 UI는 목업/사양이며 라이브 구현으로 간주하지 않는다.
  • 시트의 계산값은 preview이고, 한 번의 커밋에서 상시 6종 모두 DeductionRecord가 된다. seed-sheet-templates.ts에서 4대보험 4열과 income_tax·local_income_tax 2열 모두 commitToKind="deduction"으로 확인됐다.
  • 고용보험 fixture는 귀속월 유효 요율이 있는 상태로 통일한다. 유효 요율 부재 시에는 금액과 정상 상태를 동시에 보여 주지 않는다.
  • 코드 근거 시점: 로컬 hereby-dhub commit 0840824d38051acf1dba547015c7f70614f5a930 (2026-08-22 13:26:38 KST). 카나의 변경 완료 보고는 현재 Confluence 저장본문 근거로 채택하지 않고, 생성 동선은 사용자 채널 지적/본 개정 요청으로 추적한다.
  • insurable_gross는 현재 템플릿·급여대장의 보험료 산정기초 구현 키다. 이를 과세소득과 법적으로 동치라고 정의하지 않는다.

문서 히스토리 ​

날짜작성자팀변경 내용
2026-08-24카나PM최초 작성
2026-08-25RubyProduct인수 리뷰 반영: 구현 표기, 6종 커밋, 항목별 판정, fail-safe, 공통 계약·상태 교정
2026-09-06RubyProduct로컬 리뷰 v2.1: 누락된 생성 동선·데이터 방향 추가, system template 시작 상태 불일치와 미결 제안 분리
2026-09-29카나PM공제 베타 dev 배포 관측(#907/#912, v0.14.0) — "dev 미배포·PoC" 전제 붕괴. okbest dev 추천 템플릿 21종에 「법정공제 — 4대보험 (월별)」 [베타] 노출 실측. 구현 대비 차이는 OQ로만 기록(본문 재작성은 정규 배치에서)

정책 → 로직 → UI 경계 ​

층위이 문서에서 따르는 내용
정책전 직원 × 6종 판정, 제외 사유 기록, 유효 요율·세액표가 없는 확정 금지, 확정 시 근거 동결
비즈니스 로직귀속월 기준 판정·요율/표 조회, 6열 계산, 게이트 검증, 6종 원장 커밋, stale 판정
UI 로직판정·경고·계산값을 표시하고 상세/진단/대사로 연결하며 커밋 결과를 피드백
사양아래 필드·상태·분기. procedure명·RBAC 토큰·문구 원문은 확인 전 TBU

실제 Hereby 화면 구조 재사용 ​

  • [[HBHR100_시트 워크스페이스 (공통 Shell)]]의 실제 구조를 그대로 쓴다: 상단 월 선택 툴바 → 좌측 시트 사이드바 → 가운데 sheet-grid → 우측 고정 레일의 3단 구조다. 법정공제 전용 페이지 Shell을 새로 만들지 않는다.
  • 시트명·수명주기 배지·검색·메모/셀 상세·히스토리·반영 상태는 공통 시트 헤더/툴바를 재사용한다.
  • 확정 CTA 명칭·위치는 실제 그리드의 툴바 우측 급여대장에 반영 버튼과 반영 필요/반영됨/수당 변경됨 — 재반영 필요 라벨을 따른다. 별도 하단 커밋 바를 만들지 않는다.
  • 판정/진단 상세가 레일형이면 기존 w-80 우측 레일 슬롯을 사용하고 셀 상세·메모·열 설정·급여 반영·행 정보·히스토리 레일과 상호 배제한다. 정확한 진입 버튼과 레일 콘텐츠는 미구현/TBU다.
  • 상태 배지는 기존 .ds-label, 버튼은 기존 ds-btn, 툴팁은 기존 DS tooltip을 사용한다. 이 사양은 새 배지·버튼·테이블 계열을 정의하지 않는다.

생성·진입 프로세스 (v2.1 추가) ​

⚠ 별도 "공제 시트 없음" 진입 화면은 없다 (콸 지적 2026-09-06). 공제 시트는 도메인 전용 빈 화면/준비 화면으로 진입하지 않고, 급여 워크스페이스의 기존 시트 생성 동선([[HBHR101_새 시트 만들기 모달]])을 그대로 재사용한다 — 다른 시트 유형과 동일. 시트가 하나도 없을 때 뜨는 것은 워크스페이스 공통 빈 상태("시트가 없어요")뿐이며, "이 달에는 공제 시트가 없어요" 같은 공제 전용 빈 화면을 새로 만들지 않는다(실존하지 않는 UI 금지 — 기획 대원칙).

검증된 현재 구현 ​

  1. 워크스페이스의 + 새 시트 만들기로 DS 모달을 연다 (HBHR101 실존 모달 — 공제 전용 진입점 아님).
  2. 모달의 4개 탭 중 추천 템플릿(mode="system")을 선택한다. sheetTemplate.list({ includeSystem: true })가 active system template을 조직 템플릿과 함께 조회한다.
  3. 「법정공제 — 4대보험 (월별)」 카드를 클릭한다. UI applyTemplate(tpl)이 현재 연·월로 sheetTemplate.apply(SDK alias; router 구현 applyToPeriod)를 호출한다. 카드 클릭 자체가 적용 액션이며 별도 확인 CTA는 코드에서 확인되지 않았다.
  4. 서버가 템플릿 레이아웃·slug(statutory_insurance)를 현재 기간 SheetView로 복제한다. slug가 이미 있으면 같은 기간 기존 시트를 재사용한다. 완료 후 목록을 새로 읽고 생성/재사용 시트를 연다.
  5. 템플릿 identity 열의 sourceTable:"employee"가 materializer를 구동한다. materializer는 조직 범위에서 비활성 직원을 제외하고, 해당 월 말보다 먼저 입사한 직원을 1인 1행으로 만든다. 따라서 재직자 auto-populate는 시트 단위 defaultAutoPopulate가 아니라 컬럼 source binding 근거다.

시작 상태 — 확정: 사용 중 시작 (2026-09-06 코드 확증) ​

  • 확정: system template(법정공제 포함)은 적용 즉시 setupCompletedAt = new Date()로 저장 → 생성 즉시 「사용 중」 시작. develop 코드 확인(packages/api/src/routers/sheet-template.router.ts:252·391 — tpl.isSystem ? new Date() : null). 기획 초기의 「설정 중 시작」 표현은 폐기(일반 시트 기준 오인계, 카나 정정).
  • 템플릿 구조를 사용자가 조정해야 하는 제품 결정이 확정되면 system template에도 setupCompletedAt=null을 적용할지, 시스템 소유 구조를 유지할지 TBU다. 설정 중이면 commitToRecords가 서버에서 반영을 거부하므로 설정 완료 → 반영 순서를 함께 정의해야 한다.

데이터 방향 ​

  • 급여대장 → 공제시트는 산정기초 읽기, 공제시트 → 공제기록 → 급여대장은 반영 결과 집계 방향이다. insurable_gross 명칭만으로 과세소득과 법적 동일성을 주장하지 않는다.

Data I/O ​

Server → Client ​

변수근거/의미값 없을 때비고
${sheet.rows}조직 범위의 재직자 auto-populate 행Empty다른 조직 데이터 혼입 금지
${judgment.matrix}직원 × 상시 6종의 자동/수동 판정, 근거, 판정 입력 완전성입력 부재면 계산 유지 + 경고커밋 전 미해소 판정불가는 fail-safe 차단
${statutory.rates}귀속월에 유효한 4대보험 요율·상하한해당 항목 N/A + 커밋 차단고용보험 fixture는 유효 상태
${tax.table}귀속월에 유효한 간이세액표·자녀 차감 파라미터소득세/지방소득세 미계산 + 커밋 차단조용한 0 금지
${insurable_gross}급여대장 「보험료 기초(과세보수)」목표: 실패/경고현재 PoC fallback과 목표 분리
${commitStatus}커밋 여부, upstream 신선도, version/lockstale/conflict/locked 표시조직·귀속월 단위

현재 PoC와 목표 동작 ​

상황현재 PoC 관찰제품 목표 사양
급여대장 미커밋/기초 부재계약 기본급 fallbackfallback하지 않고 계산/확정 전 명시적 실패 또는 경고. 최종 차단 시점·문구 TBU
판정 입력 부재제외가 명시적으로 참이 아니면 계산 유지계산 유지 + 판정불가 경고, 커밋 전 반드시 해소
귀속월 요율 부재N/A/커밋 게이트 근거 있음해당 항목 N/A, 한 커밋 전체 차단, 기존 확정분 불변
소득세 표/구간 부재셀 비움·warn소득세/지방소득세 미계산, 한 커밋 전체 차단

Client → Server와 공통 mutation 계약 ​

액션제품 capability전달 값계약
리터럴 셀 편집공제 시트 편집organization context, sheetViewId, rowId, columnKey, value, expectedVersion, idempotencyKeyprocedure·토큰 TBU
재직자 채우기/재계산공제 시트 편집organization context, sheetViewId, expectedVersion, idempotencyKey서버가 조직 scope 재검증
6종 커밋공제 확정organization context, sheetViewId, effectiveMonth, expectedVersion, idempotencyKey게이트 통과 후 단일 트랜잭션
  • organizationId는 인증 컨텍스트에서 확정하며 클라이언트 값만 신뢰하지 않는다.
  • capability는 제품 의미로만 적는다. 실제 RBAC 토큰은 census 전 확정하지 않는다.
  • mutation은 idempotencyKey로 재시도 중복을 막고 expectedVersion으로 낙관적 잠금을 수행한다. 불일치는 conflict, 마감/외부 잠금은 locked로 응답한다.
  • audit envelope 최소 필드: organizationId, actorId, occurredAt, action, resourceType/resourceId, effectiveMonth, before, after, reason(해당 시), source/provenance(해당 시), idempotencyKey, requestId, version.

6종 커밋 앵커 ​

항목source columntarget column결과
국민연금npsded_statutory_npsDeductionRecord
건강보험hided_statutory_hiDeductionRecord
장기요양보험ltcded_statutory_ltcDeductionRecord
고용보험uided_statutory_uiDeductionRecord
소득세incomeTax / income_taxded_statutory_income_taxDeductionRecord
지방소득세localTax / local_income_taxded_statutory_local_taxDeductionRecord

모든 앵커는 payroll_ledger를 대상으로 하고 commitToKind="deduction"이다. 따라서 예상 건수는 대상 직원 n명 × 6종이며, 17명 fixture이면 102건이다. 일부 항목이 제외 판정으로 0원이어도 판정 기록 요건과 실제 레코드 생성/0원 보존 규칙은 별도 확인이 필요하다(Open Question).

화면 플로우 (Mermaid) ​

컬럼 정의 ​

그룹컬럼동작
시스템#·사번·성명·팀조직 범위 read-only
판정국민연금·건강보험·장기요양보험·고용보험·소득세·지방소득세 판정각 항목 적용/제외/판정불가와 근거. 클릭 시 [[HBHR281_판정 상세·수동 판정 모달]]
판정 입력생년월일·입사일·주 소정근로시간·급여 마감일 등숨김 입력. 항목마다 필요한 입력이 다름
기초보험료 기초(과세보수)[payroll_ledger:insurable_gross] 근거. 정확 slug는 TBU
계산국민연금·건강보험·장기요양보험·고용보험귀속월 요율·상하한. 고용보험 유효 fixture 0.9% 예시
세액 입력부양가족수·8~20세 자녀수·원천징수비율입력 완전성 검증
세액 계산소득세·지방소득세간이세액표/초과 산식·지방세 계산
커밋툴바 우측 급여대장에 반영상시 6종 단일 커밋

법정공제 시트 사양표 ​

번호항목/기능명사양 및 기능 설명구현 표기
00노출 조건공제 시트 조회 capability와 organization scope 확인UI TBU
01생성 진입+ 새 시트 만들기 → 추천 템플릿 → 법정공제 카드 클릭 적용 → 현재 월 시트 열기UI·router 코드 검증
02재직자 행 구성employee source binding으로 조직·기간 범위의 재직자 1인 1행 materialize템플릿·materializer 코드 검증
03시작 상태system template은 적용 즉시 「사용 중」 시작 (setupCompletedAt 즉시 설정)✅ 코드 확증(sheet-template.router.ts:252·391)
03b시트 생성 방식현재 수동 생성(+새 시트 만들기 → 추천 템플릿). 급여대장은 자동 생성이나 공제 시트는 수동 → 미생성 월 통누락 리스크📋 미결: 월 자동 생성+미반영 마감 경고 제안([[POL_공제_판정]]·기획서 §7)
10항목별 판정6종 각각 적용/제외/판정불가. 공통 배지 하나로 4대보험을 함께 0 처리하지 않음판정 계산 PoC 근거 / UI 목업
20판정 상세행/항목 클릭 시 [[HBHR281_판정 상세·수동 판정 모달]]미배포 UI
30요율·표 상태귀속월 유효 요율/표와 출처를 표시. 부재 시 N/A/미계산과 원인코드UI TBU
40계산 진단경고 집계에서 [[HBHR282_계산 진단 패널 드로어]]로 진입제안 UI
50대사[[HBHR290_병행운영 대사 리포트]]로 진입제안 UI
606종 커밋n×6 record를 단일 트랜잭션으로 확정. 판정불가·요율/표/기초 부재·stale·lock이면 차단계산/API/커밋 PoC 근거 / UI 미배포
70성공생성/갱신 record 수, 귀속월, 커밋 version 표시문구 TBU

공통 상태 ​

상태표시·행동
loading기존 내용 유지 가능 시 유지하고 로딩 표시; mutation 비활성
empty대상 재직자 0명 안내; 커밋 비활성
dirty편집 후 미저장/미커밋 표시
saving중복 입력 방지, 동일 idempotencyKey 재사용
success6종 커밋 건수·귀속월·version 표시
error원인코드와 재시도/해결 경로; 조용한 0 금지
forbiddencapability 또는 organization scope 불충족, 데이터 미노출
conflictexpectedVersion 불일치, 최신값 재조회 후 사용자 재확인
stale상류 수당/판정/요율 변경 표시, 재계산·재커밋 유도
locked급여 마감·외부 잠금으로 편집/커밋 차단, 잠금 주체·시각 표시

권한 요청 / 분기 요약 ​

시나리오결과
조회 capability 없음/다른 조직forbidden, 데이터 미노출
판정 입력 부재계산 유지 + 항목별 경고, 커밋 차단
6종 입력·요율·표 정상한 커밋에서 n×6 DeductionRecord
version 충돌저장하지 않고 conflict
stale/locked원인 해소 전 커밋 차단

미결 제안 — 현재 구현 아님 ​

  • 매월 자동 생성: 월 변경/월초에 법정공제 시트를 자동 provision할지, 사용자 명시 적용을 유지할지 미결. 트리거·중복방지·실패 복구·권한·감사 계약 TBU.
  • 미반영 월마감 경고: 마감 시 법정공제 시트 미생성/미반영/stale을 경고할지 미결. 마감의 정본, 차단/경고 수준, 해결 CTA, 예외권한 TBU.

Open Question ​

  • 0원 제외 항목도 DeductionRecord를 생성해 판정과 함께 보존할지, 0원 레코드를 생략하고 별도 판정 정본을 둘지.
  • 급여대장 기초 부재의 계산 차단 시점(즉시 계산 실패 vs preview 경고 후 커밋 차단)과 확정 문구.
  • 수동 금액 보정의 사유 강제 여부와 자동 계산값/채택값/audit 저장 계약([[POL_공제_계산·확정]] 미결).
  • 실제 SDK alias(sheetTemplate.apply)와 router key(applyToPeriod)의 공개 API 명칭 정합, RBAC 토큰·lock/version 응답 schema·UI 원문.
  • [payroll_ledger:insurable_gross]의 라이브 slug와 6종 판정 사유코드 전체 enum. 이 키와 세법상 과세소득/보수월액의 법적 관계는 정책·법률 근거 확인 전 TBU.
  • ⚠ 베타 배포로 전제 붕괴(9/29 실측, #907/#912) — 본 문서의 "dev 미배포·PoC" 전제가 무효. 구현 vs 문서 차이:
    • 템플릿명 "법정공제 — 4대보험 (월별)" [베타] vs 문서 제목 "4대보험·소득세 월별" — 명칭 정합 필요
    • 템플릿 설명문은 "4대보험만 커밋"처럼 읽히나 실제는 소득세·지방소득세 2열 포함 6종 모두 commitToKind=deduction 커밋 — 사용자 오해 소지, 설명문 수정 여부
    • 구현 템플릿 35열(판정 수동 오버라이드 포함), 간이세액표 2024-02-29 기준 7,128행, statutory.* 파라미터 — 문서 컬럼 정의와 대사 필요
    • opt-in 방식: #912 본문 "켠 조직에서만 보임" vs 실측 "전 조직에 템플릿 노출, 시트를 만들어야 동작"(커밋 본문 기준) — 표현 불일치, 실제 게이트 확인 필요
    • → 본문 전면 개정은 시트 생성 실사(쓰기 금지 해제 필요) 포함 정규 배치에서 진행