Skip to content

프로세스 노드 게시 거버넌스 — 설계·작업 계획 ​

작성 시각: 2026-09-13 범위: ProcessCenter 노드 기본정보 화면 + 노드 버전/게시 승인 워크플로우(스키마·서버·클라이언트) 관련 문서: docs/ANT_DESIGN_REFACTOR.md(공용 UI 전환), docs/COMMON-UI-HANDOFF.md(공용 컴포넌트 인수인계)

이 문서는 프로세스 노드의 기본정보 재구성과 버전·게시 승인 워크플로우의 확정 설계와 진행 상태를 기록한다. 새 세션은 이 문서만 읽고 이어서 진행할 수 있다.

1. 목표 ​

  1. 노드 상세의 기본정보를 요약형으로 재구성해 읽기 화면의 밀도를 낮춘다(RACI는 4행 표 → 1줄 칩).
  2. 노드 편집을 드래프트 → 검토 요청 → 승인(게시) 절차로 통제하고, 게시 시점의 불변 게시본(스냅샷) 을 남긴다.
  3. 승인 권한은 노드의 A(Accountable) 담당자가 갖고, 관리자(admin)는 예외적으로 허용한다.

2. 확정 결정 (사용자 승인, 2026-09-13) ​

#항목결정구현 결과
1게시본 불변성버전 스냅샷(불변)process_node_versions는 게시 시 INSERT만 수행하고 수정 경로를 두지 않는다. 노드 행은 드래프트(현재값), 이력은 게시본이 담당한다.
2A 승인자 식별사용자 참조 테이블process_node_raci_users(raciRole='A', directoryUserId)로 승인자를 보관하고, 판정 시 사용자 식별자(name/email/loginId/openId/id)와 일치 비교한다. 참조가 없으면 레거시 문자열(raciAccountable)로 폴백한다.
3승인 깊이전결규정 결재선 재사용게시 승인 시 resolveApprovalPolicyForL6(L6/L7 노드)로 유효 정책을 해석해 게시본 스냅샷에 결재선을 함께 기록한다. 정책 단계 참여자는 roleMasterId·participantLabel(라벨)이라 사용자 단위 권한은 A 참조로 판정한다(라벨→사용자 매핑은 후속).
4상태 enumIN_REVIEW 추가 + ACTIVE=게시DRAFT / IN_REVIEW / ACTIVE / INACTIVE / RETIRED (기존 데이터·화면 호환 유지).

3. 데이터 모델 ​

대상변경비고
process_nodesstatus에 IN_REVIEW 추가, publishedVersion(마지막 게시 버전), submittedAt(검토 요청 시각), approvedBy/approvedAt(게시 승인자·시각) 추가version은 게시 시 증가
process_node_versions신설 (processId, version) 유니크, payload(게시본 JSON), changeSummary, createdBy, approvedBy, approvedAt게시본 불변 스냅샷
process_node_raci_users신설 (processId, raciRole, directoryUserId) 유니크, sortOrderR/A/C/I 사용자 참조
portal_notifications신설 (portalId, recipientId, dedupeKey) 유니크, kind/title/body/priority/processId/readAt포털 공통 알림함(PF @hohyun94/portal-operations 계약 준수, 중복 차단)
process_node_versions (AI)aiSummary·aiSummaryDetail(bullets·changedAreas·riskLevel)·aiSummaryModel·aiSummaryStatus·aiSummaryGeneratedAt·aiSummaryInputHash 추가AI가 직전 게시본과 비교한 변경 요약(참고용 · 확정은 사람)
process_histories.actionsubmit_review / approve / reject / retire 추가이력 전용 액션

마이그레이션: drizzle/0027_process_node_publish_approval.sql, drizzle/0028_numerous_lily_hollister.sql(MySQL), drizzle/postgres-migrations/0014_process_node_publish_approval.sql(PostgreSQL, 멱등 DDL). PostgreSQL 개발 DB 적용 완료.

검증 범위(2026-09-13 결정): 현 단계는 PostgreSQL만 검증한다. MySQL 마이그레이션(0027·0028)은 파일만 유지하고 생성·적용·검증을 수행하지 않는다(후속, §8-3). 새 마이그레이션도 PostgreSQL(pnpm db:postgres:generate)만 대상으로 만들고, PostgreSQL DDL은 재실행에 안전하도록 멱등(IF NOT EXISTS)하게 작성한다.

DB 전략(2026-09-13 확정): 트랜잭션/업무 데이터는 MySQL·PostgreSQL 이중 지원을 유지하고, 벡터 검색(RAG)만 PostgreSQL+pgvector 또는 외부 벡터 저장소로 분리한다. AI 완성(요약·번역·정합성)은 DB 중립이다 → docs/DB_STRATEGY.md.

4. 상태 흐름과 권한 ​

DRAFT ──검토 요청──▶ IN_REVIEW ──승인(게시)──▶ ACTIVE ──폐기──▶ RETIRED
  ▲                     │                                   ▲
  └────── 반려 ─────────┘            INACTIVE ──폐기──────────┘
동작허용상태 조건
생성·수정·첨부·RACI 지정R 담당자 · 관리자IN_REVIEW·RETIRED에서는 잠금
검토 요청R 담당자 · 관리자DRAFT/ACTIVE/INACTIVE
승인·게시A 승인자 · 관리자IN_REVIEW
반려A 승인자 · 관리자IN_REVIEW → DRAFT
폐기A 승인자 · 관리자RETIRED가 아닌 상태
  • IN_REVIEW 전이는 전용 절차로만 가능하다(일반 수정 API로 상태만 바꾸는 우회 차단).
  • 게시 시 version/publishedVersion을 1 증가시키고 게시본 스냅샷을 남긴다.

5. API 표면 (process 라우터) ​

프로시저종류입력설명
submitForReviewmutation{ id, changeSummary? }검토 요청(IN_REVIEW)
approvemutation{ id, changeSummary? }승인·게시(게시본 확정)
rejectmutation{ id, changeSummary? }반려(DRAFT 복귀)
retiremutation{ id, changeSummary? }폐기(RETIRED)
versionsquery{ id }게시본 이력(최신순, 스냅샷 본문 제외)
raciUserRefsquery{ id }RACI 사용자 참조 목록
saveRaciUsersmutation{ id, raciRole, directoryUserIds[] }역할 단위 참조 교체(검토 중·폐기 잠금)
inboxquery—내 결재함(파생) — IN_REVIEW ∧ 내가 A 승인자인 노드 목록
notifications.listquery{ unreadOnly?, limit? }내 알림함(PF 계약 저장형, 최신순)
notifications.unreadCountquery—미읽음 알림 수(배지)
notifications.markReadmutation{ ids? , all? }읽음 처리(대상은 로그인 사용자 식별자로 한정)
analyzeVersionmutation{ id, version }게시본 AI 변경 재분석(A 승인자·관리자, PF @hohyun94/portal-ai)

6. 구현 현황 ​

  • [x] Phase A — 기본정보 요약형: 상세를 식별 정보 / 설명 / 담당(담당조직 + RACI 칩)으로 재구성, 편집 팝업 라벨 정렬 (ProcessWorkspace.tsx, ProcessManagementPanel.tsx)
  • [x] Phase B — 스키마·마이그레이션: 3개 마이그레이션 + 개발 DB 적용·검증
  • [x] Phase C — 서버: shared/processAccess.ts(승인 판정·편집 잠금), server/processAccess.ts, server/repositories/processGovernanceRepository.ts, server/services/processGovernanceService.ts, 라우터 7개 프로시저, 일반 수정/첨부 잠금 가드
  • [x] Phase D — 클라이언트: client/src/components/process/ProcessGovernanceBar.tsx(상태 배지 + 검토 요청/승인·게시/반려/폐기) 를 노드 상세 헤더에 연결
  • [x] Phase E — 사용자 참조·이력 UI:
    • 편집 저장 시 RACI 사용자 참조를 함께 저장(client/src/lib/raciReferences.ts → saveRaciUsers, 이메일 우선·openId 폴백) — A 승인자 판정이 사용자 참조로 동작
    • 상세 패널에 게시본 이력 추가(client/src/components/process/ProcessVersionHistory.tsx → process.versions)
  • [x] Phase F — 알림(1단계 + 2단계):
    • 1단계 파생 인박스(무저장): process.inbox — IN_REVIEW ∧ 내가 A 승인자인 노드(권한 판정 재사용)
    • 2단계 저장형 인앱 알림함: PF @hohyun94/portal-operations(v0.1.0) 계약 편입 → portal_notifications + dispatchProcessNotification(중복 차단) · 검토 요청 시 A 승인자, 승인·반려 시 R 담당자에게 발송
    • UI: client/src/components/process/ProcessNotificationCenter.tsx(노드 상세 헤더의 알림 배지 + 결재 대기/알림 목록 + 읽음 처리 + 노드 이동)
  • [x] Phase G — AI 게시본 차이 자동 분석:
    • 게시 확정 시 직전 게시본(v{N-1}) vs 신규(v{N}) 스냅샷을 비동기로 비교해 process_node_versions에 aiSummary·aiSummaryDetail(bullets·changedAreas·riskLevel)·aiSummaryModel·aiSummaryStatus·aiSummaryGeneratedAt·aiSummaryInputHash 기록
    • PF 공통 계층(@hohyun94/portal-ai 0.1.0) 사용 — OpenAI 호환 엔드포인트라 사내 LLM과 외부 클라우드 API를 환경변수만 바꿔 지원
    • AI 미설정·실패·변경 없음은 SKIPPED·FAILED로만 남기고 게시 절차와 수동 요약은 그대로 유지(A 승인자·관리자만 process.analyzeVersion으로 재분석)
    • UI: 게시본 이력에 PortalAiSummaryCard(요약·불릿·영향 영역·모델·시각·다시 분석) 표시
  • [ ] Phase H — 후속: 결재선 라벨→사용자 매핑, 메일 채널, PF 셸 공용 알림 슬롯 승격, MySQL 마이그레이션 배포 적용

7. 검증 ​

항목결과
pnpm check(tsc)0 errors
server/processPublishGovernance.test.ts검토 요청·게시·반려·폐기·이력·결재선 기록 검증
server/processAccess.test.tsA 승인 판정(참조/레거시/관리자)·편집 잠금 검증
client/.../ProcessGovernanceBar.test.tsx상태별 버튼 노출·배지 검증
client/.../ProcessVersionHistory.test.tsx게시본 이력 표시·빈 상태 검증
client/src/lib/raciReferences.test.ts피커 선택값 → 사용자 참조 환산·원문 보존 검증
server/notificationService.test.ts수신자 없음·중복 차단·다중 수신자·내 알림함/읽음 처리 검증
server/processVersionAiService.test.tsAI 비활성(SKIPPED)·변경 없음·정상 요약(불릿·영향·해시)·실패(FAILED) 시 게시 이력 유지 검증
client/.../ProcessVersionHistory.test.tsx게시본 이력 + AI 요약 카드(요약·불릿·모델) 표시 검증
client/.../ProcessNotificationCenter.test.tsx배지 합산·패널 목록·노드 이동·읽음 처리 검증
PostgreSQL 실 DB 스모크(pnpm db:postgres:verify-governance, 트랜잭션 롤백)참조 저장(중복 제거)·게시본 생성·상태 전이(IN_REVIEW)·알림 발송/중복 차단/읽음 처리까지 실제 PostgreSQL에서 실행 후 롤백 — 상태/행 불변 확인
개발 컨테이너마이그레이션 적용 후 http://localhost:3000/ 200 응답

8. 후속 작업 메모 ​

  1. 결재선 사용자 매핑: 전결규정 단계의 participantLabel/roleMasterId를 사용자·역할과 연결해 사용자 단위 승인선까지 확장.
  2. 알림 확장: 메일 채널(NotificationChannel 구현 추가)과 PF 셸 공용 알림 슬롯(알림센터) 승격 — 저장·중복 차단은 이미 PF 계약을 따르므로 채널/UI만 교체하면 된다.
  3. 배포(MySQL, 범위 밖): MySQL 마이그레이션(0027·0028)은 파일만 유지한다. MySQL 환경 생성·적용·검증은 PostgreSQL 검증이 끝난 뒤 별도로 진행한다.
  4. 개발 스택 주의: 컨테이너 시작 시 마이그레이션이 실행되므로 새 마이그레이션은 멱등하게 작성한다(이미 적용된 DB에서 재실행되어도 실패하지 않도록).
  5. 기존 실패 테스트(거버넌스와 무관): TaskFlowMap.test.tsx 3건, DirectoryManagementPanel.test.tsx 2건.
  6. AI·검색 서비스 경계: 탐색 실행 조합은 ExploreGateway(이 포털은 POST /api/explore/v1/search 어댑터 제공), 문서 인덱스·RAG·citation은 RagService, 모델 접근·키·쿼터·비용·감사는 ModelGateway가 소유한다(현재 RAG는 로컬 구현이며 이전 지점은 VectorStore·RagService API).

9. 노드 code 정책 (참조정보 계약 v2 확정, 2026-09-20) ​

노드 code 가 다른 시스템이 인용하는 표준 식별자가 된다(공통 규약: portal-foundation docs/REFERENCE_DATA_SUPPLY_CONVENTION.md, 이 저장소 계약: docs/PROCESS_REFERENCE_CONTRACT.md). 그래서 아래를 규칙으로 고정한다.

9.1 발급 규칙 ​

  • 확정 — 발급 규칙(dev DB 실측 2026-09-20): 코드는 <레벨>-<6자리 순번> 이다 — L1-000001, L2-000001, L3-000001 … 부모 코드를 접두로 쓰지 않는다(예: L3-000001 의 부모는 L2-000003). 부모 관계는 내부 parentId 로 연결하고, 계약에서는 parentNodeCode 로 표현한다.
  • L1 루트 코드가 분류 축(taxonomyCode)이다. 새 분류는 루트(L1)부터 만든다.
  • 전역 유일은 DB unique 제약(process_nodes_code_unique)이 보장한다. 내부 대리 키(id)는 계약 payload 에 내보내지 않는다.

9.2 변경 규칙 (D2 — 게시 후 코드 불변) ​

  • 게시본이 있는 노드(publishedVersion != null)의 code 는 변경할 수 없다. 저장 경로(server/repositories/processRepository.ts updateProcessNode)가 assertNodeCodeChangeAllowed 로 거부한다(오류: NodeCodeImmutableError).
  • 기동 코드 정규화도 게시본을 건너뛴다 (processCodeNormalizationService): 레거시 코드 정비가 참조를 조용히 바꾸지 않도록 게시본을 제외하고 건수·대상을 경고로 보고한다.
  • 불가피한 변경은 새 code 발급 + 기존 code 를 승계(successorNodeCodes)로 연결한다.
  • 게시 전(DRAFT 등) 노드는 자유롭게 고칠 수 있다 — 게시가 기준정보로 굳는 시점이다.

9.3 유일성 점검 (회귀 게이트) ​

process_nodes.code 의 전역 unique 제약은 이미 있다(process_nodes_code_unique — schema 선언 + 마이그레이션 0000/0001 + 적용 DB). 그래서 이 점검은 "제약을 걸기 위한 선행 작업"이 아니라 데이터 작업 전 정합 확인용이다.

  • 데이터 가져오기·병합·복원 전후에 중복 0건을 확인한다(제약이 있어도 적재 경로가 제약을 우회하면 깨질 수 있다).
  • 다른 환경(신규 설치·이관)에서 스키마가 실제로 제약을 갖고 있는지 확인한다.
bash
pnpm db:check-code-uniqueness   # 읽기 전용. 종료 코드 0=중복 0건 · 1=중복 발견 · 2=점검 불가(DB 설정)
  • 앞뒤 공백만 다른 code 도 같은 코드로 비교한다(trim 후 판정).
  • code 가 비어 있는 노드는 중복은 아니지만 표준 식별자가 될 수 없으므로 함께 경고한다.
  • "중복이 있다"(1)와 "점검하지 못했다"(2)를 구분한다 — 게이트가 거짓 통과하지 않게 하기 위해서다.

9.4 업무 코드 별칭 (businessCode — 식별자와 분리, 확정 2026-09-20) ​

결정: 표준 식별자는 계속 시스템 발급 순번(code)이고, 업무 코드는 별칭 필드로 분리한다.

  • process_nodes.businessCode(varchar 80, nullable, 전역 유일) — 사람이 읽는 참조용(예: IT-OPS).
  • 식별자가 아니다: 다른 시스템은 이 값으로 노드를 참조하지 않는다(개편 때 바뀔 수 있다). 참조는 taxonomyCode·nodeCode(=code) 로 한다.
  • 유일성: 값이 있으면 중복될 수 없다(서비스가 CONFLICT, DB 가 고유 인덱스로 이중 방어). 없으면 null.
  • 변경: D2(게시 후 코드 불변)의 대상이 아니다 — 별칭은 표시용이므로 바꿀 수 있고, 변경은 이력(changedFields)에 남는다.
  • 설정 경로: 노드 생성·수정 API(businessCode). 표기 규칙(대소문자·접두)은 업무가 정하며 시스템은 강제하지 않는다.
  • 마이그레이션: drizzle/postgres-migrations/0017_process_node_business_code.sql(MySQL 파일은 0027·0028 과 같이 후속).

왜 code 자체를 업무 코드로 쓰지 않는가: code 는 이미 게시 후 불변인 참조 식별자다. 개편·병합 때 의미가 바뀌는 값을 식별자로 쓰면 참조가 끊기고 승계 절차를 타야 한다. "안정 식별자"와 "사람이 읽는 의미"를 분리하면 둘 다 지킬 수 있다.

dev DB 실행 결과(2026-09-20): 노드 19건 · 게시본 보유 1건 · 중복 0건 · process_nodes_code_unique 존재.