[PLM 마이그레이션 문제해결기 #3] 위로만 번지는 번호 — EO 채번 자동화와 조상 체인 전파

제조 PLM 시스템을 Next.js로 마이그레이션하면서 겪은 문제 해결 과정을 기록하는 시리즈입니다. 세 번째 편은 설계 배포 시 EO(Engineering Order) 번호를 자동으로 채번하고, 그 번호를 변경의 영향 범위를 따라 “위로만” 전파하는 로직 이야기입니다.

배경

EO는 설계 변경 지시 번호입니다. 도면과 부품이 어떤 변경 차수에 속하는지를 이 번호로 식별하고, 제조·구매 부서는 이 번호를 보고 “어느 개정판 기준으로 만들 것인가”를 판단합니다. 기존 레거시에서는 이 채번이 수작업에 가까웠고, 그만큼 누락과 중복의 여지가 있었습니다. 마이그레이션하면서 설계 배포 시점에 시스템이 자동으로 채번하는 구조로 바꾸기로 했습니다.

규칙: 초도 배포와 설변 재배포는 다르다

채번 규칙 자체는 두 문장으로 정리됩니다.

  • 초도 배포: 배포 단위로 EO를 1개 채번하고, 배포에 포함된 전 부품에 동일하게 할당한다.
  • 설변 재배포: 변경된 부품에 새 EO를 채번하고, 그 새 EO를 변경 부품의 상위 ASSY부터 루트까지의 조상 체인에만 전파한다. 형제 노드와 하위 노드는 기존 EO를 유지한다.

구현보다 이 두 문장을 확정하는 데 더 오래 걸렸습니다. 그리고 이 문장이 확정되고 나니 구현과 테스트의 기준이 전부 이 문장에서 나왔습니다.

왜 “위로만” 번지는가

부품 하나가 바뀌면 그 부품이 들어가는 상위 조립도(ASSY)도 개정 대상입니다. 조립도에는 그 부품이 그려져 있으니까요. 그 ASSY를 포함하는 유닛 도면도 마찬가지라서, 영향은 루트까지 이어집니다. 반대로 변경 부품의 하위 부품들은 아무것도 변하지 않았고, 형제 노드도 무관합니다. 즉 설계 변경의 영향 범위는 정확히 “조상 체인”이고, EO 전파 범위도 그와 일치해야 합니다. 넓게 번지면 바뀌지 않은 도면까지 개정판이 되고, 좁게 번지면 바뀐 조립도가 구판 번호로 남습니다.

구현: 그래프를 메모리에 올려 역방향 탐색

트리 “조회”는 #2편에서 다룬 대로 재귀 CTE로 충분합니다. 하지만 채번은 사정이 다릅니다. 한 번의 설변 배포에 변경 부품이 여러 개 들어오고, 각 부품마다 조상 체인을 오르며 갱신하되 체인이 겹치는 구간은 한 번만 갱신해야 하고, 이 전체가 한 트랜잭션 안에서 끝나야 합니다. 부품마다 DB를 왕복하며 CTE를 태우는 대신, 배포 대상 BOM 행 그래프를 통째로 메모리에 올려 자식 → 부모 역방향 탐색으로 구현했습니다.

const rowsById = new Map(rows.map(r => [r.id, r]));
const visited = new Set();

for (const changed of changedRows) {
  let cur = rowsById.get(changed.parentRowId);  // 상위 ASSY부터
  while (cur && !visited.has(cur.id)) {
    assignEo(cur, newEo);
    visited.add(cur.id);
    cur = rowsById.get(cur.parentRowId);        // 루트까지
  }
}

여기서 #2편의 규약 — 부모 참조는 부모 행의 행 ID — 가 그대로 효자 노릇을 합니다. 역방향 탐색이 Map 조회 한 번씩으로 끝나고, “어느 위치의 그 부품인지” 같은 모호함도 없습니다. visited 집합은 두 변경 부품이 같은 조상을 공유할 때의 중복 갱신을 막아줍니다.

검증: 운영 DB에서의 트랜잭션 드라이런

채번은 성격상 되돌리기 부담스러운 갱신입니다. EO 번호는 도면에 찍혀 외부로 나가는 값이라, 잘못 부여된 채로 하루가 지나면 교정 비용이 커집니다. 그리고 이런 로직의 진짜 엣지 케이스는 스테이징의 깨끗한 데이터가 아니라 운영 데이터의 분포에서 나옵니다.

그래서 반영 전에 운영 DB에서 트랜잭션 드라이런을 돌렸습니다.

  1. BEGIN — 트랜잭션을 연다.
  2. 채번 로직을 실제로 실행한다.
  3. 결과를 집계로 검증한다 — EO별 부여 부품 수, 체인 길이 분포, 기존 EO가 유지돼야 할 노드가 바뀌지 않았는지.
  4. ROLLBACK — 전부 되돌린다.

집계 리포트를 확인한 뒤에야 같은 로직을 커밋 모드로 반영했습니다. BEGIN → 검증 → ROLLBACK은 별도 시뮬레이터를 만드는 것보다 훨씬 싸고, 운영 데이터 그대로를 상대한다는 점에서 더 정직한 리허설이었습니다.

교훈

  1. 도메인 규칙은 코드보다 먼저 문장으로 확정하라. “새 EO는 조상 체인에만 전파된다”는 한 문장이 구현, 테스트 케이스, 검증 집계의 공통 기준이 됐다.
  2. 트리 알고리즘의 비용은 자료구조 규약에서 이미 결정된다. 부모 참조가 행 ID로 깔끔했기 때문에 역방향 탐색이 단순해졌다. #2편의 규약이 없었다면 이 편의 구현은 몇 배 지저분했을 것이다.
  3. 되돌리기 어려운 배치 갱신은 운영 DB 트랜잭션 드라이런으로 리허설하라. BEGIN/ROLLBACK은 가장 싼 시뮬레이터다.

덧붙여

이 프로젝트는 AI 코딩 에이전트(Claude Code)와 페어로 진행하고 있습니다. 그래프 탐색 구현과 드라이런 집계 리포트는 에이전트가 만들었고, “조상 체인에만 전파한다”는 규칙의 확정과 드라이런 결과가 도메인적으로 타당한지(이 도면에 이 EO가 맞는가)의 판단은 사람이 했습니다. 실험을 돌리는 손이 빨라질수록, 무엇이 맞는 결과인지 정의하는 일이 병목이자 본질이 된다는 걸 다시 확인했습니다.

프로젝트 전체 개요와 다른 문제 해결기는 GitHub 저장소에 정리되어 있습니다.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top