다크모드
FE 핸드오프 — 연체료 발생 중지 설정 (2026-06-25)
독자: FE 메인테이너/AI. 연체료 발생 중지(accrualPause) 기능의 UI 배선·컴포넌트 구조·데이터 흐름·테스트 위치·확장 포인트를 정의한다. as-built: 브랜치
worktree-late-fee-pause(2026-06-25). BE 계약:docs/handoff/backend/late-fee-accrual-pause.md
1. 진입 경로 (라우트)
관리비 앱 → 관리비설정 → 연체료 할증 (/service-charge/setting/surcharge).
- 라우트 세그먼트:
lineLevel['setting/surcharge']→IndexView.vue→MainOrg.vue+overlays/SheetUpdateSurchargePause.vue. - 연체료 할증 페이지는 연체료 기본(인식기준)·연체요율·발생 중지 세 카드를 모두 포함한다.
2. 컴포넌트 구조
service-charge/setting/surcharge/
MainOrg.vue — 읽기 바인딩 (발생 중지 카드 표시)
overlays/
SheetUpdateSurchargePause.vue — 편집 시트 (read↔edit 토글 + 저장)2-1. MainOrg.vue — 읽기 카드
파일: src/components/service-charge/setting/surcharge/MainOrg.vue
js
const { policy } = useLateFeePolicy();
const accrualPause = computed(() => policy.value.accrualPause);accrualPause.enabled→ 카드 요약 배지("사용" / "미사용")accrualPause.startDay,accrualPause.endDay→ 기간 표시 행
편집 버튼: command="show-modal" commandfor="sheet-update-surcharge-pause" → SheetUpdateSurchargePause 오픈.
2-2. SheetUpdateSurchargePause.vue — 편집 시트
파일: src/components/service-charge/setting/surcharge/overlays/SheetUpdateSurchargePause.vue
sheet id: sheet-update-surcharge-pause sheet 폭: sheet-width-lg
Script setup 핵심
js
const { policy, setAccrualPause } = useLateFeePolicy();
const isEditMode = ref(false);
const draft = ref({ isUsed: "inactive", startDay: 10, endDay: 20 });
const seedDraft = () => {
const p = policy.value.accrualPause;
draft.value = {
isUsed: p.enabled ? "active" : "inactive",
startDay: p.startDay,
endDay: p.endDay,
};
};
seedDraft(); // 마운트 시 정책 → draft 동기화
const startEdit = () => {
seedDraft();
isEditMode.value = true;
};
const cancelEdit = () => {
seedDraft();
isEditMode.value = false;
};
const confirmEdit = () => {
setAccrualPause({
enabled: draft.value.isUsed === "active",
startDay: draft.value.startDay,
endDay: draft.value.endDay,
});
isEditMode.value = false;
};draft ↔ 정책 매핑
| draft 필드 | 타입 | 정책 필드 | 변환 |
|---|---|---|---|
isUsed | 'active' / 'inactive' | enabled (boolean) | 'active' ↔ true |
startDay | number (1–31) | startDay | 그대로 |
endDay | number (1–31) | endDay | 그대로 |
폼 필드
- 발생 중지 기능 —
<Select v-model="draft.isUsed">(inactive/active),:disabled="!isEditMode" - 발생 중지 기간 —
startDaySelect (1–31 일) +endDaySelect (1–31 일), 두 드롭다운,:disabled="!isEditMode"
푸터 모드 토글
읽기 모드: [편집] → startEdit()
편집 모드: [취소] → cancelEdit() | [확인] → confirmEdit()버튼 클래스: 편집 button-neutral-subtle button-md / 취소 button-neutral-subtle button-md / 확인 button-primary-bold button-md.
3. 데이터 흐름
lateFeePolicy.js
policy (ref, singleton)
└── .accrualPause { enabled, startDay, endDay }
│
├── [읽기] MainOrg.accrualPause (computed) → 카드 표시
│
└── [편집] SheetUpdateSurchargePause
seedDraft(): policy → draft (isUsed, startDay, endDay)
confirmEdit(): draft → setAccrualPause() → policy 교체
setAccrualPause(partial)
→ { ...policy.value.accrualPause, ...partial } 검증(1≤start≤end≤31)
→ 유효 시 policy.value = { ...policy.value, accrualPause: next }
policy 변경 후 즉시:
useLateFee.lateFeesFor()
effectiveAsOf(asOf, policy.value.accrualPause)
computeLateFees(installments, evalDate, ...)
→ 연체료 현황·수납 상세·분개 등 모든 소비처가 reactive로 재계산취소(cancelEdit) 시: seedDraft()로 draft를 정책 현재값으로 되돌림 → 정책 불변.
4. 소비 컴포저블 요약
| 컴포저블 | 용도 |
|---|---|
useLateFeePolicy() | policy ref (읽기), setAccrualPause(partial) (쓰기) |
effectiveAsOf(asOf, pause) — interestEngine.js 직접 import | 순수 함수, useLateFee 내부에서 호출 |
useLateFee().lateFeesFor(axis, key, asOf) | 발생 중지 적용된 연체료 산출 (소비처: 수납 상세·현황 등) |
5. 테스트 위치
| 파일 | 커버 내용 |
|---|---|
src/composables/__tests__/interestEngine.spec.js | effectiveAsOf — 창 내 동결 7케이스 (startDay=1 전월말일, 창 이전/이후 통과, disabled 통과 등) |
src/composables/__tests__/lateFeePolicy.spec.js | accrualPause 정책 — setAccrualPause 부분갱신·검증 6케이스 (무효 no-op, 경계값 등) |
src/composables/__tests__/useLateFee.spec.js | 통합 — 발생 중지 적용 시 lateFeesFor 연체료 동결 2케이스 |
src/components/service-charge/setting/surcharge/__tests__/SurchargePause.spec.js | UI 읽기 바인딩 2케이스 (enabled 상태 표시·startDay/endDay 렌더링) |
6. 확장 포인트
6-1. 3계층 스코프 (❌ 예정)
현재 accrualPause는 lateFeePolicy.js 단일 싱글톤(로컬 스코프). 충당순서·연체요율처럼 프리셋/글로벌/로컬 3계층으로 분리하려면:
DEFAULT_LATE_FEE_POLICY.accrualPause를 프리셋(frozen).globalAccrualPauseref 추가.setAccrualPause(partial, scope)— scope 분기.MainOrg에 스코프 토글 + provenance 배지(충당순서§1-B-3참조).- 엔진은 항상 로컬
policy.accrualPause를 읽음(충당순서와 동일 패턴).
6-2. 일회성 날짜 구간 (❌ 예정)
현재 "매월 N일M일" 반복 창만 지원. 특정 날짜 구간 한 번(예: 2025-12-102025-12-20)만 중지하려면:
accrualPause구조에type: 'monthly' | 'once'필드 추가.effectiveAsOf에once분기:asOf자체가 구간 내면 동결.- UI: 시트에 구간 타입 선택 + 날짜 피커 추가.
6-3. 엔진 무영향 원칙 유지
lateFeeOf, computeLateFees, overdueDays, rateSegments 시그니처는 변경하지 않는다. 중지 로직은 항상 effectiveAsOf에서 asOf를 필터링하는 방식을 유지한다.