Skip to content

프로세스 참조정보 계약 v2 (프로세스 노드 · RACI · 시스템 매핑) ​

상태: 확정 · 구현: v2 server/remoteProcessNodesV2.ts (v1 경로는 2026-09-20 제거 — 공용 로직은 server/processNodeSource.ts) · 테스트: server/remoteProcessNodesV2.test.ts(16건) · server/processNodeSource.test.ts(8건) 소유: ProcessCenter — 프로세스는 L2 참조정보(2차 기준정보)이며, 소유는 이 도메인에 둔다. 공통 규약: portal-foundation docs/REFERENCE_DATA_SUPPLY_CONVENTION.md (L2 공급 5요소) 관련 문서: docs/PROCESS_NODE_GOVERNANCE_PLAN.md(게시 거버넌스), docs/EXPLORE_ADAPTER_CONTRACT.md(계약 문서 형식 선례), docs/DIRECTORY_AUTHORIZATION_INTEGRATION.md(L1 인용)

1. 위치 — L2 참조정보이고 소유는 ProcessCenter 다 ​

  • L1 최상단 기준정보(조직도·임직원·부서코드)의 정본은 PortalConsole 디렉터리다. 프로세스는 그 위에 얹힌 L2 다.
  • 프로세스는 도메인 안에서는 운영 기준정보이고, 밖에서는 참조 기준이다 — 신규 시스템이 어느 노드에 매핑되는지 식별하고, 인력 구조를 프로세스 뷰로 보는 교차 조회의 축이 된다.
  • 그래서 프로세스 데이터를 PortalConsole 로 옮기지 않는다. 공급 방식만 공통 규약을 따른다(읽기 전용·서버 간·표준 식별자·버전/변경 피드·온보딩).

이미 확보된 L1 인용 재료 (재사용) ​

L1프로세스 측 인용처상태
부서코드process_nodes.organizationCode✅ 있음(레거시 organization 폐기 예정)
임직원process_node_raci_users.directoryUserId✅ 있음((processId, raciRole, directoryUserId) 유니크)
시스템 코드process_nodes.systemId → systems.code(전역 유니크)✅ 있음(노드 → 시스템 방향)

RACI 는 참조가 정본이다(2026-10). 노드에 복사해 두던 이름 문자열 컬럼(raciResponsible…)은 제거했다. 목록 응답의 raciResponsible… 필드는 참조에서 조회 시점에 해석한 값이라 이름이 바뀌면 화면·검색·CSV 가 따라간다. 경위: docs/RACI_REFERENCE_MIGRATION_PLAN.md

2. 이전 v1 계약과 결함 (2026-09-20 제거 — 기록) ​

v1 경로(/api/v1/process-nodes*, server/remoteProcessNodes.ts)는 제거했다. 소비자(AgentCenter)가 v2 로 전환을 마쳤고, 아래 결함 D1·D2 를 안은 채로 남겨둘 이유가 없었다. 공용 로직(buildTreeIndex·decideNodeAccess)은 server/processNodeSource.ts 로 옮겼다.

경로 (인증 Authorization: Bearer <AGENT_CENTER_API_KEY>, 실패 시 401 process_node_service_auth_required)

메서드경로응답
GET/api/v1/process-nodes?taxonomyId=&cursor=&changedSince={ nodes, nextCursor?, snapshotToken, complete }
GET/api/v1/process-nodes/{sourceSystemId}:{taxonomyId}:{externalNodeId}ProcessNode | 404
POST/api/v1/process-node-access{ decisions, validUntil }

taxonomyId 는 L1 루트 노드의 code 다. 노출 상태·RACI 판정은 docs/PROCESS_NODE_GOVERNANCE_PLAN.md 를 따른다.

결함 (참조정보로 쓰기 전에 고쳐야 한다) ​

#결함근거(코드)영향
D1표준 식별자가 내부 대리 키다toProcessNodePayload: externalNodeId: String(node.id), parentExternalNodeId·pathIds 도 id 문자열. findNodeByKey: Number.parseInt(key.externalNodeId)DB 재적재·병합 시 id 가 바뀌면 참조가 끊긴다. 신규 시스템 매핑처럼 오래 남는 참조에 부적합
D2게시 거버넌스 미반영pageTaxonomyNodes 는 taxonomy 필터만 적용 — status 가 DRAFT/IN_REVIEW 인 노드도 목록에 실린다(PORTAL_VISIBLE_STATUSES 는 decideNodeAccess 에만 적용)작성 중 값이 참조 기준으로 퍼진다. 게시본(process_node_versions)과 어긋날 수 있다
D3code 에 유일성 제약이 없다 → 정정(2026-09-20): 결함이 아니었다스키마 선언(codeUnique: uniqueIndex("process_nodes_code_unique")), PostgreSQL 마이그레이션(postgres-migrations/0000_majestic_tempest.sql:396), MySQL 마이그레이션(0001_fuzzy_ogun.sql:24), 적용 DB(dev DB pg_indexes 확인)에 모두 있다. 중복 0건처음엔 컬럼 정의만 보고 "제약 없음"으로 적었으나 사실이 아니다 — 제약은 마이그레이션 0000/0001 부터 있었다. v2 는 유일성 작업 없이 code 식별자로 갈 수 있다

D1·D2 는 계약 v2 에서 해소한다. D3 은 위 정정대로 결함이 아니었다(없는 작업을 만들지 않는다).

3. v2 계약 (확정 제안) ​

3.1 표준 식별자 — 업무 코드 기반 ​

ts
/** 참조 표준 식별자. 소비자는 이 세 값으로 노드를 지목한다. */
type ProcessNodeRefV2 = {
  sourceSystemId: string;   // "process-center"
  taxonomyCode: string;     // L1 루트 노드의 code (분류 축)
  nodeCode: string;         // 노드의 업무 code (전역 유일)
};
  • 대리 키(id)는 계약에서 제거한다(D1). parentNodeCode·pathCodes[] 로 계층을 표현한다.
  • 확정(2026-09-20) — 유일성은 전역 유일: process_nodes.code 의 전역 unique 제약은 이미 적용되어 있다(process_nodes_code_unique — 스키마 선언·마이그레이션 0000/0001·dev DB 확인). 신규 마이그레이션은 필요 없다(dev DB 실측: 노드 19건 · 중복 0건). taxonomyCode(L1 루트)는 분류·스코프 속성으로 유지하고 식별의 필수 요소로 두지 않는다.
  • 확정 — 발급 규칙(dev DB 실측 2026-09-20): 코드는 <레벨>-<6자리 순번> 이다(L1-000001, L2-000001 …). 부모 코드를 접두로 쓰지 않는다 — 부모 관계는 내부 parentId 로만 연결하고 계약에서는 parentNodeCode 로 표현한다. L1 루트 코드가 분류 축(taxonomyCode)이다. (규칙 전문: docs/PROCESS_NODE_GOVERNANCE_PLAN.md §9.1)
  • 확정 — 게시 후 code 변경 금지(서버 강제): 게시본이 있는 노드(publishedVersion != null)는 저장 시 code 변경을 서버가 거부한다. 정책 문서만으로 두지 않는다. 불가피한 변경은 새 코드를 발급하고 기존 코드를 승계(successorNodeCodes)로 연결한다.
  • 업무 코드는 식별자가 아니다(확정 2026-09-20): 사람이 읽는 업무 코드(IT-OPS 등)는 별칭 필드(businessCode)로 분리한다(거버넌스 §9.4). 소비자는 별칭으로 노드를 참조하지 않는다 — 참조는 taxonomyCode·nodeCode 로 한다(개편 때 별칭이 바뀔 수 있다).
  • process_nodes.code 는 이미 검색(like)에 쓰이므로 값 자체는 재사용 가능하다. 바뀌는 것은 참조용 식별자로 승격된다는 점이다.

3.2 노출 기준 — 게시본만 (D2 해소) ​

  • publishedVersion 이 있는 노드(게시 이력 보유)만 공급한다. DRAFT·IN_REVIEW 는 어떤 경로로도 내보내지 않는다.
  • 응답에 status(ACTIVE/INACTIVE/RETIRED)와 publishedVersion 을 함께 싣는다 — 소비자가 "게시된 기준정보인지"를 판단한다.
  • snapshotToken 은 taxonomy별 게시 버전 기준으로 만든다(pc:{taxonomyCode}:{publishedVersion}).
  • 게시 거버넌스 정의는 docs/PROCESS_NODE_GOVERNANCE_PLAN.md 를 따른다(게시본 불변 스냅샷).

3.3 L1 인용 필드 (재정의 금지) ​

필드인용 대상규칙
organizationCode디렉터리 부서코드없으면 null — 이름을 복사하지 않는다
raci[].directoryUserId디렉터리 사용자 식별자process_node_raci_users 그대로, 역할은 R/A/C/I 로 유지
systemCodesystems.code노드 → 시스템 참조(코드). 시스템 이름은 소비자가 필요 시 조회
successorNodeCodes[]같은 taxonomy 의 노드 코드필드만 먼저 둔다 — 값은 프로세스 병합/승계 기능을 만들 때(폐기·병합 승인 시) 채운다. 소비자 규칙(확정): 참조 대상이 RETIRED 이고 승계가 있으면 승계를 따라가고, 승계가 없으면 그 참조를 무효로 표시한다(조용히 다른 노드를 가리키지 않는다)

3.4 엔드포인트 ​

메서드경로용도
GET/api/v2/process-nodes?taxonomyCode=&cursor=&changedSince=스냅샷·증분 목록(게시본만)
GET/api/v2/process-nodes/{taxonomyCode}:{nodeCode}resolve/검증 — 신규 시스템이 "이 노드에 매핑"을 확인할 때
POST/api/v2/process-node-access사용자별 VIEW/REVIEW 판정(코드 기준)
GET/api/v2/systems시스템·시스템 그룹 목록(systems.code 전역 유니크) — 노드의 systemCode 가 가리키는 정본. "신규 시스템이 어느 노드에 매핑되는가"를 코드로 잇는 축

경로 prefix 를 v2 로 올렸다. v1 은 제거했다(§2·§4) — v1 응답 필드(externalNodeId 등 id 기반)는 더 이상 존재하지 않는다.

3.5 응답 스키마 ​

json
{
  "nodes": [
    {
      "sourceSystemId": "process-center",
      "taxonomyCode": "IT-VC",
      "nodeCode": "IT-VC-02",
      "parentNodeCode": "IT-VC",
      "pathCodes": ["IT-VC", "IT-VC-02"],
      "levelCode": "L4",
      "name": "가상화 운영",
      "status": "ACTIVE",
      "publishedVersion": 3,
      "sourceVersion": "3",
      "organizationCode": "DEPT-IT-001",
      "businessCode": "IT-OPS",
      "raci": [{ "raciRole": "A", "directoryUserId": "hong.gd" }],
      "systemCode": "SYS-VMWARE",
      "successorNodeCodes": [],
      "updatedAt": "2026-09-20T00:00:00.000Z",
      "sourceUrl": "/processes?node=IT-VC-02"
    }
  ],
  "nextCursor": null,
  "snapshotToken": "pc:IT-VC:3",
  "complete": true
}
  • complete 는 한 응답에 전체 게시 스냅샷이 담겼을 때만 true 다(기존 규칙 유지 — 증분 응답을 삭제 판정에 쓰지 않는다).
  • sourceUrl 도 코드 기준으로 바꾼다(포털 화면이 코드로 딥링크를 만들 수 있어야 한다).
  • 부모가 게시본이 아닐 수 있다: 게시되지 않은 상위 노드의 코드라도 parentNodeCode·pathCodes 에는 담는다(계층 식별용). 소비자는 "목록에 없는 부모 코드"를 오류로 보지 않는다 — 그 노드는 아직 게시되지 않았을 뿐이다.

구현(2026-09-20): 라우트는 server/remoteProcessNodesV2.ts(registerRemoteProcessNodeV2Routes), server/app.ts 에서 v1 과 함께 등록한다. dev DB 실측 검증 — taxonomy L1-000001 목록에 게시본 1건만 반환되고 게시본이 아닌 노드 누출 0건, resolve 200, snapshotToken=pc:L1-000001:1.

3.6 인증 · 스코프 (퍼블릭 아님) ​

항목규칙
인증Authorization: Bearer <소비자 서비스 키> — 서버 간 전용, 브라우저 노출 금지
자격증명소비자별 키. 현재 단일 키(AGENT_CENTER_API_KEY)를 소비자 무관 자격증명으로 일반화하고, 소비자마다 발급·회수
스코프process:read (필요 시 process:access) — 조회 외 권한 없음
발견·온보딩PortalControlCenter 카탈로그에 capability 등록 → 신규 소비자는 카탈로그 발견 + 키 발급만으로 온보딩
접근 정책사용자 단위 판정은 ProcessCenter 가 최종 적용(process-node-access, RACI) — 소비자 화면 숨김에 의존하지 않는다
참조 추적어떤 소비자가 어떤 노드를 참조하는지 추적 가능해야 한다 — 기록 범위는 아래 확정 참조

확정(2026-09-20) — 자격증명 이행 순서: P0 는 ProcessCenter 가 소비자별 키를 자체 관리한다. 설정은 PROCESS_NODE_CONSUMERS_JSON(데이터)이고, v1 키 AGENT_CENTER_API_KEY 는 전환 기간 동안 두 스코프를 가진 소비자로 자동 승격한다. PortalControlCenter 카탈로그 연계는 후속이다 — 카탈로그 등록 메커니즘이 없는데 발급을 카탈로그로 몰아넣으면 신규 소비자를 붙일 수 없다.

bash
# PROCESS_NODE_CONSUMERS_JSON=[{"consumerId":"agent-center","secret":"<키>","scopes":["process:read","process:access"]}]
# scopes 생략 시 두 스코프를 모두 갖는다. JSON 이 깨지면 레거시 키로 폴백하고 경고를 남긴다.

스코프 매핑: process:read = 목록·resolve·systems, process:access = process-node-access.

발급 · 순환 절차 (ProcessCenter 에서 실행) ​

bash
# 발급: 출력된 JSON 한 줄을 PROCESS_NODE_CONSUMERS_JSON 에 넣고 재시작한다
pnpm process-node:consumer-key -- --consumer agent-center --scopes process:read,process:access

# 무중단 순환: 새 키를 추가(옛 키 유지) → 소비자를 새 키로 전환 → 옛 키 제거
pnpm process-node:consumer-key -- --consumer agent-center --scopes process:read,process:access --rotate
pnpm process-node:consumer-key -- --consumer agent-center --remove <옛 secret>

# 현재 목록(비밀은 끝 4자만 출력)
pnpm process-node:consumer-key -- --list
  • 비밀은 발급 시 한 번만 출력된다 — 소비자 쪽 설정에 바로 넣고 저장소·문서에 남기지 않는다.
  • 순환 기간에는 같은 consumerId 로 여러 secret 이 함께 유효하다(PROCESS_NODE_CONSUMERS_JSON 에 두 항목).
  • 소비자 측 확인: pnpm verify:process-integration(AgentCenter) — 상대 서버가 기동을 마친 뒤 실행한다(연결 실패는 종료 코드 2).
  • 회수: --remove <secret> 로 항목을 빼고 재시작한다(그 키는 즉시 401).

확정 — 참조 추적 기록 범위: 어느 소비자 키가 어느 노드를 resolve/조회했는지의 구조화 요약만 남긴다(원문·비밀정보 없음, §14.1 정신). 보존 기간은 사내 정책 확정 전까지 값을 두지 않는다 — 로그 스키마만 준비하고 임의 보존일수를 코드에 넣지 않는다.

카탈로그 등록 값(예) — 탐색 어댑터가 쓰는 등록 형식(docs/EXPLORE_ADAPTER_CONTRACT.md §2)과 같은 자리에 참조 capability 로 등록한다. 구체 등록 키·발급 절차는 §5-3 확정 대상이다.

json
{
  "portalId": "process-center",
  "capabilities": ["reference"],
  "reference": {
    "kind": "process-nodes",
    "endpoint": "/api/v2/process-nodes",
    "resolveEndpoint": "/api/v2/process-nodes/{taxonomyCode}:{nodeCode}",
    "authMode": "service_token",
    "scopes": ["process:read"],
    "l1Citations": ["organizationCode", "directoryUserId", "systemCode"]
  }
}

4. v1 → v2 이행 ​

원칙: 병행 제공 후 소비자 전환, 마지막에 v1 제거. 참조가 끊기면 소비자 매핑이 고아가 되므로 한 번에 바꾸지 않는다.

단계내용완료 조건
1식별자 기반 정비 — 유일성 제약은 기존재(작업 없음). 발급 규칙 문서화, 게시 후 code 변경 금지 서버 가드 배선가드 테스트 통과 + 중복 0건 유지
2v2 경로 구현 — /api/v2/process-nodes* + /api/v2/systems, 게시본 기준 노출, code 기반 payload, 소비자별 키(자체 관리)v2 테스트 통과, v1 무변경
3소비자 전환 — AgentCenter 를 v2 로 전환(shared/processPortal.ts 의 NodeKey·ProcessNode, 어댑터, 테스트)소비자 회귀 테스트 통과
4v1 정리 — externalNodeId(id) 경로 제거, 계약 문서·integration-contract 갱신✅ 완료(2026-09-20) — 라우트 제거, 공용 로직은 server/processNodeSource.ts, v1 테스트는 processNodeSource.test.ts 로 이관

이행 완료. 소비자(AgentCenter)는 v2 를 쓰고, v1 경로·payload 는 저장소에 없다.

전환 기간에는 v1 과 v2 응답이 서로 다른 식별자 체계를 갖는다(같은 노드가 id 로도 code 로도 표현됨). 이 기간에 소비자 매핑을 새로 만들지 않는다 — 만들면 두 체계가 섞인다.

소비자(AgentCenter) 측 변경 범위 ​

  • shared/processPortal.ts: NodeKey → { sourceSystemId, taxonomyCode, nodeCode }, ProcessNode 계약 필드 교체
  • server/adapters/process-portal/processCenter.ts: 경로 v2 + payload 매핑
  • 화면(client/src/pages/ProcessAgents.tsx)은 trpc.processNodes.* 계약을 유지하므로 렌더러 불변
  • 환경값: PROCESS_PORTAL_API_KEY → 소비자별 키로 교체(키 이름 확정 시 반영)

5. 확정된 결정 (2026-09-20) ​

#항목확정
1code 유일성 범위전역 유일 — 이미 적용됨(process_nodes_code_unique, 중복 0건 확인). 신규 마이그레이션 없음. taxonomyCode 는 분류·스코프 속성으로 유지(식별 필수 요소 아님)
2코드 체계·변경발급 규칙(<레벨>-<6자리 순번>) 문서화, 게시 후 code 변경은 서버가 거부, 예외는 새 코드 발급 + 승계 연결 — 배선 완료(2026-09-20): updateProcessNode 경계에서 거부, 기동 코드 정규화는 게시본 제외
3서비스 키 발급·순환P0 는 ProcessCenter 자체 관리(소비자별 키 + process:read, 데이터로 관리) → 후속 PortalControlCenter 카탈로그 연계
4승계successorNodeCodes 는 필드만 먼저 둔다. 값은 폐기·병합 승인 시 채우고, 소비자는 승계 추종/무효 표시 규칙을 따른다
5참조 추적·감사 보존구조화 요약만 기록. 보존 기간은 사내 정책 대기(임의 값 없음, 로그 스키마만 준비)
6systems 공급함께 공급 — GET /api/v2/systems(시스템·그룹, code 전역 유니크)
7업무 코드별칭 필드로 분리(확정 2026-09-20) — code 는 시스템 발급 순번(참조 정본), 사람이 읽는 업무 코드는 businessCode(표시용·전역 유일, 식별자 아님). D2 대상 아님

남은 외부 의존은 하나다 — 참조 추적 보존 기간(사내 정책). 그 전까지 추적 로그는 스키마만 두고 보존·삭제 정책을 코드에 넣지 않는다.

6. 검증 계획 (구현 시) ​

대상검증
식별자process_nodes_code_unique 기존재 확인 + 중복 0건(pnpm db:check-code-uniqueness, dev DB 실행 결과 19건·중복 0), resolve 가 code 로만 동작(id 미사용)
코드 불변게시본 보유 노드(publishedVersion != null)의 code 변경 시도가 거부됨
systems/api/v2/systems 가 시스템·그룹·code 를 주고, 노드 systemCode 와 조인 가능
게시본DRAFT/IN_REVIEW 노드가 목록·resolve·access 어디에도 나오지 않음
L1 인용organizationCode·directoryUserId·systemCode 가 값 복사 없이 참조로만 전달됨
변경 피드changedSince 증분 응답이 complete=false, 삭제 판정에 쓰이지 않음
승계폐기 노드 참조 시 successorNodeCodes 로 이어짐(규칙 확정 후)
인증소비자별 키·스코프, 미인증 401, 스코프 부족 403, 퍼블릭 비노출
접근 정책사용자별 VIEW/REVIEW 판정이 v1 과 동일 결과를 냄(회귀)
소비자AgentCenter v2 전환 후 프로세스 트리·매핑 화면 회귀 테스트 통과