쓰던 표를 그대로 가져오면 새 ERP를 바로 쓸 수 있을까. 나는 먼저 그 표의 한 행이 무엇을 뜻하는지, 같은 번호가 다시 나왔을 때 어떤 자료를 남길지 확인해야 한다고 본다.

공개 체험에서는 실제 업체 파일을 받지 않고 고정된 합성 표를 사용했다. 이번에는 그 검토 장면의 이유를 더 깊게 설명하고, 검토와 반영 후보 생성을 나눈 독립 모듈을 함께 만들었다.

파일을 읽었다는 결과와 업무를 이해했다는 결과

파일이 열린다고 이관이 끝난 것은 아니다. 품목 열을 수량으로 잘못 연결해도 프로그램은 문자열을 읽을 수 있다. 거래 번호를 숫자로 바꾸면 앞의 0이 사라질 수 있고, 빈 수량을 0으로 채우면 아직 확인하지 않은 업무가 실제 0개인 주문처럼 보인다. 나는 파서가 성공했다는 결과 뒤에 업무 의미를 확인하는 자리를 두고 싶었다.

현재 체험의 수량 열 선택은 그 차이를 설명하기 위한 작은 장면이다. 미리 만든 네 행을 읽고 올바른 열인지, 식별자가 겹치는지, 수량이 비었는지 보여 준다. 실제 파일 업로드나 엑셀 전체를 해석하는 기능은 없다. 이 한계를 적어야 방문자가 시연의 간결함을 실제 회사 자료의 이관 난이도로 오해하지 않는다.

한 행을 무엇으로 구분할지 먼저 정했다

공개 예시에는 같은 S01이 두 번 나타난다. 현재 체험은 뒤에 나온 같은 번호를 중복으로 표시하고 정상 후보 두 행을 보여 준다. 이 표시만으로 실무의 중복 정책을 확정한 것은 아니다. 두 행이 같은 주문의 다른 품목인지, 다른 지점의 같은 번호인지, 다시 가져온 같은 자료인지를 정해야 삭제나 병합을 판단할 수 있다.

새 모듈에서는 이 판단을 대신하지 않고 같은 식별자의 모든 등장 행을 검토 대상으로 남겼다. 한 행을 조용히 골라 반영하지 않는다. 식별자는 문자열로 보존하므로 001은 1로 바뀌지 않는다. 회사별로 지점·문서 번호·행 번호를 합쳐야 한다면 그 조합 규칙을 먼저 정한 뒤 모듈에 전달할 식별자를 만들 수 있다.

코드와 구현 살펴보기

현재 체험의 고정 표 검토 함수

export function exampleImport(mapping='quantity'){
  const rows=[{id:'S01',item:'A',quantity:'6'},{id:'S01',item:'A',quantity:'6'},{id:'S02',item:'B',quantity:''},{id:'S03',item:'B',quantity:'4'}];
  const seen=new Set();return rows.map(row=>{let status='ready';if(mapping!=='quantity')status='mapping';else if(seen.has(row.id))status='duplicate';else if(!/^[1-9]\d?$/.test(row.quantity))status='missing';seen.add(row.id);return {...row,status};});
}

정상 후보가 보인다고 전체 반영을 허용하지 않았다

체험에서 후보라는 말은 검토할 수 있는 행이라는 뜻이며 서버 저장의 완료를 뜻하지 않는다. 이번 독립 모듈도 정상화 가능한 행을 따로 보여 주지만 한 행이라도 중복·누락·잘못된 열 연결이 있으면 전체 반영 후보는 내보내지 않는다. 일부만 먼저 들어가서 나머지의 재시도와 섞이는 일을 작은 예제 안에서 피하기 위한 선택이다.

이 방식이 모든 이관에 맞는 것은 아니다. 큰 자료라면 검토 단위를 나누고 각 묶음의 식별자와 처리 이력을 남기는 편이 나을 수 있다. 일부 항목만 반영할 수 있는지와 그 처리 방식을 미리 정해야 한다. 어느 범위가 함께 성공해야 하는지, 실패한 행은 어디에 남는지, 재시도 때 이미 들어간 자료를 어떻게 찾을지를 명시해야 한다.

검토한 뒤 자료가 바뀌었다면 다시 확인한다

검토 화면을 열어 놓은 사이 다른 작업자가 같은 번호를 추가할 수도 있다. 새 모듈은 예상 수정 번호와 현재 수정 번호가 다르면 반영 후보 생성을 거절한다. 검토 당시의 정상 표시를 그대로 믿지 않고 반영 직전의 입력과 기존 식별자 목록으로 검사를 다시 수행한다. 수량을 검토 뒤에 빈칸으로 바꾸면 이전 통과 결과가 새 입력에 재사용되지 않는다.

여기서 반환하는 것은 메모리의 새 배열이다. 실제 데이터베이스에 대한 잠금이나 원자적 저장은 구현하지 않았다. 운영 서버라면 현재 번호 확인과 쓰기가 같은 트랜잭션 또는 조건부 쓰기 경계 안에서 이루어져야 한다. 순수 함수의 번호 비교만으로 동시 수정 문제가 해결됐다고 설명하지 않는 것이 제작자로서 지켜야 할 구분이다.

정리 규칙이 원본을 지워 버리지 않도록

모듈은 원본 행을 직접 고치지 않고 식별자·품목·정수 수량만 가진 후보를 만든다. 수량은 1~99의 문자열을 받아 정수로 바꾸며 소수·지수 표기·앞뒤 공백을 임의로 해석하지 않는다. 이 범위는 공유 예제의 계약이다. 실제 회사에서 소수 단위나 다른 품목 코드가 필요하면 별도 규칙을 정해 확장해야 한다.

원본 보관과 검토 결과도 구별한다. 회사 자료를 실제로 이관한다면 원본 파일은 보호된 위치에 보존하고 어느 버전의 연결 규칙으로 어떤 행을 처리했는지 남기는 편이 좋다. 원본 전체를 공개 블로그에 올려야 깊은 설명이 되는 것은 아니다. 나는 새로운 합성 표와 오류 사례로 판단 과정을 공개하고 실제 업체 정보는 사용하지 않았다.

다른 회사에 적용할 때 먼저 묻는 질문

빈칸과 0의 의미, 거래 식별자의 범위, 수량의 단위, 기존 자료와 겹칠 때의 정책을 먼저 확인한다. 그런 다음 자료를 읽는 단계, 검토하는 단계, 반영을 제안하는 단계, 실제 저장을 분리해 본다. 각 단계의 실패가 다음 단계의 성공으로 표시되지 않는지 확인하면 복잡한 이관도 설명할 수 있는 작은 경계들로 나뉜다.

공유 모듈에는 정상·누락·중복·잘못된 열·기존 식별자 충돌·검토 뒤 수정 번호 충돌을 바꾸어 보는 한영 데모가 있다. 추출한 패키지의 테스트와 이벤트 연결을 코드로 확인했다. 실제 회사 파일의 변환·성능·운영 이관이나 브라우저 화면 검수를 완료했다는 뜻은 아니다. 독자가 자기 회사의 규칙을 더할 위치까지 읽을 수 있도록 범위를 남겼다.