[PonsLink] 놓친 이벤트와 중복 요청을 동시에 줄이는 법
놓친 이벤트와 중복 요청은 서로 다른 오류처럼 보이지만 사용자에게는 같은 결과로 남는다. 한 번 보낸 요청이 사라지거나, 같은 요청이 두 번 처리되면 화면의 상태와 실제 상태가 어긋난다.
PonsLink의 요청 흐름을 만들면서 이 문제를 뒤늦게 분리했다. 요청을 먼저 받고, 상태를 바꾸고, 방이나 다음 단계로 넘기는 과정에서 이벤트가 한 번 빠져도 다시 확인할 수 있어야 했다.

한 번 처리됐다는 말의 함정
네트워크가 느리면 사용자는 버튼을 다시 누른다. 서버가 첫 요청을 처리했지만 응답이 늦게 도착한 경우라면, 두 번째 요청은 새로운 요청이 아니라 같은 요청의 재전송이다.
반대로 이벤트가 저장되기 전에 연결이 끊기면 서버는 아무 일도 없었던 것처럼 보일 수 있다. 화면에는 제출이 끝난 것처럼 보이는데 내부에는 기록이 없거나, 다시 열었을 때 같은 작업이 두 번 실행되는 이유다.

재생 가능한 요청으로 바꾸기
각 요청에 다시 확인할 수 있는 식별자를 붙이고, 처리 결과를 그 식별자와 함께 저장하는 쪽으로 바꿨다. 같은 식별자가 다시 들어오면 작업을 새로 실행하지 않고 이미 기록한 결과를 돌려준다.
이렇게 하면 재시도는 위험한 예외가 아니라 정상적인 복구 경로가 된다. 중요한 것은 요청을 한 번만 보내게 만드는 것이 아니라, 여러 번 도착해도 결과가 한 번만 만들어지게 하는 것이다.

놓친 이벤트를 다시 확인하는 기준
재생성할 수 있는 것은 요청의 결과이지, 모든 외부 효과는 아니다. 결제나 알림처럼 밖으로 나간 효과는 별도의 상태와 재시도 기준이 필요하다. 그래서 내부 이벤트와 사용자에게 보이는 상태를 같은 것으로 취급하지 않았다.
운영자가 장애를 확인할 때도 같은 식별자로 요청의 최초 도착, 처리 결과, 재시도 횟수를 따라갈 수 있어야 했다. 그래야 “두 번 눌렀다”는 추측 대신 어느 단계에서 기록이 끊겼는지 말할 수 있다.
마치며
중복을 막는 핵심은 버튼을 비활성화하는 데 있지 않았다. 요청이 다시 와도 같은 결과로 수렴하도록 기록과 경계를 설계하는 데 있었다. 다음 이벤트 시스템에서도 먼저 재시도와 누락을 정상 흐름으로 놓고 시작한다.