버튼을 클릭하면 서버에 요청이 한 번만 들어가야 합니다. 물론 서버에서도 여러 번의 요청이 오더라도 1번 처리하도록 해야 하지만 프론트에서 처리하는 방법도 있습니다.
1. 클릭 시 disabled 처리
중복 클릭을 막는 방법으로 제일 처음 생각하게 되는 것은 disabled 처리일 것입니다. 클릭을 하면 버튼에 disabled 처리를 해 비활성화를 시키고 서버에서의 응답을 받은 후에 버튼을 다시 활성화시키는 것이다.
처음에는 저 또한 이러한 방법으로 처리했습니다. 하지만 버튼이 비활성화되는 것보다 더 빠르게 중복 클릭이 되어 2번 요청이 들어와 시스템 상에 오류가 발생하는 일이 있었습니다.
2. 디바운싱 처리
이러한 오류를 해결하기 위해 디바운싱을 이용하기로 했습니다. 디바운싱이랑 연이어 호출되는 함수들 중 마지막(또는 제일 처음)만 호출하도록 하는 것입니다. 가장 처음 클릭만 허용하고 1초 동안은 그 어떠한 클릭이 일어나더라도 무시하도록 합니다.
** 디바운싱과 함께 언급되는 쓰로트링도 있는데 일정한 주기마다 실행하는 것입니다. 예를 들어 scroll 이벤트가 발생할 때 복잡한 작업이 있다면 성능 상 문제가 발생해 scroll 이벤트에서 몇 초에 한 번씩만 실행되게 제한을 둡니다.
물론 클릭 이벤트가 실행되어 서버로 요청을 보내면 disabled 처리를 하고, 서버에서 응답이 올 경우 버튼이 활성화되도록 할 것 입니다. 그러기 위해 직접적으로 props를 명시적으로 선언하지 않고 폴스루 속성을 이용하였습니다. 제가 만들 컴포넌트는 싱글 루트 엘리먼트를 렌더링 하기 때문에 자동으로 속성이 추가됩니다. (class, style, id, v-on 이벤트 등)
<SubmitButton :disabled="isSumitted" :onClick="handleOnClick" />
<template>
<button type="submit" @click.prevent="handleOnClick">
<slot>버튼</slot>
</button>
</template>
<script>
export default {
name: 'SubmitButton',
props: {
onClick: { type: Function, required: true },
},
data() {
return {
timer: undefined,
}
},
methods: {
handleOnClick() {
if (!this.timer) this.onClick()
this.timer = setTimeout(() => {
this.timer = undefined
}, 1000)
},
},
}
</script>
제대로 중복 클릭이 방지가 되는지 스토리북에 테스트 코드를 작성합니다. 클릭 이벤트를 여러 발생시키고 실제로 버튼 이벤트가 1번말 발생했는지를 테스트 코드로 작성합니다.
import { expect, fn, userEvent, within } from '@storybook/test'
import SubmitButton from '@/components/atoms/SubmitButton.vue'
export default {
title: 'components/atoms/SubmitButton',
component: SubmitButton,
parameters: {
componentSubtitle:
'처음 클릭 이벤트 후에 1초 동안 다른 클릭 이벤트가 일어나도 무시하도록 동작하는 버튼 컴포넌트입니다.',
},
}
const handleSubmitButtonOnclick = fn(() => {})
export const Default = {
args: {
disabled: false,
onClick: handleSubmitButtonOnclick,
},
play: async ({ canvasElement, step }) => {
const canvas = within(canvasElement)
const button = canvas.getByRole('button')
await step('더블 클릭 방지 테스트', async () => {
await userEvent.click(button)
await userEvent.click(button)
await userEvent.click(button)
await expect(handleSubmitButtonOnclick).toHaveBeenCalledTimes(1)
})
},
}
스토리북에서 테스트 코드에 대한 결과 화면입니다.
'Dev > Vue.js' 카테고리의 다른 글
[Vue] Storybook 사용하는 법 (1) | 2025.01.15 |
---|---|
[Vue] 타이머 구현하기 (0) | 2025.01.08 |
[Vue] 이메일 유효성 검사 (0) | 2024.12.21 |
[Vue] 웹뷰에서 중요정보 노출 방지 (+ 메모리 덤프 확인하는 법) (0) | 2024.12.14 |
[Vue] Vue3, Storybook8 프로젝트 설정 (2) | 2024.11.28 |
댓글