Skip to content

관리비 콘솔 "공지" — 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.vuebillings 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?) 형태로 신설하면 기존 관례와 정합적일 것.
  • 목록 실배선billings mock 삭제 지점(NoticeDynamicTableOrg.vue/NoticeIndividualDynamicTableOrg.vueref([]))이 정확히 실데이터가 들어갈 자리다. 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 로케일)만으로 구동되는 순수 렌더 검증 — 스토어/네트워크 목킹 없음(공지 데이터 소스가 없으므로 자연히 없음).