Skip to content

일괄 업로드 다이얼로그 — FE 메인테이너 참고

펀치리스트 Critical C-6 (최소 정직 배선). 정본 plan: docs/superpowers/plans/2026-07-10-upload-dialog-honest-wire.md.


1. 배경

전 앱 ~75개 SheetCreateUpload*Art.vue는 파일 입력 + "서식 다운로드" 폼은 있었으나 footer(취소/업로드하기)가 통째 주석 처리 + <script setup> 빔인 복붙 패밀리였다 — 힌트 텍스트는 "[업로드하기] 클릭"인데 그 버튼이 없어 제출 불가능한 dead-end. 오너 결정 "최소 정직 배선": 실 CSV/xlsx 파싱·행 생성은 구현하지 않되(follow-up), 파일 선택 검증 + 제출 이벤트 발행 + 닫기까지는 정직하게 동작하도록 배선한다.


2. SheetCreateUploadArt (공유 컴포넌트)

src/components/_shared/SheetCreateUploadArt.vue

Props

Prop타입기본값설명
idString (required)dialog id. 부모의 command="show-modal" commandfor 트리거 및 내부 close 버튼의 :commandfor="id"가 이 값을 공유한다.
titleString''sheet-header 제목
templateLabelString'서식 다운로드'서식 다운로드 버튼 라벨
descriptionString'파일을 선택하고 [업로드하기] 버튼을 클릭하세요.'파일 입력 아래 안내 텍스트

Emit

이벤트페이로드시점
uploadFile (선택된 원본 파일 객체, 파싱 없음)[업로드하기] 클릭 시(파일 선택된 경우만 — 버튼이 disabled라 미선택 시 클릭 자체 불가)

내부 상태 / 동작

  • selectedFile ref — <input type="file" @change="onFileChange">로 갱신.
  • canUpload computed — !!selectedFile. 업로드하기 버튼의 :disabled="!canUpload" 게이트.
  • submit(): canUpload 확인 → emit('upload', selectedFile)closeDialog()(dialog .close(), jsdom 미구현 typeof 가드) → selectedFile/file input 리셋.
  • cancel(): selectedFile 리셋 + file input 리셋 (close는 command="close" 네이티브 버튼 동작에 위임).
  • defineExpose({ selectedFile, canUpload, onFileChange, cancel, submit }) — 테스트 훅.

DS 사용

기존 마크업(header/close/file input/서식 다운로드 버튼) 보존, 신규 DS 클래스 0. sheet-footer는 button-neutral-subtle(취소) + button-primary-bold(업로드하기, disabled 게이트) — CLAUDE.md 버튼 스타일 매트릭스 준수.


3. 얇은 wrapper 패턴 (Task 2, 5곳 적용)

각 도메인 wrapper는 기존 id·title을 그대로 prop으로 전달하며 공유 컴포넌트를 렌더하는 얇은 래퍼로 교체됐다:

vue
<script setup>
import SheetCreateUploadArt from "@/components/_shared/SheetCreateUploadArt.vue";
</script>

<template>
  <SheetCreateUploadArt id="기존-id-그대로" title="정정된 제목" />
</template>
Wrapper 파일id (보존)트리거 상태
tax-invoice/tax-invoice/unit/general/overlays/SheetCreateUploadUnitArt.vuesheet-create-upload-unit-art✅ 살아있음 — tax-invoice/issue/single/general/blocks/DataToolBarOrg.vuecommand="show-modal" commandfor="sheet-create-upload-unit-art"로 연다
administration/billing/workspace/property/overlays/SheetCreateUploadPropertyArt.vue (루트)sheet-create-upload-property-art✅ 살아있음 — administration/billing/workspace/property/blocks/DataToolBarOrg.vue가 연다
administration/billing/workspace/property/console/general/overlays/SheetCreateUploadPropertyArt.vuesheet-create-upload-property-art❌ orphan — 이 콘솔의 DataToolBarOrg에 여는 트리거 없음
administration/billing/workspace/property/console/account/overlays/SheetCreateUploadSiteArt.vuesheet-create-upload-site-art❌ orphan — 동일
administration/billing/workspace/property/console/plan/overlays/SheetCreateUploadBuildingArt.vuesheet-create-upload-building-art❌ orphan — 이 모듈(billing/…/console/plan)의 DataToolBarOrg.vuesheet-create-building-art(생성 시트)만 열며, 업로드 시트를 여는 트리거는 없다

정정된 제목: 5개 wrapper 모두 원본이 복붙 버그로 "유닛 업로드"였던 것을, 각 엔티티에 맞게 정정했다(유닛=그대로 정확·프로퍼티/현장/동=정정). 자세한 근거는 각 wrapper 파일 상단 주석 참고.

⚠️ id 중복 주의: 이 앱 전역에서 dialog id 문자열이 여러 파일에 재사용되는 사례가 이미 존재한다(예: sheet-create-upload-building-artadministration/organization/workspace/property/console/building/overlays/SheetCreateUploadBuildingArt.vue에도 존재하며 그쪽은 자체 DataToolBarOrg.vue에서 살아있는 트리거를 갖는다 — 이번에 건드린 billing 모듈의 동명 id와는 별개 파일·별개 라우트). 이번 슬라이스는 이 pre-existing 중복을 만들지도 해소하지도 않았다 — id는 각 wrapper 내에서만 보존.


4. Orphan 다이얼로그 (3곳) — follow-up 필요

console/general(프로퍼티)·console/account(현장)·console/plan(동) 3개 wrapper는 공유 컴포넌트로 정직하게 배선됐지만, 여는 버튼이 없어 현재 화면에서 도달 불가능하다. 이는 이번 스왑이 만든 문제가 아니라 스왑 이전부터 있던 gap이다(원본도 각 모듈 DataToolBarOrg에 트리거가 없었다).

follow-up: 각 콘솔의 blocks/DataToolBarOrg.vue<button command="show-modal" commandfor="{해당 id}">업로드</button>를 추가하면 즉시 도달 가능해진다(공유 컴포넌트·wrapper 쪽 변경 불필요).


5. ~75개 패밀리 — 기계적 스왑 follow-up

SheetCreateUpload*Art.vue 명명 패턴을 가진 파일이 앱 전역에 ~75개 존재하며(이번에 적용한 5개 제외), 전부 동일한 dead-end 패턴(footer 주석·script 빔)을 공유한다. 도메인: HR(human-capital/*이번 작업 미접촉, 건드리지 말 것)·facility·crm·contract·master-data·billing/hospitality 등.

예시 목록(전량 아님, 대표):

  • human-capital/compensation/payroll/general/overlays/SheetCreateUploadEmployeeArt.vue 외 human-capital 4개
  • facility/asset/*, facility/maintenance/*, facility/status/*, facility/task/* 각 하위 다수
  • contract/integration/*, contract/resource/* 각 하위 다수
  • crm/resource/note/* 3개
  • master-data/resource/* 다수(counterparty·associate·business·cash-receipt·direct-debit·member·unit·vehicle·virtual-bank-account 등)
  • accounting/accounting-mgmt/financial-accounting/basic-setting/business-partner/general/overlays/SheetCreateUploadBusinessPartnerArt.vue
  • billing/hospitality/master/room/general/overlays/SheetCreateUploadRoomArt.vue

follow-up 작업 방식: 각 파일을 이번 5개와 동일한 얇은 wrapper 패턴(<SheetCreateUploadArt :id=".." :title=".." />)으로 기계적 치환 — 로직 변경 없음, id/title만 원본에서 이관. 각 도메인이 판매 스코프에 들어올 때 그 도메인 담당 슬라이스에서 처리 권장(한 번에 75개를 몰아서 하지 않는다 — 회귀 범위 관리).


6. 테스트 위치

파일대상
src/components/_shared/__tests__/SheetCreateUploadArt.spec.js공유 컴포넌트 — 파일 미선택 disabled·선택 후 활성·upload emit+close·cancel 리셋·id prop 렌더
src/components/tax-invoice/tax-invoice/unit/general/overlays/__tests__/SheetCreateUploadUnitArt.spec.jswrapper — id/commandfor 보존
src/components/administration/billing/workspace/property/overlays/__tests__/SheetCreateUploadPropertyArt.spec.jswrapper(루트) — id/commandfor 보존
src/components/administration/billing/workspace/property/console/general/overlays/__tests__/SheetCreateUploadPropertyArt.spec.jswrapper(콘솔) — id 보존(orphan)
src/components/administration/billing/workspace/property/console/account/overlays/__tests__/SheetCreateUploadSiteArt.spec.jswrapper(콘솔) — id 보존(orphan)
src/components/administration/billing/workspace/property/console/plan/overlays/__tests__/SheetCreateUploadBuildingArt.spec.jswrapper(콘솔) — id 보존(orphan)

7. 확장 포인트

  • 실 파싱 연결: @upload="handleFile" 리스너를 각 wrapper(또는 부모 MainOrg)에 추가하고 BE 핸드오프(§3 계약 스케치) 구현.
  • orphan 트리거 추가: §4 참고, 컴포넌트 변경 없이 DataToolBarOrg 버튼만 추가.
  • 나머지 ~70개 스왑: §5 참고, 도메인별 follow-up 슬라이스로 분할 권장.