첫 페이지를 읽는 사이 새 주문이 앞에 들어오면 두 번째 페이지에서 같은 행을 다시 볼 수 있다. 삭제가 끼어들면 반대로 한 행을 놓칠 수도 있다.

나는 다음 위치의 숫자만 기억하는 대신 같은 조회 결과 안에서 어디까지 읽었는지 남기는 독립 모듈을 만들었다. 실제 운영 목록을 변경하지 않고 고정 합성 자료로 경계를 재현했다.

현재 목록의 몇 번째인지와 무엇 뒤인지는 다르다

자료가 추가되는 목록에서 처음 몇 개를 건너뛴다는 위치는 시간이 지나면 다른 행을 뜻할 수 있다. 이번 예제는 tick과 ID를 함께 정렬한 조회 복사본을 만든다. 다음 커서에는 마지막 행의 두 값을 남긴다. 같은 tick에 여러 행이 있어도 ID까지 비교해 어떤 행을 읽었는지 구분한다.

이것이 모든 목록에서 영원히 안정적인 정렬이라는 뜻은 아니다. 실제 서비스의 정렬 키가 편집될 수 있는지, 삭제된 행을 어떻게 다루는지 확인해야 한다. 이번 복사본 안의 값은 변하지 않으므로 그 범위에서만 순서를 유지한다. 실시간 최신 목록과 같은 기준의 이어 읽기를 동시에 보장하는 마법 같은 페이지 방식으로 설명하지 않는다.

페이지가 속한 조회 결과를 같이 식별한다

커서는 다음 위치뿐 아니라 조회 지문을 담는다. 지문에는 회사·필터·검색어·자료 버전과 선택한 행의 내용이 들어간다. 다른 필터로 바꾸거나 원본 내용이 바뀐 새 조회에 예전 커서를 쓰면 거절한다. 호출자가 같은 버전 번호를 잘못 재사용해도 실제 선택 내용이 달라지면 지문은 달라진다.

지문은 같은 결과인지 비교하는 장치다. 커서를 숨기거나 서명하는 보안 기능은 아니다. 패키지는 base64 JSON으로 커서를 표현하며 내용을 아는 호출자는 같은 결과 안의 다른 유효한 위치를 만들 수 있다. 이를 의도적으로 통과시키는 테스트도 있다. 자료 권한과 서명 요구는 실제 서버가 별도로 구현해야 한다.

원본의 추가와 삭제를 복사본에서 분리했다

독립 테스트는 조회 복사본을 만든 뒤 원본 앞에 새 행을 넣거나 원본 목록을 지운다. 이미 만든 결과에는 처음 선택한 세 행이 남고 새 결과는 바뀐 원본을 반영한다. 기존 페이지를 이어 읽는 일과 새로고침해서 최신 자료를 보는 일을 서로 다른 선택으로 만든 것이다. 두 결과가 몰래 섞이지 않아야 한다고 판단했다.

실제 서버에서 이 방식을 쓰려면 조회 복사본을 어디에 얼마 동안 보관할지, 사용자가 어떤 권한으로 다시 읽을지 정해야 한다. 이번 모듈은 Python 값으로만 결과를 보관하고 저장소 서비스나 만료 정책은 없다. 브라우저가 닫혀도 같은 조회를 서버에서 복구할 수 있다는 기능은 구현하지 않았다. 화면 밖의 지속 상태는 또 다른 책임이다.

잘못된 커서를 정상 결과로 바꾸지 않는다

다른 조회의 커서, 형식이 깨진 문자열, 존재하지 않는 위치는 오류로 멈춘다. 이를 빈 목록으로 바꾸면 사용자는 끝까지 읽었다고 오해할 수 있다. 끝난 결과는 next_cursor가 없다는 정상 신호로, 잘못된 이어 읽기는 거절로 나눴다. 커서는 최대 500자이고 알려진 필드만 받는다. 기본 페이지 크기도 1~50의 정수로 제한하며 Python의 참·거짓 값을 정수 크기로 받지 않는다.

현재 모듈은 오류 문구를 사용자 언어로 정리하는 화면은 없다. 실제 UI는 검색 조건을 유지하면서 다시 시작할지 새 조회를 만들지 설명해야 한다. 권한 오류와 조회 만료, 잘못된 페이지도 서로 다르게 다룰 수 있다. 코드가 값을 거절한다는 사실과 방문자가 다음 행동을 이해하는 검수는 구별해 둔다.

겹치지 않고 빠지지 않았다는 근거

테스트는 같은 tick의 ID 순서, 모든 페이지와 전체 출력의 일치, 다른 조건과 바뀐 내용의 커서 거절을 확인한다. 실제 로컬 실행 기록에는 이전 복사본과 새로운 복사본을 나란히 담았다. 어떤 목록이 기준인지 다시 읽을 수 있다. 실제 회사 주문을 추가·삭제하거나 브라우저 페이지 이동을 검수한 것은 아니다.

처음에는 페이지 번호 버튼만 만들면 충분해 보일 수 있다. 하지만 업무 자료가 계속 바뀐다면 사용자가 읽던 위치와 최신 자료의 관계를 정해야 한다. 나는 모든 데이터를 최신으로 덮는 편리함보다 비교 가능한 한 번의 조회가 필요한 상황을 설명하고 싶었다. 공유 코드에서 그 선택과 적용 조건을 함께 확인할 수 있다.