Skip to content

FE 메인테이너 — 멤버 상세 시트 + 마스터데이터 목록 IA

계약 연결 탭 시각 계약

  • 계약 연결 시트의 탭 목록은 SSOT 클래스 tabs-list tabs-list-primary-moderate tabs-list-radius-full을 사용한다.
  • 각 실제 목적지만 tabs-trigger tabs-trigger-md로 렌더링한다.
  • 탭 목록 안에 arrow_menu_close 같은 아이콘 전용 가짜 탭을 삽입하지 않는다. 접기·이전 이동이 필요하면 탭과 분리된 공식 컨트롤을 사용한다.
  • 탭 CSS는 leysys-design 카피본을 그대로 유지하며 제품 레포에서 크기·패딩·radius를 덮어쓰지 않는다.

독자: FE 메인테이너/AI. "이 페이지를 이어 개발하려면 무엇을 알아야 하나". 대상: 멤버(general) 목록 + 통합 상세 시트(R/C/U) + 연결 컬렉션 오케스트레이션. 정본 패턴: CLAUDE.md §0(Sheet 편집 패턴, 오너 이원화) · §1(코드 체계) · 리스트 IA.


1. 라우트

pathview상태
/master-data/resource/member/generalsrc/views/master-data/resource/member/general/IndexView.vue✅구현
/master-data/resource/member/group.../group/IndexView.vue(별도)
/master-data/resource/member/label.../label/IndexView.vue(별도)

정의: src/router/index.js (general = L921 부근).


2. 컴포넌트 트리

IndexView.vue (general)
├── HeaderOrg.vue
└── MainOrg.vue
    ├── SearchToolBarOrg.vue        (검색)
    ├── CriteriaToolBarOrg.vue      (필터)
    ├── DataToolBarOrg.vue          (생성/삭제/업로드/다운로드 — 생성 진입점)
    ├── DynamicTableOrg.vue         (목록: 이름 클릭 = 상세 진입)
    ├── PaginationToolBarOrg.vue

    └── (overlays — MainOrg가 형제로 마운트)
        ├── SheetReadMemberArt.vue          ★ 통합 R/C/U 상세 시트 (read|edit|create)
        ├── SheetCreateUploadMemberArt.vue  (CSV 등 일괄 업로드)

        └── 연결 컬렉션 시트(자체 R/C/U) — SheetReadMemberArt 내 탭에서 호출:
            ├── 차량      Sheet{Read,Create,Update}VehicleArt
            ├── 일원      Sheet{Read,Create,Update}AssociateArt
            ├── 사업자    Sheet{Read,Create,Update}BusinessArt
            ├── 현금영수증 Sheet{Read,Create,Update}CashReceiptArt
            ├── 가상계좌  Sheet{Read,Create,Update}VirtualBankAccountArt
            ├── 자동이체  Sheet{Read,Create,Update}DirectDebitArt
            └── 노트      Sheet{Read,Create,Update}NoteArt
        └── 공통 다이얼로그: Dialog{Create,Read,Update,Delete,Confirm,Cancel,List,Attach}Art
  • 마운트 전략: MainOrg.vue<dialog popover>형 시트/다이얼로그를 전부 형제로 한 번씩 마운트해두고, 목록·툴바·시트 내부 버튼이 command="show-modal" commandfor="..."(네이티브 popover/dialog command)로 연다. Vue v-if 토글이 아니라 DOM id 기반 오케스트레이션이다.
  • 연결 컬렉션 시트들은 다른 도메인 디렉터리(master-data/resource/{vehicle,associate,business,...})에서 import해 재사용한다. 멤버 전용이 아님.

3. 소비 컴포저블 — useMemberForm.js

src/composables/useMemberForm.js. 모듈 스코프 reactive 싱글톤(프로토타입 — useReceipts·useForwarding 선례). 여러 컴포넌트(DynamicTableOrg, DataToolBarOrg, SheetReadMemberArt)가 같은 인스턴스를 공유한다.

노출 상태/메서드

이름종류역할
recordreactive표시 원본(조회). 저장 시 여기에 커밋
draftreactive편집·생성 작업본
moderef 'read'|'edit'|'create'시트 통합 모드
validcomputed필수 충족: type && regNum && name && representative
dirtycomputeddraft ≠ 편집 진입 스냅샷(baseline)
openRead(rec?)fnrec 있으면 record 주입 + mode='read' (목록에서 호출)
startEdit()fndraft←record + 스냅샷 + mode='edit'
startCreate()fn빈 폼 + 기본값(개인/남자) + 관리코드 자동부여 + mode='create'
cancel()fnmode='read' (작업분 버림)
save()fnrecord←draft + mode='read'
syncDraft()fn카드별 편집용 — mode 불변, draft←record + 스냅샷
commitDraft()fn카드별 편집용 — record←draft (mode 불변)
autoRegNum()fn[자동입력] 등록번호 생성(아래 §5)

⚠ 프로토타입 한계: 단일 데모 레코드(DEMO()). 목록의 openRead()는 인자 없이 호출되어 항상 같은 데모 레코드를 연다(행별 주입 미구현 ❌예정). 실제 구현 시 디렉터리/스토어에서 선택 행을 openRead(rec)로 주입.


4. 편집 모델 — 이원화 (사용자 결정, CLAUDE.md §0)

SheetReadMemberArt.vue 속성 영역은 두 편집 경로를 병존시킨다(중복이 아니라 의도된 이원화).

  • 기본·컨택·주소 각 카드가 자체 [편집]→[취소/저장]로 그 카드만 인라인 폼 전환.
  • 상태: reactive cardEdit { basic, contact, address }.
  • editCard(key)syncDraft() + cardEdit[key]=true.
  • cancelCard(key)cardEdit[key]=false(버림). saveCard(key)commitDraft() + false.
  • 카드 폼 표시 여부 = {section}Editing = editing(통합) || cardEdit.{section}.
  • [편집]startEdit()mode='edit': 속성 전체가 한 번에 폼 전환.
  • editing = computed(mode !== 'read').
  • [취소]=cancel(), [저장/생성]=save():disabled="!valid || !dirty"(통째 편집 게이트 §0-7).

4-3. 이원 충돌 방지

  • 통합 편집 중(mode!=='read')엔 카드 footer를 숨긴다(v-if="mode === 'read'"). 커밋 경로를 sheet-footer 하나로 모아 이중 저장 방지.
  • create 모드: 카드 footer 없음(전부 통합), 연결 컬렉션 탭은 v-show="mode !== 'create'"로 숨김(아직 멤버 미존재).

5. 필드 잠금 / 자동화 규칙 (CLAUDE.md §1 코드 체계)

필드createedit규칙
관리코드 mgmtCode편집 가능 필드(자동부여 디폴트 mgmt-####, 직접 수정 가능)비노출 — zone-top에 subtitle-sm로만 표시최초 등록만 결정. 이후 불변
등록번호 regNum입력 가능 + [자동입력] 버튼(autoRegNum):disabled(잠금)준식별 코드. 최초 등록만 변경
생년월일 birthDate입력 가능:disabled(잠금)불변
이름 name입력 가능입력 가능중복 허용. 개인이면 대표자=이름 watch 동기화
대표자 representative법인만 노출 입력법인만 노출개인은 숨김(watch가 name으로 자동 세팅)
타입 typeSelect(개인/법인)Select필수

등록번호 자동입력 포맷(autoRegNum)

  • 개인: 생년월일 8자리 있으면 생년월일6 + 영숫자7, 6자리면 앞6 + 영숫자7, 없으면 영숫자6 + 영숫자7.
  • 법인: 영숫자6 + 영숫자7.
  • 영숫자(alnum)는 첫 글자 소문자 영문 강제(숫자만 패턴 회피).

대표자 동기화 watch

watch([draft.type, draft.name]) → if 개인: draft.representative = draft.name. 개인 멤버는 대표자 입력 필드를 노출하지 않는 근거.


6. 리스트 IA — DynamicTableOrg.vue

  • 제목(이름) 클릭 = 상세 시트 진입. 전용 "상세" 버튼 폐기(CLAUDE.md 리스트 IA). 이름 셀이 <button @click="openRead()" command="show-modal" commandfor="sheet-read-member-art">.
  • 클릭 affordance: hover:underline + cursor-pointer + table-hover(행 호버 하이라이트) + sticky-thead + 이름 컬럼 sticky-col.
  • 컬럼 노출(§1 UI 노출 순서): 타입(badge) → 이름 → 연락처(dropdown) → 차량/일원/사업자/현금영수증/가상계좌/자동이체/노트(요약 badge). 등록번호·대표자·생년월일·성별 컬럼은 주석으로 보존(노출 안 함). 관리코드는 목록 비노출(정책 준수).
  • 데이터: 로컬 members ref(데모 3건). i18n 라벨은 KO 값 → legalTypeLabel() 등으로 로케일 파생(값은 KO 유지).

7. 엔티티 속성 vs 연결 컬렉션 (CLAUDE.md §0-6)

  • 엔티티 속성(기본·컨택·주소): 한 몸 — read↔edit 토글 + 통째 저장(또는 카드별). useMemberFormrecord/draft가 바인딩.
  • 연결 컬렉션(차량·일원·증빙〔사업자+현금영수증〕·페이〔가상계좌+자동이체〕·노트): 독립 생명주기 1:N. 탭 + disclosure + 자체 Data Tool Bar(생성/연결/해제/삭제) + 자체 상세 시트. useMemberForm과 무관, read↔edit 토글과 분리.
  • 시트 폭 sheet-width-3xl(48rem) — 세로 폼 집중 폭(§0-5).

8. 확장 / 전파 포인트

  1. 행별 레코드 주입(❌예정): openRead(member) 인자 전달 + record 행별 매핑. 현재 데모 싱글톤.
  2. 실제 디렉터리/스토어: DEMO() reactive 싱글톤 → API 백킹 스토어로 교체. record/draft 계약은 유지.
  3. 유효성 확장: valid에 등록번호 포맷·연락처 정규식 등 추가 시 useMemberForm computed에 집중(컴포넌트 ad-hoc 금지).
  4. 연결 컬렉션 추가: 새 1:N은 같은 패턴(탭 + Data Tool Bar + 외부 도메인 시트 import). 속성 폼에 섞지 말 것.
  5. 다른 마스터데이터 리소스(vehicle/business 등) 목록 IA: 본 패턴(이름 클릭 진입 + table-hover + 상세버튼 폐기)을 동일 적용 — 본 문서가 레퍼런스.
  6. leysys-design SSOT: card/field/sheet/table 클래스는 전부 SSOT 카피본. DS 결함은 본 레포 즉석 패치 금지(§SSOT 강제 룰).