Files
frontend/apps/as/docs/review-manpower-management/SPEC.md
T

23 KiB

화면설계서: 검토인력관리

1. 기본 정보

항목 내용
버전 0.2
프로세스명 UI설계
화면명 검토인력관리
화면 유형 [v] 조회 [v] 입력 [ ] 수정 [v] 삭제 [ ] 출력
메뉴 경로 디지털행정지원 > 사이트관리 > 검토인력관리

2. 화면 목적

조직도에서 한 명의 사용자를 선택한 뒤, 결재 단계(접수·검토·승인·최종승인)별 인원 배치 영역에 즉시 등록한다. 등록·목록 갱신은 별도 저장 버튼 없이 [선택 인원 배치] 클릭 시 rvw-mnpw API로 바로 반영한다. 좌측 조직도는 CMS 조직·사용자 트리 API를, 우측 배치 목록은 검토인력관리 API를 사용한다.


3. 화면 구성

3-1. 전체 레이아웃

┌──────────────────────────────────────────────────────────────────────────────────────────────────┐
│  페이지 타이틀: 검토인력관리              브레드크럼: 디지털행정지원 > 사이트관리 > 검토인력관리 │
├─────────────────────────────────┬──────────────────────────────────────────────────────────────────┤
│  [영역 A] 조직도                │  [영역 B] 인원 배치 (결재구분별 4블록, 세로 스택)                │
│  제목: 조직도                   │                                                                  │
│  [A-1] 검색:                    │  ┌─ [B-1] 접수 ───────────── count ─ [선택 인원 배치] ─┐         │
│  [성명·부서 검색] [조회][초기화]│  │  (배치 목록 또는 빈 상태 문구)                       │        │
│  선택 안내:                     │  └──────────────────────────────────────────────────────┘        │
│  "선택된 인원: {이름}           │  ┌─ [B-2] 검토 ───────────── count ─ [선택 인원 배치] ──┐        │
│   ({부서} / {직급})"            │  └──────────────────────────────────────────────────────┘        │
│  [A-2] 조직 트리 + 사용자 행    │  ┌─ [B-3] 승인 ───────────── count ─ [선택 인원 배치] ──┐        │
│  ┌────────────────────┐         │  └──────────────────────────────────────────────────────┘        │
│  │ 조직 트리 + 사용자 │         │  ┌─ [B-4] 최종승인 ───────── count ─ [선택 인원 배치] ──┐        │
│  │ (단일 선택 강조)   │         │  └──────────────────────────────────────────────────────┘        │
│  └────────────────────┘         │                                                                  │
└─────────────────────────────────┴──────────────────────────────────────────────────────────────────┘

3-2. 화면 데이터 흐름

BEGIN

  // 최초 진입
  CALL userOgnzService.findOgnzUserTree()
  DISPLAY 조직도 트리 (영역 A)

  CALL rvwMnpwMngService.findPage({ size: 충분히 큰 값 })   // 또는 atrzSeComCd별 분할 조회
  GROUP BY atrzSeComCd
  DISPLAY 인원 배치 4블록 (영역 B-1 ~ B-4) + 건수 뱃지

  // 영역 A-1: 조직도 검색 (클라이언트 필터 — 서버 API 없음)
  ON [조회] 버튼 CLICK / 검색 폼 SUBMIT DO
      SET displayTree = filterOgnzUserTree(원본트리, keyword)
      SET expandedOgnzSnIds = collectExpandedOgnzSnIds(displayTree)   // 매칭 노드 자동 펼침
      DISPLAY 필터된 트리 (영역 A-2)
  END ON

  ON [초기화] 버튼 CLICK DO
      RESET keyword = ""
      RESET displayTree = 원본트리
      RESET expandedOgnzSnIds = 빈 Set (또는 기본 접힘)
      DISPLAY 전체 트리 (영역 A-2)
  END ON

  // 영역 A-2: 사용자 단일 선택
  ON 조직도 사용자 행 CLICK DO
      VALIDATE 동일 행 재클릭이 아닌 경우만 변경
      SET selectedPerson = { userUuid, userNm, ognzSn, ognzNm, jbgdSeCd, ... }
      DISPLAY "선택된 인원: {userNm} ({ognzNm} / {직급명})"
  END ON

  // 영역 B-k: 선택 인원 즉시 배치
  ON [B-k] [선택 인원 배치] 버튼 CLICK DO
      VALIDATE selectedPerson 존재
      SET targetAtrzSeComCd = 블록 k 에 매핑된 결재구분 코드 (§4-2 표)
      CALL rvwMnpwMngService.save({
        atrzSeComCd: targetAtrzSeComCd,
        ognzSn: selectedPerson.ognzSn,
        userUuid: selectedPerson.userUuid
      })
      ON 성공 DO
          CALL rvwMnpwMngService.findPage (또는 해당 블록 목록만 갱신)
          DISPLAY [B-k] 배치 목록·건수 뱃지 갱신
          TOAST "배치되었습니다."
      END ON
      ON 실패(이미 존재) DO
          TOAST (백엔드 메시지 연동)
      END ON
  END ON

  // 영역 B-k: 배치 인원 제거 (UI 상 삭제 UX — 스크린샷 미표기, API는 제공됨)
  ON 배치 인원 [삭제] / [제거] CLICK DO
      CONFIRM "해당 인원을 배치에서 제거하시겠습니까?"
      CALL rvwMnpwMngService.delete(atrzSeComCd, ognzSn, userUuid)
      CALL rvwMnpwMngService.findPage
      DISPLAY [B-k] 목록·뱃지 갱신
  END ON

END

4. 영역 A — 조직도

4-1. 표시·상호작용

요소 내용
섹션 제목 조직도
검색 폼 (A-1) 성명·부서 키워드 입력 + [조회] + [초기화] — §4-3
선택 안내 문구 선택된 인원: {userNm} ({ognzNm} / {직급표시}) — 미선택 시 안내 문구
트리 (A-2) 본부 → 팀 계층 + 팀 소속 사용자 목록 (displayTree 기준 표시)
선택 규칙 사용자 1명만 선택 가능 (라디오형 단일 선택)
선택 시각 피드백 선택된 사용자 행 강조(배경·테두리)
로딩/오류/빈값 Skeleton / 오류 문구 / "등록된 조직/사용자가 없습니다."
검색 결과 없음 필터 후 매칭 노드·사용자 0건. "조회된 인원이 없습니다." 문구 노출

4-2. 조직도 데이터 매핑

UI 표시 트리 DTO 필드 (OgnzUserTreeResponseDto) 비고
조직명 ognzNm 접기/펼치기 노드
사용자명 users[].userNm 클릭 시 선택
부서(안내 문구) ognzNm (소속 팀 노드) 선택 시 selectedPerson.ognzNm
직급(안내 문구) users[].jbgdSeCd 또는 jbpsSeCd 공통코드 라벨 변환
저장용 조직 SN ognzSn 사용자가 속한 리프/팀 노드의 ognzSn
저장용 사용자 users[].userUnqId API body userUuid로 전달 (§6-3)

4-3. 조직도 검색 (영역 A-1)

결재선 관리 AprvrPickerDialog와 동일한 클라이언트 사이드 필터 패턴을 사용한다. 별도 검색 API는 없으며, useOgnzUserTree로 받은 원본 트리를 filterOgnzUserTree로 가공해 displayTree에 반영한다.

필드 정의

표시명 입력 유형 필수 기본값 API 파라미터 매핑 비고
성명·부서 검색 Text N (빈값) — (클라이언트만) placeholder="성명·부서 검색"
조회 Button — — — type="submit" — 폼 제출 시 필터 적용
초기화 Button — — — type="button" — 키워드·트리·펼침 초기화

필터 규칙 (filterOgnzUserTree)

대상 매칭 조건
조직명 ognzNm에 키워드 포함 (대소문자 무시)
사용자명 userNm에 키워드 포함 (대소문자 무시)
조직 매칭 시 해당 노드 소속 사용자 전원 유지
사용자만 매칭 시 매칭 사용자만 남기고 상위 조직 노드는 유지(경로 보존)
하위 조직 재귀 필터 — 자식·손자 노드 매칭 시 상위도 표시
키워드 빈값 원본 트리 전체 반환

조회 후 트리 펼침 (collectExpandedOgnzSnIds)

  • [조회] 성공 후 필터 결과 트리의 모든 노드 ognzSn을 expandedOgnzSnIds에 넣어 자동 펼침
  • 검색 결과에서 사용자·하위 조직을 바로 확인할 수 있도록 함

초기화 동작

상태 초기화 후 값
keyword ""
displayTree useOgnzUserTree 원본 data
expandedOgnzSnIds new Set() (전체 접힘)
selectedPerson 유지 — 검색 초기화와 선택 해제 분리

버튼

버튼명 동작
조회 filterOgnzUserTree(data, keyword) → displayTree 갱신
초기화 키워드·표시 트리·펼침 상태 리셋 (§3-2 ON [초기화] 블록)

5. 영역 B — 인원 배치

5-1. 블록 공통

요소 내용
블록 제목 접수 / 검토 / 승인 / 최종승인
건수 뱃지 해당 atrzSeComCd에 배치된 인원 수
액션 버튼 [선택 인원 배치] — 클릭 시 즉시 save (§3-2)
빈 상태 "배치된 인원이 없습니다."
배치 목록(있을 때) 인원명·부서·직급 등

5-2. 결재구분 ↔ 블록 매핑

블록 UI 제목 atrzSeComCd 비고
B-1 접수 ATRZ00 결재선 관리 ATRZ_RECEIVER_CD와 동일
B-2 검토 ATRZ01 결재선 ATRZ_REVIEWER_1_CD. ATRZ02 별도 블록 여부 — TBD
B-3 승인 ATRZ03 결재선 ATRZ_APPROVER_CD
B-4 최종승인 ATRZ99 결재선 ATRZ_FINAL_CD

공통코드 마스터 ATRZSE / 화면 셀렉트용 ATRZSECD와 정합성은 배포 환경 코드표로 최종 확인.

5-3. 배치 목록 항목 (표시)

표시명 Field (응답 DTO) 비고
사용자명 userUuidNm @CodeName 미적용 시 TBD
결재구분 atrzSeComCdNm 블록 제목과 중복 — 생략 가능
조직 SN ognzSn 삭제 API path에 사용
사용자 userUuid 삭제 API path에 사용

5-4. 버튼

버튼명 위치 동작
선택 인원 배치 각 B-k 헤더 save 후 목록·뱃지 갱신 (§3-2)
제거 목록 항목 delete 후 목록·뱃지 갱신 (§3-2)

5-5. 검증·제약

규칙 내용
선택 필수 [선택 인원 배치] 시 selectedPerson 없으면 버튼 비활성 또는 안내
단일 선택 조직도에서 사용자 1명만 selectedPerson 유지
중복 등록 동일 PK (atrzSeComCd, ognzSn, userUuid) 재등록 시 서버 오류
다건 배치 동일 블록에 서로 다른 사용자·조직은 복수 행 가능 (PK가 다름)
결재구분 변경 본 화면 주 흐름은 save만

6. API/DTO 명세

6-1. 공통

항목 내용
인증 JWT (Authorization 헤더)
페이징 Spring Pageable: page, size, sort → PageResponse<T>
프론트 엔티티 @/entities/user-organization, @/entities/rvw-mnpw

6-2. 조직·사용자 트리 (영역 A) — user-ognz

항목 내용
서비스 cms-service
Base prefix /cms-service/api/v1/user-ognz
기능 Method URL Request DTO Response DTO API
조직·사용자 트리 GET /treeuser — OgnzUserTreeResponseDto[] userOgnzService.findOgnzUserTree

OgnzUserTreeResponseDto: ognzSn, upOgnzSn, ognzNm, users[], children[]

OgnzUserTreeUserDto: userUnqId, userNm, jbpsSeCd, jbgdSeCd

React Query: useOgnzUserTree

6-3. 검토인력관리 (영역 B) — rvw-mnpw

항목 내용
서비스 as-service
Base prefix /as-service/api/v1/rvw-mnpw
ENDPOINT ENDPOINT.AS_COMMON_SERVICE.RVW_MNPW
기능 Method URL Request DTO / Query Response DTO API
목록 조회 GET / RvwMnpwMngSearchDto + Pageable Page<RvwMnpwMngResponseDto> rvwMnpwMngService.findPage
상세 조회 GET /{atrzSeComCd}/{ognzSn}/{userUuid} Path RvwMnpwMngResponseDto rvwMnpwMngService.findById
등록(즉시 배치) POST / RvwMnpwMngSaveRequestDto RvwMnpwMngResponseDto (201) rvwMnpwMngService.save
결재구분 변경 PUT /{atrzSeComCd}/{ognzSn}/{userUuid} RvwMnpwMngUpdateRequestDto RvwMnpwMngResponseDto rvwMnpwMngService.update
삭제 DELETE /{atrzSeComCd}/{ognzSn}/{userUuid} Path string rvwMnpwMngService.delete

RvwMnpwMngSearchDto: atrzSeComCd?, ognzSn?, userUuid? + page, size, sort

RvwMnpwMngSaveRequestDto: atrzSeComCd*, ognzSn*, userUuid*

RvwMnpwMngUpdateRequestDto: newAtrzSeComCd* — 본 화면 기본 흐름에서는 미사용

RvwMnpwMngResponseDto: atrzSeComCd, atrzSeComCdNm?, ognzSn, userUuid, userUuidNm?, rgtr?, regDt?

6-4. 조직도 ↔ 검토인력 필드 매핑 (저장 시)

조직도 (OgnzUserTreeUserDto + 노드) RvwMnpwMngSaveRequestDto 비고
(블록 k의 atrzSeComCd) atrzSeComCd §5-2 표
소속 팀 ognzSn ognzSn
userUnqId userUuid 문자열(UUID)로 직렬화

7. UI 구현 가이드

7-1. 기술 스택·원칙

  • 공통 컴포넌트: @common/components (Button, Badge, Card 또는 섹션 레이아웃)
  • 데이터: React Query — useOgnzUserTree, useRvwMnpwMngPage, useSaveRvwMnpwMng, useDeleteRvwMnpwMng
  • 상태: selectedPerson(단일), keyword, displayTree, expandedOgnzSnIds, 블록별 배치 목록(서버 데이터 파생)
  • 조직도 검색: filterOgnzUserTree, collectExpandedOgnzSnIds (widgets/ognz-user-tree/lib 또는 페이지 lib)
  • 저장 UX: 별도 화면 저장 없음 — [선택 인원 배치] = save mutation 즉시 호출
  • 조직도 UI: 결재선 관리 ognz-user-tree.tsx를 widgets로 승격해 공통화

7-2. 모듈·파일 배치(제안)

구분 경로
라우트 apps/as/app/(with-base-layout)/rvw-mnpw-mng/page.tsx
페이지 apps/as/src/pages/rvw-mnpw-mng/
UI .../ui/rvw-mnpw-mng-page.tsx, rvw-mnpw-assignment-panel.tsx 등
model .../model/selected-ognz-user.ts, rvw-mnpw-block-config.ts (atrzSeComCd 상수)
lib .../lib/group-rvw-mnpw-by-atrz-se.ts (목록 → B-1~B-4 분배)
위젯 apps/as/src/widgets/ognz-user-tree/ — 아래 이전 대상
엔티티 entities/user-organization/, entities/rvw-mnpw/ (구현 완료)

widgets/ognz-user-tree 이전 (결재선 → 공통)

현재 (결재선 관리) 이전 후 (widgets)
pages/approval-line-management/ui/ognz-user-tree.tsx widgets/ognz-user-tree/ui/ognz-user-tree.tsx
pages/.../ui/ognz-user-tree-node.tsx widgets/ognz-user-tree/ui/ognz-user-tree-node.tsx
pages/.../model/reviewer-option.ts widgets/ognz-user-tree/model/selected-ognz-user.ts (이름·필드 확장: ognzSn 필수)
pages/.../lib/filter-ognz-user-tree.ts widgets/ognz-user-tree/lib/filter-ognz-user-tree.ts (필수 — 검색 공통화)
widgets/ognz-user-tree/index.ts OgnzUserTree, OgnzUserTreeNode, 타입 export

결재선 AprvrPickerDialog는 @/widgets/ognz-user-tree import로 교체. 검토인력관리 페이지는 인라인 조직도(다이얼로그 없음)로 동일 위젯 사용.

SelectedOgnzUser — ReviewerOption 확장:

export interface SelectedOgnzUser {
  userUuid: string; // OgnzUserTreeUserDto.userUnqId
  userNm: string;
  ognzSn: number;
  ognzNm: string;
  jbgdSeCd?: string;
  jbpsSeCd?: string;
}

7-3. 영역별 UI 매핑

영역 주요 UI
A-1 검색 폼 (Input + 조회/초기화) — AprvrPickerDialog 90~103행 패턴
A-2 OgnzUserTree(nodes={displayTree}) + 선택 안내 + useOgnzUserTree
B-k 제목·Badge(count)·선택 인원 배치·배치 목록(또는 empty)
공통 페이지 헤더·브레드크럼·2열 레이아웃 (grid / flex, 모바일은 스택)

7-4. 다이얼로그·라우팅

항목 내용
다이얼로그 없음 (조직도 인라인, 즉시 저장)
라우팅 메뉴 검토인력관리 단일 페이지

7-5. 데이터 흐름(구현 요약)

  • 진입: useOgnzUserTree → displayTree = data + useRvwMnpwMngPage({ page: 0, size: N }) → 4패널.
  • 검색: keyword 로컬 상태 → [조회] 시 filterOgnzUserTree + collectExpandedOgnzSnIds → OgnzUserTree에 displayTree 전달.
  • 초기화: keyword·displayTree·expandedOgnzSnIds 리셋 (selectedPerson 유지).
  • 선택: 트리 onSelect → selectedPerson (반드시 ognzSn 포함).
  • 배치: useSaveRvwMnpwMng().mutate({ atrzSeComCd, ognzSn, userUuid }) → onSuccess에서 rvw-mnpw query invalidate.
  • 삭제: 목록 항목에서 useDeleteRvwMnpwMng — UI 확정 후 연결.

7-6. 미확정 / 검증 필요

  • ATRZ02(검토2) 별도 블록 필요 여부
  • 권한(역할별 CRUD)