실패한 자동 저장을 생성 버튼이 건너뛸 수 있다

이미지 생성이 서버에 저장된 디자인 지시문을 읽는다면, 입력란에 보이는 값이 저장됐는지부터 확인해야 한다. 서버 값 A를 B로 고친 뒤 자동 저장이 실패했는데 화면이 B를 “마지막으로 보낸 값”으로 기억하면, 추가 편집 없이 생성할 때 B를 다시 저장하지 않을 수 있다. 화면의 B와 실제 생성에 사용하는 A가 갈라지는 경로다.

지시문 편집 다이얼로그에서 저장 Promise가 끝난 뒤 수정한 값을 재생성 핸들러에 직접 넘겨, 이전 서버 값으로 생성하던 경로를 고쳤다. 이후 자동 저장 직렬화·서버 값 비교·페이지 교체 보호를 추가로 보완했다.

AI Commons의 자동 저장 수정과 후속 변경은 생성 전에 지시문 저장을 끝내도록 했다. 제품 통합 전의 기능 구현이다. 자동 저장은 같은 시도 값을 반복 전송하지 않도록 마지막 시도 값을 기억하지만, 명시적인 생성은 현재 서버 값과 다른 편집을 다시 저장한다. 자동 저장 실패가 끝없는 재시도로 이어지지 않게 하는 조건과 생성에 사용할 입력을 확정하는 조건이 다르다.

  1. A → B 편집 후 자동 저장 실패화면에는 B가 남고 서버에는 A가 남는다.
  2. 추가 편집 없이 생성 클릭예약 타이머를 지우고 B가 현재 서버 값과 다른지 비교해 저장을 다시 요청한다.
  3. 저장 성공 후 생성재저장도 실패하면 생성 호출로 넘어가지 않는다.

현재 서버 값을 비교 기준으로 쓴다

캐시에 들어온 현재 page.visualInstruction을 기준으로, 로컬 편집을 병합한 지시문과 서버의 편집 가능 섹션을 같은 규칙으로 재조립한 지시문을 비교한다. 편집 중 서버 값이 Y로 바뀌었다면 옛 기준 A가 아니라 Y와 비교한다.

재조립은 NARRATIVE를 보존하고 편집 가능한 VISUAL·LAYOUT을 반영한다. 양쪽을 같은 함수로 정규화하므로 레거시 KEY CONTENT 제거, 헤더 없는 입력의 형태 차이만으로 불필요한 저장을 만들지 않는다. 값이 A→B→A로 돌아왔더라도 실제 서버가 Y라면 A는 저장해야 할 편집이다. 반대로 현재 서버와 같고 진행 중인 저장도 없으면 편집 중 상태를 내려 이후 서버 변경을 따라간다.

오래 걸린 B 저장이 C를 덮지 않게 줄을 세운다

B 저장이 진행 중일 때 C를 입력하면 두 요청을 동시에 보내지 않는다. 새 저장은 앞선 Promise 뒤에서 실행한다. 앞선 요청의 실패는 다음 요청을 시작할 수 있도록만 흡수하며, 새 요청의 실패는 그대로 거절한다. 아래는 실제 저장 연결의 핵심이다.

const previous = pendingInstructionSaveRef.current;
const promise = previous
  ? previous.catch(() => {}).then(send)
  : send();

생성 직전에는 편집 값이 현재 서버와 다르면 즉시 저장을 줄에 추가하고, 그렇지 않으면 이미 진행 중인 저장을 기다린다. 기다린 저장이 실패하면 false를 반환해 생성 핸들러를 종료한다. 오류 화면의 재시도 버튼도 같은 관문을 쓴다. 다음은 페이지 식별과 길이 검사를 생략한 의미상의 축약이다.

const pending = edited && differsFromServer(value)
  ? saveInstructionNow(value)
  : pendingSave;
try { await pending; return true; }
catch { return false; }

이 순서는 생성에 저장 지연을 더하고, 저장소가 실패하면 이미지 생성도 막는다. 여러 편집이 연달아 오면 불필요한 중간 저장이 줄에 남을 수도 있다. 대신 마지막 전송 성공 여부가 확인되기 전에 이전 입력으로 생성하는 경로를 차단한다. 직렬화는 이 카드의 저장 순서를 다루며 다른 클라이언트의 저장까지 잠그지는 않는다.

저장 Promise도 어느 페이지의 것인지 알아야 한다

지시문 저장에 함께 적용하는 입력 경계
경계적용 방식
페이지전송 시 id와 index를 캡처한다. 응답 때 같은 페이지인지 확인한 뒤 입력을 정규화한다.
언어추출·비교·병합·성공 후 정규화에 같은 slideLanguage를 쓴다.
길이입력란만이 아니라 NARRATIVE까지 합친 payload가 10000자를 넘으면 저장·생성을 막는다.

카드가 다른 페이지로 바뀌면 대기 저장 참조를 초기화해 새 페이지 생성이 옛 페이지 저장을 기다리지 않게 한다. 옛 요청의 성공이 새 페이지 입력란을 옛 값으로 정규화하지도 않는다. 참조 초기화가 이미 보낸 네트워크 요청의 취소는 아니다. 길이가 상한을 넘으면 자르지 않고 줄이도록 안내한다. 화면에서 보지 못한 NARRATIVE를 잘라 생성 의미를 바꾸는 것을 피할 수 있다.

저장 실패·응답 순서·페이지 교체를 따로 주입한다

UI 테스트는 가짜 타이머와 제어 가능한 저장 Promise로 실패 후 생성의 재저장, 반복 실패 시 생성 차단, B 저장 중 C 편집의 직렬 전송, 첫 실패 후 다음 저장 진행을 확인한다. 현재 서버 값 변경과 레거시 정규화 비교, 언어별 헤더, 페이지 교체 후 옛 응답의 격리, 병합 payload의 10000자 경계도 다룬다. 실제 브라우저의 통신 지연을 측정한 결과는 아니다.

현재 서버 값은 브라우저가 관찰한 스냅샷이다. 저장 완료와 백엔드의 생성 입력 읽기 사이에 다른 사용자가 수정할 여지는 남는다. 서버의 편집·완료 저장 경합은 잠금과 결과 저장 조건으로 따로 다뤄야 한다. AI Commons의 이 UI 관문은 화면이 시도한 편집을 저장 확인 없이 생성 입력으로 취급하지 않도록 하는 범위다.