저장 버튼을 누른 뒤 문장을 더 고쳤는데 첫 저장의 응답이 늦게 돌아온다면 어떻게 해야 할까. 최신 입력이 사라지는 문제는 서버의 저장 성공 여부와 별개로 화면에서 만들 수도 있다.

이번에는 기존 체험에 자동 저장이 있었다고 쓰지 않고 새 독립 상태 모듈을 만들었다. 실제 통신 없이 요청 순서와 입력 버전을 직접 바꿔 확인했다.

초안과 저장 확인을 하나의 값으로 두지 않았다

edit는 초안과 입력 revision을 바꾸고 begin_save는 그때의 내용과 revision을 요청에 복사한다. 응답이 돌아와도 초안에 값을 대입하지 않는다. 응답이 확인하는 것은 어떤 요청을 처리했는지와 그 요청의 입력 버전이다. 최신 필드의 내용을 서버 응답 객체로 통째로 덮는 방식을 피했다.

입력 revision과 서버 버전도 별도로 둔다. 내가 입력을 세 번 고쳤다는 사실이 서버 문서 버전이 세 번 증가했다는 뜻은 아니기 때문이다. 이 예제의 서버 버전은 합성 응답에 명시적으로 넣는다. 실제 서버가 어떻게 버전을 부여하고 충돌을 막는지는 앞서 공개한 조건부 변경 계약과 연결할 책임이다.

나중 요청의 응답을 먼저 받은 경우

각 요청에는 증가하는 sequence를 준다. 두 번째 요청의 성공을 반영한 뒤 첫 번째 응답이 돌아오면 첫 요청의 대기 표시는 제거하지만 확인된 최신 순서와 서버 버전은 되돌리지 않는다. 테스트는 첫 내용과 나중 내용을 저장한 뒤 응답 순서를 뒤집고 나중 초안과 버전이 남는지 검사한다.

이미 처리해 제거한 요청의 응답을 다시 받거나 알 수 없는 번호를 받으면 오류로 멈춘다. 실제 네트워크의 중복 응답을 자동 복구하는 코드는 아니다. 호출한 화면은 무시 가능한 중복과 추적할 오류의 표현을 정해야 한다. 이번 모델에서는 조용히 다른 요청으로 해석하지 않는 경계를 먼저 확인했다.

응답을 기다리는 동안 더 고친 내용은 남는다

첫 요청 뒤에 새로 입력하고 아직 보내지 않았다면 첫 성공을 받아도 dirty는 참이어야 한다. acked_revision은 보낸 내용까지만 확인하고 최신 revision과 비교한다. 저장 중 표시를 지웠다는 이유로 보내지 않은 수정까지 저장됐다고 말하지 않는다. 실패한 요청도 확인 버전을 앞으로 옮기지 않는다.

화면에는 저장 중, 이 버전까지 확인됨, 아직 보내지 않은 수정이 있음이라는 서로 다른 정보가 필요할 수 있다. 이번 함수는 dirty와 pending 개수, 서버 버전을 돌려주며 실제 상태 문구나 알림은 만들지 않았다. 숫자의 의미를 정하는 일과 사용자가 문구를 이해하는 일은 별도로 검수할 수 있도록 남겨 뒀다.

화면 순서가 서버 저장 순서를 대신하지 않는다

늦은 응답을 무시해도 서버에서 오래된 요청을 나중에 덮어썼다면 실제 문서는 잘못될 수 있다. 그래서 이번 코드를 동시 수정이나 중복 저장을 모두 해결하는 모듈로 소개하지 않는다. 서버는 기대 버전 확인, 요청 식별, 실제 저장 결과 재조회 같은 별도의 약속을 가져야 한다.

합성 모듈에는 로그인, 영구 대기 목록, 재시도 기한과 충돌 병합이 없다. 순서가 더 최신인 성공 응답이라도 서버 버전이 뒤로 가면 오류를 낸다. 이 검사는 이상한 응답을 감지할 뿐 저장소를 고치지 않는다. 사용자가 새로 입력한 내용과 서버에서 확인한 사실을 섞지 않는 최소 책임만 구현했다.

공유한 코드를 어디에 연결할까

패키지 테스트에는 역순 응답, 보내지 않은 추가 수정, 실패, 같은 응답 반복과 서버 버전 역행이 들어 있다. 입력 객체를 복사해 돌려주므로 테스트 전 상태를 보존하며 비교할 수 있다. 실제 브라우저에서 통신을 늦추거나 사용자의 입력을 바꾼 검수는 아니다. 페이지의 기록도 고정 합성 실행 결과다.

실제 화면에 적용한다면 응답 처리기가 초안을 덮는 지점부터 찾아보면 좋다. 서버 계약과 상태 문구를 연결한 뒤 실제 입력과 지연 통신을 함께 검수해야 한다. 나는 저장이 됐다는 한 단어를 여러 사실로 풀어 보고, 그중 무엇을 확인했는지 코드와 테스트로 남기는 과정을 공개하고 싶었다.