새 기능을 붙인 뒤 이전 자료가 열리지 않는다면 최신 화면이 예뻐도 업무는 이어지기 어렵다. 나는 자료를 언제 어떻게 바꾸는지 보이지 않는 자동 처리로 남기고 싶지 않았다.

이번 코드는 고정 합성 v1 자료를 v2 제안으로 바꾼다. 실제 회사 DB를 변환하거나 복구 파일을 덮지 않으며, 앞서 만든 복구 검증과도 다른 변환 미리보기 책임을 다룬다.

이름을 바꿀 때 새 의미를 몰래 넣지 않는다

합성 v1 행은 id와 qty를 갖는다. v2 제안은 qty를 quantity로 옮기고 status를 draft로 추가한다. 실제 업체의 저장 구조를 가져온 것이 아니며 단순한 변환 규칙을 시험하는 예제다. 상태를 어떻게 기본 지정할지 정하지 않으면 같은 옛 자료가 서로 다른 새 의미로 읽힐 수 있다.

draft라는 기본값도 모든 실무 자료에 적용할 수 있는 정답은 아니다. 실제 변환은 기존 업무 상태의 의미를 확인하고 사용자가 판단할 항목을 남겨야 한다. 이번 예제는 매핑을 명시적으로 고정하고 왜 추가 필드가 생겼는지 설명한다. 변환 함수가 모르는 업무를 추측해서 채워 넣지는 않는다.

변환 전에 모든 행을 확인한다

문서 키, 스키마 번호, 행 배열과 각 행의 필드·식별자·수량을 먼저 검사한다. 알 수 없는 키나 지원하지 않는 버전은 거절한다. 중복 ID도 실패다. 첫 행이 맞아도 두 번째 행이 틀리면 전체 미리보기가 오류로 끝나며 앞부분만 바꾼 결과를 돌려주지 않는다.

실제 데이터베이스에 여러 행을 저장하는 원자성은 구현하지 않았다. 여기서는 입력을 수정하지 않는 순수 계산이므로 검사 실패 뒤 원본이 그대로임을 확인한다. 대량 자료를 일부씩 변환하는 서비스라면 실패 위치, 재개 기준과 이미 반영한 범위를 별도로 관리해야 한다. 작은 배열의 전체 실패와 운영 변환 작업을 혼동하지 않는다.

제안과 적용을 다른 사실로 남긴다

migration_preview는 from_schema, to_schema, changed, proposed와 applied를 반환한다. applied는 항상 거짓이다. 새 구조가 계산됐다는 것과 실제 서비스에 반영됐다는 것을 구별한다. 원본을 저장할 위치나 실제 복구본은 이 함수가 선택하지 않는다. 자료가 바뀌었다는 오해를 결과 형식에서 줄이려는 설계다.

실제 적용에는 미리보기 확인, 쓰기 권한, 현재 버전 재확인과 원본 보관이 필요하다. 변환 직전 다른 직원이 수정했다면 오래된 제안을 그대로 적용해서는 안 된다. 이번 패키지는 그 적용 거래를 수행하지 않으므로 실제 변경 이력도 만들지 않는다. 어떤 단계가 빠져 있는지 숨기지 않고 연결할 책임으로 적었다.

이미 새 버전인 자료는 다시 바꾸지 않는다

v2를 받으면 같은 규칙으로 검증하고 복사본을 반환한다. changed는 거짓이며 다시 변환해도 제안 내용은 같다. 반복 실행이 필드를 중복 추가하거나 수량을 다시 계산하는 문제를 막는 작은 검사다. 테스트는 v1 제안을 다시 넣어 결과의 동등성과 원본 v1 보존을 함께 확인한다.

이 동등성만으로 되돌리기가 가능해지는 것은 아니다. v2 상태를 v1이 표현할 수 없는 경우도 생길 수 있다. 이번 모듈에는 역변환이 없고 이전 코드와 새 자료의 호환성은 다른 함수로 판단한다. 변환이 반복 가능하다는 성질과 손실 없는 복구 가능성을 같은 말로 쓰지 않았다.

적용 전에 호환성까지 같이 본다

테스트는 정상 매핑뿐 아니라 잘못된 두 번째 행, 알 수 없는 버전, 허용하지 않은 필드와 반복 미리보기를 확인했다. 다른 모듈과 연결한 검사에서는 새 제안의 schema 2를 옛 코드가 읽지 못한다는 결과도 남겼다. 변환 결과만 맞고 실행할 프로그램이 못 읽는 상태를 놓치지 않기 위해서다.

공유한 것은 합성 코드와 로컬 증거다. 실제 운영 자료를 변환하거나 롤백한 결과는 아니다. 실무에 붙일 때는 원본 보존과 실제 복구 연습, 코드 배포 순서와 적용 권한까지 확인해야 한다. 나는 자료 형식의 변경을 화면 기능 뒤에 숨기지 않고 검토할 제안으로 꺼내 놓는 방식을 공개했다.