신청 중복 저장, 버튼만 막으면 충분할까

신청 중복 저장을 막을 때 버튼을 잠깐 비활성화하는 것만으로는 충분하지 않다. 응답을 받지 못한 사용자가 같은 요청을 다시 보낼 수 있기 때문이다. 같은 요청 키로 재시도하면 첫 저장 결과를 돌려주고, 다른 내용에 예전 키를 쓰면 거부되는지 저장 결과까지 확인해야 한다.

사용자가 실수로 연속 클릭한 것과, 같은 사람이 다른 날짜에 다시 신청한 것은 다를 수 있다. 이름이 같다는 이유로 둘 중 하나를 지우면 정상 신청을 잃을 수 있다. 화면에서 한 번만 누르게 만드는 조치가 다른 기기나 재시도까지 모두 막는다고 볼 수도 없다.

같은 파란 키의 동일 요청 두 건은 저장 결과 하나로 합치고 같은 키에 바뀐 내용은 거부하며 새 초록 키의 다른 날짜 신청은 별도 저장하는 흐름

중복의 업무 기준부터 쓴다

가상의 행사 신청에서 같은 행사·같은 연락처·같은 신청 내용이 짧은 간격으로 들어왔다고 하자. 이것을 같은 신청으로 볼지, 수정 신청으로 볼지 운영 규칙이 필요하다. 개인정보를 실제 예제로 쓰지 않고 가상 식별자를 이용해 설명한다.

상황 정할 처리
빠르게 두 번 클릭 한 번의 접수로 안내
응답을 못 보고 재시도 기존 접수 여부 확인
날짜를 바꿔 다시 신청 새 신청·변경 중 구분
다른 사람이 같은 이름 별개의 사람으로 처리

버튼과 저장의 역할을 나눈다

버튼 비활성화는 한 화면에서 바로 이어지는 클릭을 줄여 준다. 하지만 네트워크가 끊겨 성공 응답을 못 받았거나 다른 창에서 다시 보내는 경우까지 판정하지는 못한다.

저장 쪽에서는 요청마다 식별 키를 받고 결과를 기록한다. 식별 키는 “이 전송이 아까 보낸 것과 같은 요청인지”를 구별하며, 동일한 논리 요청을 재시도할 때는 같은 키를 다시 쓴다. 동일한 키와 내용이 다시 오면 새로 저장하지 않고 첫 결과를 돌려준다. 같은 키에 내용이 달라지면 기존 신청을 덮지 않고 거부한다.

의뢰서에는 아래처럼 검수 문구를 그대로 넘길 수 있다.

  • 같은 요청 키와 같은 내용을 두 번 보내면 저장은 1건이고, 두 응답의 신청 ID는 같아야 한다.
  • 같은 요청 키에 날짜나 내용을 바꾸어 보내면 기존 신청을 덮지 않고 거부해야 한다.
  • 다른 날짜의 새 신청에 새 키를 쓰면 별도의 신청 ID로 저장돼야 한다.

격리 실행으로 확인한 결과

Node.js v22.22.0의 단일 프로세스에서 가상 식별자와 메모리 저장소를 사용했다. 개인정보·실제 고객 폼·외부 메일은 사용하지 않았다.

검사 상황 실제 결과 판정
연속 클릭을 가정해 같은 요청을 2회 호출 첫 호출 201 created, 둘째 200 replayed, 둘 다 APP-001 저장 1행
응답 유실 뒤 재시도와 같은 순차 재호출 첫 호출 201 created, 둘째 200 replayed, 둘 다 APP-002 새 행 없음
다른 날짜를 새 키로 신청 201 created, APP-003 정상 신청은 보존
예전 키에 다른 날짜를 실어 재사용 409 key_reused_with_different_payload 기존 내용을 덮지 않음
첫 저장이 끝나기 전에 같은 키가 다시 도착 첫 요청 201 created, 동시 요청 409 request_with_same_key_in_progress 처리 중 요청은 두 번 저장하지 않음

마지막에 저장된 행은 3개였다. 이 결과는 버튼 제어만으로는 확인할 수 없는 재시도의 완료 기준을 보여 준다.

동시에 도착한 두 요청도 따로 확인한다

“먼저 조회하고 없으면 저장”만 하면 두 요청이 거의 동시에 도착했을 때 둘 다 조회를 통과할 수 있다. 공개 질문에서도 처리에 4초 넘게 걸리는 폼을 여러 번 눌러 값이 중복 저장됐고, 버튼 잠금 외에 서버 쪽 방어를 어떻게 할지 묻는 사례가 확인됐다. 오래된 다른 공개 질문에는 Post/Redirect/Get을 썼는데도 빠른 두 번 클릭으로 두 행이 생겼다는 재현이 남아 있다. 이는 시장 전체 빈도나 seoin.dev 독자의 직접 응답을 뜻하지 않는 정성 신호다.

이번 격리 실행은 같은 키가 처리 중이면 둘째 요청을 409로 돌려주고 저장을 한 행으로 유지했다. IETF Idempotency-Key Internet-Draft도 완료 뒤 재시도에는 이전 결과를 돌려주고, 첫 요청 처리 중 같은 키가 다시 오면 충돌 응답을 주는 흐름을 설명한다. 다만 이 문서는 2026년 4월 만료된 Internet-Draft이므로 확정된 RFC라고 부르지 않는다.

실서비스 완료 기준에는 아래 한 줄을 더 넣는다.

  • 같은 키의 요청 두 건을 거의 동시에 보내도 저장은 1건이어야 하며, 둘째 요청은 처리 중임을 구별할 수 있어야 한다.

메모리의 처리 중 표시만으로 여러 서버와 데이터베이스 경쟁까지 해결되지는 않는다. 실제 구현에서는 키를 한 번만 선점하도록 저장소의 원자적 제약이나 트랜잭션을 사용했는지 개발자에게 확인하고, 실제 배포 환경에서 동시 요청을 다시 시험해야 한다.

이미 생긴 중복은 자동 삭제하지 않는다

어떤 건이 유효한지 정하지 않은 상태에서 일괄 삭제하면 확인에 필요한 기록도 잃을 수 있다. 원본을 보존하고 후보를 비교한 뒤 정리하는 방법을 논의한다. 새 중복 방지와 기존 데이터 정리는 다른 작업으로 적는다.

완료 확인은 저장된 결과까지 본다

버튼이 한 번만 눌린 것처럼 보여도 저장이 두 번 될 수 있다. 반대로 한 번 저장됐는데 실패 문구가 나오면 사용자가 다시 누를 수 있다. 접수 결과와 사용자 안내가 일치하는지 확인한다.

의뢰 메모에는 ‘중복 제거’라는 기능명보다 같은 것으로 보는 기준과 예외를 적는다. 이 기준이 있어야 정상 신청을 지킨다. 예를 들어 날짜 변경을 새 신청으로 받을지, 기존 신청의 변경 기능으로 받을지는 운영 규칙으로 먼저 정한다.

결제·재고·법적 접수처럼 실패 비용이 큰 서비스라면 이 메모리 모형을 실서버 검증으로 취급하면 안 된다. 데이터베이스 유일 제약, 트랜잭션, 동시 요청, 키 만료를 별도로 확인해야 한다.

직접 확인할 자료

CSV 중복 정리 실습에서 중복 기준을 고르고 제외 후보를 검토하는 방식을 볼 수 있다. 서버의 중복 접수 방지 기능을 검증한 사례는 아니다.

중복 제출 방지를 의뢰할 때 무엇을 물어보나

버튼을 비활성화하면 끝인가?

아니다. 한 화면의 연속 클릭은 줄일 수 있지만, 응답 유실 뒤 재시도나 다른 창에서 보낸 요청은 저장 단에서 같은 요청인지 다시 판정해야 합니다.

이름과 날짜가 같으면 한 건으로 합쳐도 되나?

그렇게 단정하면 정상 신청을 잃을 수 있습니다. 어떤 항목이 같아야 동일 요청인지, 날짜 변경은 새 신청인지 수정인지를 업무 규칙으로 먼저 정합니다.

검수 완료를 어떻게 확인하나?

같은 키·같은 내용의 반복 요청은 저장 1건과 같은 신청 ID를 남겨야 합니다. 첫 저장 처리 중 같은 키가 들어오면 저장 1건을 유지하고 처리 중임을 구별해야 합니다. 같은 키의 변경된 내용은 기존 값을 덮지 않고 거부하며, 새 키의 정상 신청은 별도 ID로 보존되는지 확인합니다.

검증 기준

  • 마지막 업데이트일: 2026-09-18
  • 확인 환경: Node.js v22.22.0, 단일 프로세스의 메모리 Map과 가상 식별자.
  • 실행 자료: sample/verify.mjs. 단일 프로세스·메모리 Map이므로 네트워크 손실·데이터베이스·다중 서버 경쟁을 재현하지 않았다.
  • 공개 질문: 처리가 느린 폼의 연속 클릭과 서버 방어 질문, PRG 뒤에도 생긴 두 번 저장 질문. 두 사례는 정성 신호이며 검색량이나 자사 독자 반응이 아니다.
  • 주요 근거: Stripe의 멱등성 키 요청, IETF Idempotency-Key Internet-Draft, MDN submit button
  • 출처 적용 범위: Stripe는 같은 키의 재시도·첫 결과 재사용·변경된 매개변수 거부, IETF 초안은 완료 후 이전 결과와 처리 중 409 충돌 흐름, MDN은 disabled 제출 버튼 설정과 실행 중 변경 방법만 받쳐 준다. 이 출처들은 버튼 잠금이 모든 재시도를 막는다고 증명하지 않는다. IETF 문서는 2026-04-18에 만료된 Internet-Draft이며 최종 RFC가 아니다.

직접 만든 실습 자료와 새 도구 소식

AI 도구로 만든 실습 자료, 달라진 기능과 강의 소식을 준비하고 있습니다. 발송을 시작하면 안내해 드려요. 먼저 확인 메일에서 본인 이메일을 확인해 주세요.

신청하기 전에 첫 소식 미리보기에서 내용과 자료를 확인해 보세요.

질문이나 의견을 남겨주세요

이름을 입력하지 않아도 돼요. ‘깜짝 놀란 올빼미’ 같은 별명이 자동으로 붙어요.