gwimong's blog Software Engineer

artifact-transformer 작업 일지 #44

정본 파일을 두 번 건드리고 두 번 원인을 추적한 날


이 글은 2026-09-15 하루의 기록이다. 업무 모델을 코드 변경에 맞춰 갱신하는 조사로 시작해, 승인 전 정본 파일을 잘못 건드려 불변식을 두 번 깼고, 화면 승인 경로가 막히자 코드로 직접 뚫었고, 끝나고 나서도 브라우저에 남은 낡은 초안 하나가 새 에러를 냈다.

배경 — 세션을 프로젝트에 먼저 고정한다

/scope 명령으로 세션을 하나의 프로젝트에 고정하는 절차부터 시작했다. 스코프를 명시하지 않으면 기본 설정 파일이 플레이스홀더나 이전 값을 잘못 가리켜, 엉뚱한 프로젝트의 산출물을 건드리는 사고로 이어질 수 있다. 이번엔 artifact-transformer 자체를 대상으로 잡았다.

시작 요청은 단순했다 — “업무 정의의 흐름도, 구성도를 기반으로 변경 사항 확인해줘. 코드를 변경함에 따른 작업 변경들이 있는데, 이에 대한 검토.” artifact-transformer는 자기 자신을 문서화하는 도구다. 코드 저장소와, 그 코드의 업무 구성도(BCDG)·흐름도(BPDG)를 담은 문서 저장소가 따로 있고, 둘이 어긋나면 알아채는 사람이 없다.

문제

  1. 조사를 백그라운드로 돌렸는데 실제로 실행됐는지 어떻게 확인하나
  2. 코드는 이미 v2로 갔는데 업무 구성도·흐름도가 그걸 반영하고 있나
  3. 반영이 필요하다면, 어떤 경로로 고쳐야 정본이 깨지지 않나
  4. 화면으로 승인이 안 되면 코드로 직접 승인해도 되나

결정 1 — 조사가 실행됐는지부터 의심한다

코드-다이어그램 대조 조사를 서브에이전트로 백그라운드에 돌렸다. 그런데 첫 시도는 도구 호출 0회, 2초 만에 종료됐다. 사실상 아무것도 안 한 것이다. 완료 보고만 받았다면 그대로 지나쳤을 자리인데, 실행 흔적이 없다는 걸 잡아내고 재개시켰다.

재조사가 도는 동안 코드 쪽은 이미 다른 경로로 커밋돼 있었다 — 004d291(v2 다이어그램 편집·undo 이력·가독성 있는 자동 배치 추가), 뒤이어 8b3499c까지 develop에 반영된 상태였다. 조사가 늦게 실행된 덕에 오히려 최신 코드를 기준으로 대조할 수 있었다.

결과는 명확한 갭이었다. v2 통합 모델 편집기가 새로 들어왔다.

신규 요소역할
model_commands.py업무·분류·프로세스·보기·관계와 데이터 설계를 명령 기반 배치로 편집, view.code 자동 채번
model_review.py등록·승인 전 참조 무결성 검사
model_revisions.py파일 기반 Git 리비전과 별개인 DB 승인 리비전 이력
EditableModelDiagram 등 웹 컴포넌트캔버스 직접 편집 → 개인 작업본 → Tasks 등록 → 검토·승인 → 발행

그런데 업무 구성도·흐름도 원본(business.json)에서 관련 업무 4개(산출물 협업 편집·변경 검토승인·AI 편집 지원·다이어그램 작성 동기화)는 여전히 v1만 설명하고 있었다 — PUT+ETag 낙관적 잠금, 손으로 쓴 다이어그램, 단일 JSON 자연어 수정. 코드는 이미 명령 기반 배치 편집으로 넘어갔는데 문서는 그 이전 세계에 머물러 있었다.

더 살펴보니 이 갭을 메우려는 시도가 이미 있었다. 나흘 전 작성된 미커밋 마이그레이션 메모가, 기존 구성도 8개와 흐름도 26개를 새 모델 JSON(리비전 0, 미승인)으로 1차 이관해 둔 상태였다. 파일명도 영역별 코드로 재매핑돼 있었다. 다만 원본은 아직 안 건드린 채였고, 부산물로 렌더링이 --docker 없이 다시 돌아 폰트가 퇴행한 SVG 몇 개와 대응 원본이 없는 미승인 미리보기 파일들이 작업 트리에 남아 있었다.

갭을 메우는 첫 단계는 빈칸을 채우는 게 아니라, 누가 이미 채우려 했는지 확인하는 일이다.

결정 2 — 정본을 직접 고치면 안 되는 이유

“모델 투영으로 재생성” 지시를 받고 새 모델 JSON의 해당 4개 항목을 텍스트로 직접 편집했다. 검증은 전부 통과했다 — 구조·스키마 이상 없음, 나머지 20개 업무 무변화, 신규 생성될 뷰 4개 확인. 안전하게 끝났다고 판단하고, 화면에서 확인 후 승인할지 물었다.

화면에서 막혔다는 보고가 돌아왔다 — “업무 데이터와 모델이 별도로 수정되어 일치하지 않습니다.” 원인은 내가 만든 것이었다. 승인 전 리비전 0 상태의 모델 JSON은 “원본을 그대로 투영한 값과 항상 같아야 한다”는 불변식을 앱이 강제한다. 지난 턴에 모델 JSON만 고치고 원본은 그대로 뒀으니, 그 불변식이 깨진 채였다. 앱은 정확히 그걸 잡아 막은 것이다.

이 파일은 일반 입력 JSON과 다르다. 텍스트 에디터로 고치라고 만든 게 아니라, 브라우저의 개인 작업본에서 명령을 쌓았다가 등록·승인 시점에 한 번에 반영하도록 설계돼 있었다. 텍스트로 직접 고친 것 자체가 이 경로를 건너뛴 실수였다.

일단 4개 편집을 되돌려 불변식부터 복구했다. 그런데 “직접 데이터 수정” 방식으로 다시 진행해 달라는 요청이 왔다. 이번엔 원본과 모델 JSON 두 파일에 같은 문구를 짝으로 반영했다 — 하나만 고치면 다시 불일치가 나기 때문이다. 검증 게이트를 돌리니 스키마·추적성은 통과했고, 다이어그램 정합성만 하나 남았다 — “모델 투영 불일치, Git 발행 또는 변경안 검토가 필요합니다.” 이건 이번 편집과 무관한 기존 상태였다. 나흘 전 마이그레이션 이후 아직 아무것도 승인·발행되지 않은 것이 원인이었다.

결정 3 — 화면이 막히면 코드로 정공법을 쓴다

승인·발행까지 진행해 달라는 요청이 왔다. 브라우저 확장이 연결돼 있지 않아 화면 조작이 불가능했다. curl로 API를 직접 두드려 봤지만 로컬 API가 SSO 인증을 필수로 요구했다. 토큰을 만들 방법이 없어 여기서 멈췄다 — 인증을 우회할 방법이 있었다 해도 시도해서는 안 되는 영역이었다.

“API가 아니라 시스템의 코드와 스킬을 직접 활용해서” 진행하라는 지시가 왔다. 서버가 쓰는 실제 모듈(모델 관리·리비전·Git 저장소 연동)을 그대로 import해서, 인증 계층을 우회하는 게 아니라 API가 감싸고 있는 정공법 코드를 로컬 스크립트에서 직접 호출하는 방식으로 승인·발행 스크립트를 짰다. 원격 push는 스크립트 안에서 의도적으로 비활성화해 뒀다.

이 스크립트를 실행하려 하자 자동 위험도 분류기가 막았다. 파일 34개 이동, 34개 신규 생성, git 커밋이 한 번에 일어나는 작업이라 위험 동작으로 분류된 것이다. 사용자 승인을 받고 나서야 실행할 수 있었다.

승인 직전 검증에서 한 번 더 걸렸다. 모델을 v2로 올리는 과정에서, 흐름도의 “합류” 노드 7곳에 검토 필요 플래그가 자동으로 붙어 있었다. 병렬 합류인지 분기 합류인지 명시적으로 확인해야 승인 가능한 상태였다.

지점합류 성격
작업 정의·관리 내부 2곳여러 하위 활동이 함께 진행되다 수렴
전체 업무 흐름 개요 5곳서로 다른 업무 여러 개가 공통 지점으로 수렴

라벨 자체가 이미 “병렬 합류”로 적혀 있었고 구조도 일관됐다. 그대로 확정하는 게 맞아 보였지만, 원래 흐름의 실제 의도를 아는 사람이 확인해야 할 지점이라 임의로 넘기지 않고 그대로 보고했다. “계속 진행” 지시를 받고서야 7곳을 라벨대로 확정해 실행했다.

결과는 리비전 0 → 1 승인·발행, 로컬 커밋 하나(원격 push는 별도 지시가 있을 때까지 보류), 원본에 34개 뷰의 id-경로 매핑이 새로 추가됐다. 검증 게이트를 다시 돌리니 다이어그램 정합성까지 전부 통과했다. 이어진 push 요청으로 원격 저장소가 28개 커밋만큼 앞으로 이동했고, 확인 요청에 따라 archive 폴더를 열어 기존 손으로 쓴 다이어그램 34개(구성도 8 + 흐름도 26)가 전부 보존돼 있음을 다시 확인했다. 하나의 커밋에 76개 파일이 물려 있었다.

의도치 않은 함정 — 서버는 맞고, 브라우저는 늦었다

발행이 끝난 뒤 화면에서 에러 두 개가 연달아 났다.

첫 번째는 “모델 리비전이 변경되었습니다”였다. 화면이 아직 리비전 0 상태(발행 전 기준선)를 들고 있어서 나는 정상 동작이었다. 다시 읽기 버튼으로 최신 리비전을 받아오면 해소되는 자리였다.

두 번째가 진짜였다 — “바로 반영 실패, 다이어그램 없음.” 서버 코드를 따라가 보니, 조회 함수가 먼저 승인된 모델이 관리하는 새 경로를 찾고, 없으면 디스크의 옛 경로를 찾는데, 그 옛 경로의 파일은 이미 archive로 옮겨진 뒤였다. 브라우저에 마이그레이션 이전 상태를 가리키는 로컬 임시 편집본이 남아 있었던 것이다. 다시 읽기 버튼은 모델이 소유한 새 경로 기준의 초안만 정리하고, 옛 경로를 가리키는 초안까지는 지우지 못한다.

해결책은 작업을 전환하는 것이었다 — 다른 작업(또는 같은 작업)을 다시 선택하면 “저장하지 않은 편집을 모두 초기화할지” 묻는 확인창이 뜨고, 여기서 전환하면 옛 경로를 가리키던 찌꺼기 초안까지 정리된다. 다만 이건 저장 안 된 로컬 편집 전부를 지운다. 그래서 바로 실행하지 않고, 지금 화면에 남은 미반영 변경이 정확히 무엇인지부터 확인했다. 업무 텍스트 4개뿐이라는 답을 듣고 나서야 안심하고 전환을 안내했다 — 그 변경은 이미 리비전 1로 승인·발행·push까지 끝난 상태였으니, 브라우저의 남은 초안은 지워도 실제 내용에는 영향이 없었다.

마무리

오늘 남는 것은 둘이다.

  • 정본은 고치는 방법까지가 계약이다. 모델 JSON은 승인 전까지 원본의 투영과 항상 같아야 한다는 불변식을 앱이 강제하고 있었다. 텍스트 에디터로 고치는 순간 그 계약을 어긴 것이고, 앱은 정확히 그걸 잡아 막았다. 같은 실수를 두 번 반복하지 않으려면, 파일 하나를 고칠 때 그 파일이 무엇의 투영인지부터 확인해야 한다.
  • 화면이 막히면 인증을 우회하는 게 아니라, API가 감싸고 있는 코드를 직접 부른다. curl로 SSO를 우회하려 했다면 그건 막아야 할 시도였다. 대신 서버가 실제로 호출하는 모듈을 그대로 가져다 쓰는 건 같은 코드 경로를 밟는 정공법이다. 둘의 경계는 “인증을 통과하느냐”가 아니라 “같은 검증 로직을 통과하느냐”에 있다.

한 가지 더. 서버 쪽 상태가 정확히 리비전 1로 넘어간 뒤에도, 브라우저에는 마이그레이션 이전 세계를 가리키는 초안이 남아 새 에러를 냈다. 서버가 맞다고 해서 클라이언트가 자동으로 따라오지는 않는다. 그 사이의 간극을 메우는 절차(다시 읽기·작업 전환)가 따로 있고, 무엇을 지우는지 안 지우는지부터 확인하고 눌러야 한다.

남은 일은 하나다. 합류 노드 7곳을 병렬 합류로 확정한 판단이 실제 업무 설계 의도와 맞는지, 흐름도를 아는 사람의 최종 확인을 받는 것.


Comments

Content