다크모드
관리비 콘솔 "공지" — FE 메인테이너 참고
관리비 상용화 펀치리스트 Critical C-3 해소(최소 정직 게이팅 — 신규 기능 구현 아님). 설계 정본:
docs/superpowers/plans/2026-07-10-notice-honest-gate.md.
1. 배경 — 무엇이 문제였나
service-charge/actual/console/notice/{general,individual}는 이름 때문에 "고지서 목록 콘솔"로 오인되기 쉽지만, 실제로는 공지(announcement) 문구 게시판이다(BE 문서 §1 참고). 정찰 결과:
- 목록(
Notice[Individual]DynamicTableOrg.vue)은ref([...3개 mock행])하드코딩(공지문구 예시 "입주를 환영합니다" 등). - 상단 툴바(
NoticeDataToolBarOrg.vue)의 삭제·다운로드·지난공지·추가와, 기본정보 영역(DataToolBarOrg.vue)의 편집은 전부 dead 또는 borrowed 배선이었다:- 삭제 → confirm 다이얼로그만 열고 mutation 없음.
- 다운로드 → 핸들러 자체 없음.
- 지난공지 → 다른 도메인(tax-invoice)의 업로드 시트를 빌려 열었음(개념적으로 안 맞음).
- 추가 → 다른 도메인(
billing/_core/console)의SheetCreateArt를 빌려 열었음. - 편집(행별 + 상단) →
<script setup>이 아예 없는FormSheetArt.vue를 열었음(입력·저장 전부 no-op).
- 공지 데이터를 저장할 스토어/API가 시스템 어디에도 없다 —
useInvoice/useAllocations는 재무 데이터라 공지 문구의 소스가 될 수 없다. 그래서 "실데이터로 배선"이 아니라 "죽은 표면을 정직하게 비활성화"가 이번 슬라이스의 스코프였다.
2. 이번 슬라이스(C-3) as-built
2-1. 변경 파일
| 파일 | 변경 |
|---|---|
notice/general/blocks/NoticeDataToolBarOrg.vue | 삭제·다운로드·지난공지·추가 → disabled + title="공지 작성 기능 준비 중", 빌린/죽은 commandfor 제거. 미리보기 버튼은 무변. |
notice/individual/blocks/NoticeDataToolBarOrg.vue | 동형(개별 공지 i18n 키만 다름). |
notice/general/blocks/DataToolBarOrg.vue | 기본정보 영역 [편집] → disabled + 동일 title. |
notice/individual/blocks/DataToolBarOrg.vue | 동형. |
notice/general/blocks/NoticeDynamicTableOrg.vue | billings mock 3행 → 빈 배열(ref([])). v-if="!billings.length" 빈 상태 행("등록된 공지가 없습니다 · 공지 작성 기능 준비 중") 추가. 행별 [편집] 버튼은 disabled + title로 남기고 commandfor="form-sheet-art" 제거. |
notice/individual/blocks/NoticeIndividualDynamicTableOrg.vue | 동형(세대 열 유지). |
MainOrg.vue(general/individual 둘 다), HeaderOrg.vue, TabBarOrg.vue, overlays/FormSheetArt.vue는 손대지 않았다(파일은 여전히 존재하지만, 더는 어떤 버튼도 그것을 열지 않는다 — dead code로 남아있음, §4 참고).
2-2. disabled 버튼 규약
모든 죽은 컨트롤은 다음 두 속성으로 일관되게 표시한다(신규 CSS 클래스 없음, 기존 button 유틸 + 네이티브 disabled/title만 사용):
html
<button
disabled
title="공지 작성 기능 준비 중"
class="button button-neutral-subtle button-sm"
></button>title이 hover 시 안내 문구로 동작한다(네이티브 브라우저 툴팁, 커스텀 컴포넌트 없음).
2-3. 빈 상태 패턴
기존 DS 전용 empty-state 컴포넌트는 없어, 레포 관례대로 <tbody> 안 v-if="!list.length" 단일 행 + colspan + text-center text-dimmed로 처리했다(다른 도메인 dynamic table의 빈 상태와 동형).
html
<tr v-if="!billings.length">
<td colspan="6" class="text-center text-dimmed">
등록된 공지가 없습니다 · 공지 작성 기능 준비 중
</td>
</tr>colspan은 general=6(체크박스+번호+내용+수정일+작성자+관리), individual=7(세대 열 추가).
3. 유일하게 동작하는 컨트롤 — 미리보기
두 NoticeDataToolBarOrg.vue 모두 미리보기 버튼은 이번 작업에서 변경하지 않았다:
html
<button
command="show-modal"
commandfor="invoice-preview-art"
class="button button-neutral-subtle button-sm"
>
<span class="material-symbols-outlined"> preview </span>{{ $t('...button.preview') }}
</button>MainOrg.vue에 이미 배선되어 있던 <dialog id="invoice-preview-art"> 안의 <InvoiceDynamicOrg/>(canon 고지서 뷰, src/components/document/transaction/InvoiceDynamicOrg.vue)를 그대로 연다. 이 콘솔이 직접 만든 화면이 아니라 이미 다른 슬라이스에서 완성된 canon 컴포넌트를 재사용하는 것 — 새로 배선한 것이 없으므로 회귀 리스크가 없다.
4. 컴포넌트 트리 (general 기준, individual 동형)
views/service-charge/actual/console/notice/general/IndexView.vue
└─ components/.../notice/general/MainOrg.vue (무변)
├─ HeaderOrg.vue (무변 — 스테이지/탭바 상위)
├─ blocks/TabBarOrg.vue (무변 — 전체/개별 라우트 전환)
├─ blocks/DataToolBarOrg.vue ← 편집 disabled
├─ blocks/NoticeDataToolBarOrg.vue ← 삭제/다운로드/지난공지/추가 disabled, 미리보기 무변
├─ blocks/NoticeDynamicTableOrg.vue ← mock→빈 상태
├─ <dialog id="invoice-preview-art"><InvoiceDynamicOrg/></dialog> (무변)
├─ overlays/FormSheetArt.vue (더 이상 열리지 않음 — dead, §5)
├─ DialogDeleteArt.vue (더 이상 열리지 않음 — dead)
├─ SheetCreateUploadUnitArt.vue(빌린 것) (더 이상 열리지 않음 — dead)
└─ SheetCreateArt.vue(빌린 것) (더 이상 열리지 않음 — dead)5. 남은 dead 표면(follow-up 시 정리 대상)
MainOrg.vue는 여전히 아래 오버레이들을 import/렌더하지만, 어떤 버튼도 이들을 열지 않는다(commandfor 전부 제거됨). 실 공지 authoring을 구현할 때:
overlays/FormSheetArt.vue— 현재<script setup>이 없는 정적 폼(고지서명/년/월/시작일/종료일/납기일 필드 — 공지 문구 입력란 자체가 없다, 애초에 고지서 기본정보 폼을 복붙한 것으로 보인다). 실 공지 작성 폼으로 재작성하거나 새 시트로 교체 필요.DialogDeleteArt.vue/SheetCreateUploadUnitArt.vue(tax-invoice에서 빌림) /SheetCreateArt.vue(billing/_core에서 빌림) — 전부 다른 도메인 컴포넌트를 그대로 가져다 쓴 것. 실 구현 시 공지 전용 컴포넌트로 교체하거나, 정말 재사용할 것이면 도메인 결합을 명시적으로 정리해야 한다.
이번 슬라이스는 이 오버레이들을 삭제하지 않았다(dead import 상태로 유지) — 회귀 표면을 최소화하기 위해 "여는 버튼 제거"까지만 하고 파일 자체는 건드리지 않는 최소 변경을 택했다.
6. 확장 포인트 (follow-up)
- 공지 데이터모델/스토어 —
useBillingPeriod/useCharges류의 module-level singleton 패턴(line+edition 키잉·clone-replace·localStorage)을 참고해useNotice(scope, unitId?)형태로 신설하면 기존 관례와 정합적일 것. - 목록 실배선 —
billingsmock 삭제 지점(NoticeDynamicTableOrg.vue/NoticeIndividualDynamicTableOrg.vue의ref([]))이 정확히 실데이터가 들어갈 자리다.computed(() => list(scope, ...))로 교체. - authoring 시트 —
overlays/FormSheetArt.vue를 공지 문구 입력 폼(content/기간/scope)으로 재작성 후,NoticeDataToolBarOrg.vue의 [추가] 버튼과 목록 행의 [편집] 버튼에commandfor를 다시 연결. - NoticeAto.vue 연동 —
src/components/document/transaction/blocks/NoticeAto.vue가 정적 "라인1~5"를 렌더 중. 공지 데이터가 생기면 이 블록이 실제 공지 문구를 구독하도록 배선(BE 문서 §4-3 계약 참고). 이 콘솔의 미리보기가 여는InvoiceDynamicOrg도 같은 블록을 쓰므로, 연동되면 미리보기가 "진짜" 미리보기가 된다. - 지난공지/다운로드 — 목적지 API(BE 문서 §4-2
history/export)가 정의되면 각각의 disabled 버튼에서disabled/title제거하고 핸들러 연결.
7. 테스트 위치
| 파일 | 대상 |
|---|---|
notice/general/blocks/__tests__/NoticeDataToolBarOrg.spec.js | 미리보기 존재·활성·invoice-preview-art 배선 무변 / 삭제·다운로드·지난공지·추가 disabled+title+commandfor 제거 |
notice/individual/blocks/__tests__/NoticeDataToolBarOrg.spec.js | 동형(개별 공지) |
notice/general/blocks/__tests__/DataToolBarOrg.spec.js | 기본정보 [편집] disabled+title |
notice/individual/blocks/__tests__/DataToolBarOrg.spec.js | 동형 |
notice/general/blocks/__tests__/NoticeDynamicTableOrg.spec.js | 가짜 mock 문구("입주를 환영합니다") 미노출 / 빈 상태 문구 렌더 / form-sheet-art 향 행 편집 버튼 없음 |
notice/individual/blocks/__tests__/NoticeIndividualDynamicTableOrg.spec.js | 동형(세대 열 포함) |
모든 테스트는 @vue/test-utils mount + createI18n(ko 로케일)만으로 구동되는 순수 렌더 검증 — 스토어/네트워크 목킹 없음(공지 데이터 소스가 없으므로 자연히 없음).