본문으로 건너뛰기
yceffort
PostsSeriesTagsAbout🧪 Research
EN

Tweaks

theme
accent palette
film grain
minimal mode
BACK TO INDEX
◆ ESSAY
--min
--year
KOoriginal

mailMail icongithubtwitter
yceffort
•
© 2026
•
https://yceffort.kr
BACK TO INDEX
◆ ESSAY

(함수형으로) 자바스크립트로 HTML 버튼 중복 클릭 방지하기

avatar
yceffort
2020-10-22 · 5분
5min
2020year
KOoriginal
javascriptfrontend

버튼 중복 클릭을 방지하는 것은 중요하다. 물론 기본적인 중복 방지에 대한 처리는 서버에 되어 있어야 하지만, 그렇다고 마냥 프론트엔드에서 손놓고 있을 수는 없는 일이다.

주니어 풀스택 개발자일때, 중복 클릭에 대해서 프론트엔드에서 부단히도 막아보려고 노력했지만, 사용자들은 갖가지 방법으로 여러번 클릭을 했고, 그 때마다 사용자들은 더욱 더 빠른 속도로 (혹은 창의적인 방법으로) 중복클릭을 해서 괴롭히곤 했다. 결국 어느정도 감내하고 (?) 서버에서 막는 방향으로 가긴했지만 - 그 때 마다 더 좋은 방법이 없을까 고민하곤 했다. 그 때 나왔던 다양한 중복 클릭 방지 방법을 이야기 해보고, 문제점은 무엇이었으며, 이를 해결할 수 있는 최선의 코드를, 함수형으로 써보면 어떻게 되는지 알아보장

방법 1) 글로벌 플래그 사용하기

let clicked = false

function payment() {
  if (!clicked) {
    clicked = true
    window.alert('결제가 진행 중입니다.')
    // 결제 프로세스..
  }
}

이 방법도 물론 작동은 하겠지만 몇가지 문제가 있다.

  • 전역 변수를 선언하는 것은 위험하다. 전역변수는 누구든 접근 가능하기 때문에, 자신이 알지도 못하는 새에 값이 변경될 수 있다.
  • 사용자가 다시 버튼을 누를 수 있는 상황에 대비하여 해당 변수를 다시 초기화 하는 코드를 어딘가에도 넣어야 한다.
  • 외부 변수에 의존하기 때문에 테스트 하기가 곤란하다.

방법 2) 핸들러를 날려 버리기 (혹은 변경하기)

function payment() {
  document.getElementById('payment').onclick = null
  window.alert('결제가 진행 중입니다.')
  // 결제 프로세스..
}

이 방법도 일단은 작동하겠지만 문제가 존재한다.

  • DOM의 버튼과 매우 강하게 연결 되어 있는 코드 이기 때문에, 재사용이 불가능하다.
  • 위 예제와 마찬가지로 다시 버튼 클릭할 수 있는 상황에 대비하여 초기화하는 코드가 필요하다.
  • DOM 요소가 있어야만 테스트가 가능하다.

방법 3) 버튼을 disable 처리하기

function payment() {
  document.getElementById('payment').setAttribute('disabled', 'true')
  window.alert('결제가 진행 중입니다.')
  // 결제 프로세스..
}

역시 위와 마찬가지 이슈가 있다.

방법 4) 지역 변수 사용하기

var payment = ((clicked) => {
  return () => {
    if (!clicked) {
      clicked = true
      window.alert('결제가 진행 중입니다.')
      // 결제 프로세스..
    }
  }
})(false)

이번에는 즉시실행함수를 사용하였다. 이는 함수형 접근법이기도 하고, clicked를 외부에서 접근할 수 없기도 하다. 1번의 예제에서 전역변수로 되어 있던 것을 지역변수로 바꾸고, 스코프를 잠궈서 접근하지 못하게 했다고 생각하면 된다. 이 코드의 단점은 한번만 클릭이 필요한 모든 함수에 대해서 이와 같이 똑같이 작업을 해야한다는 것이다.

(아마도) 최선의 방법

  • 단 한번만 호출해야 하는 원래 함수는 원래 작업 (결제) 이외의 작업을 해서는 안된다.
  • 원본 함수를 수정해서는 안된다.
  • 원본 함수를 한번만 호출하는 새로운 함수가 필요하다.
  • 기존 기능에 적용할 수 있는 일반적인 솔루션이 필요하다.

여기에 필요한 것이 바로 고차함수 (higher-order function) 이다.

const doOnce = (fn) => {
  let done = false
  return (...args) => {
    if (!done) {
      done = true
      fn(...args)
    }
  }
}
const doOnceTypescript = (fn: Function) => {
  let done = false
  return (...args: any) => {
    if (!done) {
      done = true
      fn(...args)
    }
  }
}

이를 적용하면 아래와 같은 모습일 것이다.

<button id="payment" onclick="doOnce(payment)()">결제하기</button>

관련 글

  • #javascript#animation#web-animations-api

    number-flow를 구형 브라우저로 이식하기: 다섯 가지 결정과 두 가지 번복

    number-flow가 애니메이션을 켜는 최소 버전은 Chrome 125, Safari 17.2다. 이 하한을 Chrome 66과 WebKit 16.4까지 내리는 포크를 만들면서 내린 결정들과, 뒤집게 된 판단 두 가지, 그리고 자동 강등을 포기한 Safari 버그 조사의 기록.

    2026-08-11·44분
  • #javascript#frontend#web-performance

    IntersectionObserver 싱글톤 패턴과 WeakMap으로 메모리 누수 방지하기

    수백 개의 요소를 효율적으로 관찰하면서 메모리 누수도 방지하는 방법

    2026-01-17·20분
  • #react#javascript#frontend

    리액트 면접 질문 모음 (1)

    [목차](/2019/08/13/reactjs-interview-questions/) ```toc tight: true, from-heading: 2 to-heading: 3 ``` ## Core React ### What is React 리액트는 오픈소스 프론트엔드 자바스크립트 라이브러리로, 특히 싱글 페이지 애플리케이션의 사용자 인터페이스 구축을...

    2019-08-21·64분
  • #turbopack#nextjs#bundler

    Next.js turbopack에서 싱글톤이 두 개가 됐다: scope hoisting 버그와 순환 import

    Next.js 16 turbopack 프로덕션 빌드에서 모듈 스코프 싱글톤이 런타임에 두 개가 됐다. 같은 동기 구간에서 조건 판정이 뒤집히고, 응답이 도착해도 타임아웃이 나는 증상을 번들 산출물로 추적한 기록. scope hoisting의 부분 병합, 순환 import, 이미 고쳐져 있던 upstream 버그, 그리고 뒤늦게 돌린 단일 변수 실험까지.

    2026-08-19·28분

새 글을 놓치고 싶지 않으시다면 RSS로 구독해 주세요.

RSS 구독 →

yceffort — 프론트엔드 엔지니어입니다. 발표·기술 자문·기고 문의는 이곳에서 받고 있습니다.

← Back to the blogIssue on GitHub →