"payload 는 제출 누른 순간 얼어. 그 뒤로 세상이 건드릴 수 있는 건 상태 플래그 하나뿐이야."
한 턴 안의 두 종류 상태
캡처된 턴엔 아주 다른 두 종류의 상태가 있고, 그 둘을 분리하는 게 끊김을 무해하게 만들어:
- payload — 질문 텍스트랑 이미지 blob. 캡처 순간부터 불변이야. 네트워크 이벤트, 앱 죽음, 재시도 뭐도 이걸 다시 쓰지 않아.
- sync 상태 —
pending → sending → answered | failed. 이게 유일한 가변 부분이고, 모든 전이가 내구성 있는 쓰기야.
턴을 이렇게 보면, '전송 도중 앱이 죽으면 어떻게 돼?' 는 지루하고 올바른 답을 가져: payload 는 손 안 탐(얼었고 내구성 있었으니), 상태는 여전히 sending 이거나 이미 failed 로 뒤집힘. 어느 쪽이든, 다음 실행이 뭘 할지 정확히 알아. 끊김은 데이터 손실 이벤트가 아냐. 상태 전이 이벤트야.
왜 payload 의 불변성이 중요한가
끊김이 payload 를 바꿀 수 있으면, 모든 실패가 잠재적 손상이야. 질문을 다시 인코딩하는 재시도, 반쯤 쓰인 blob 을 다시 읽는 백그라운드 sync, 두 필드 업데이트 사이의 크래시 — 각각이 사용자의 실제 말을 동의 없이 바꾸는 통로가 돼. payload 를 캡처 때 얼리고 상태만 전이시키면, 거대한 실패 모드 공간을 하나의 작고 잘 이해된 상태 기계로 접어버려. 무서운 질문 '그 크래시 후에 내 데이터 아직 맞아?' 가 '어느 상태에 있어?' 가 돼 — 답이 넷뿐이고, 다 안전한 질문.
상태 기계가 곧 계약
상태 전이를 명시적으로 설계하면 견고함이 따라와. payload 를 삭제하는 전이는 없어. '잃음'을 뜻하는 상태도 없어. 최악의 상태는 failed 인데, 그것도 여전히 턴 전체를 쥐고 그냥 재시도를 청해. 그게 보이게 된 약속이야: 끊긴 턴은 절대 사라지지 않아 — 그냥 작고 안전한 지도 어딘가에서 기다릴 뿐이야.