반응형
해당 글에서는 버튼의 중복 호출을 막기 위해 lodash 라이브러리의 Debounce 모듈을 활용하는 방법에 대해 알아봅니다.
1) 문제점 파악
💡 문제점 파악
- SetState()로 버튼을 누르면 즉각적으로 버튼에 대해 disabled = true 처리를 하면 해결이 되지만, 부모와 자식 구조간에 관계에서 빠르게 처리가 되지 않아서 중복으로 함수 처리가 되는 문제가 발생하였습니다.
- 이에 따라서 버튼을 여러 번 빠르게 눌러도 한 번의 처리인 것과 같은 기능을 구현하고 싶어서 찾아보는 중 lodash 라이브러리의 Debounce 모듈을 알게 되어 이를 적용하였습니다.
2) lodash
💡 lodash
- JavaScript에서 사용할 수 있는 유틸리티 라이브러리입니다.
- 배열, 객체, 함수, 언어 유틸, 숫자, 문자열, 날짜, 논리 등과 같은 다양한 데이터 타입을 처리하는 데 도움이 되는 수많은 메서드를 제공합니다.
- 이 라이브러리를 사용하면 코드를 보다 명확하고 간결하게 작성할 수 있습니다. 또한 Lodash는 모듈화가 되어 있어 필요한 기능만 선택적으로 사용할 수 있으므로, 애플리케이션의 성능 향상에도 기여할 수 있습니다.
1. lodash 주요 모듈
모듈명 | 설명 |
_.chunk | 배열을 지정된 크기의 여러 부분으로 나눕니다. |
_.compact | 배열에서 거짓 값(false, null, 0, "", undefined, NaN)을 제거합니다. |
_.concat | 배열 또는 값들을 연결하여 새 배열을 만듭니다. |
_.difference | 첫 번째 배열에는 있지만 두 번째 배열에는 없는 값을 반환합니다. |
_.drop | 배열의 첫 번째 요소를 제거하고 나머지 요소를 반환합니다. |
_.debounce | 연속적으로 호출되는 함수의 실행을 일정 시간 동안 ‘지연’시킵니다. |
2. lodash Debounce
💡 lodash Debounce
- 연속적으로 호출되는 함수의 실행을 일정 시간 동안 ‘지연’시키는 역할을 합니다. 이는 사용자에게서 이벤트가 여러 번 발생할 경우, 이벤트 핸들러가 과도하게 호출되는 것을 방지하고, 성능을 향상하는 데 도움이 됩니다.
- 예를 들어, 버튼을 누르면 DB INSERT를 수행하는 API를 호출하는 함수가 있다고 가정합니다.
- 그리고 해당 함수는 lodash Debounce로 감싸져 있고, 시간을 5000으로 지정했습니다.
- 그래서 사용자는 연속으로 해당 버튼을 연타를 누릅니다. 누르게 되면 5초(5000) 동안에 발생하는 이벤트 중 제일 마지막에 누른 1번만 수행을 합니다. 단 5초가 지날 때까지 감싸진 함수는 수행되지 않습니다.
3) lodash Debounce를 이용한 버튼 제어
1. 라이브러리 설치
$ npm install lodash
# or
$ yarn add lodash
# typescript
$ npm install --save @types/lodash
# or
$ yarn add --dev @types/lodash
2. lodash Debounce를 이용한 버튼 제어 예시
💡 lodash Debounce를 이용한 버튼 제어 예시
- TestScreen로 컴포넌트 화면을 구성하였습니다. ‘버튼 테스트를 수행합니다.’라는 버튼을 누르면 onPressTest() 함수가 수행되는 구조입니다.
- onPressTest() 함수는 lodash의 debounce 함수로 감싸져 있어서 버튼을 누르면 5초 뒤에 수행이 됩니다.
- 이 5초가 수행되는 과정에 여러번 버튼을 터치한 경우에 5초가 지나기 전 제일 마지막에 누른 이벤트만 수행을 합니다.
import React from "react";
import { Text, TouchableOpacity } from "react-native";
import { View } from "react-native";
import _ from "lodash";
const TestScreen = () => {
const onPressTest = _.debounce(() => {
console.log("[+] 여러번 반복 터치를 수행합니다.");
}, 5000)
// or
const pressHandler = (() => {
return {
onPressTest: _.debounce(() => {
console.log("[+] 여러번 반복 터치를 수행합니다.");
}, 5000)
}
})();
return (
<View>
<TouchableOpacity onPress={onPressTest} style={{ backgroundColor: "red", height: 200 }}>
<Text>버튼 테스트를 수행합니다.</Text>
</TouchableOpacity>
</View>
)
}
export default TestScreen
3. lodash Debounce 문제점
💡 문제점
- 컴포넌트가 리 렌더링 될때마다 debounce 함수는 재생성되므로 debounce가 초기화됩니다. 따라서 연속적으로 이벤트가 발생하더라도 debounce 함수는 새롭게 시작되므로 원하는 디바운스 효과를 얻지 못할 수 있습니다.
- 그렇기에 이 문제를 해결하기 위해서는 debounce 함수를 컴포넌트 외부에 정의하거나, React의 useCallback Hook을 이용하여 함수를 메모이제이션 하는 방법이 일반적으로 사용됩니다.
💡 아래의 코드에서는 TestScreen 컴포넌트가 리렌더링 될 때마다 onPressTest 함수를 재생성합니다. 이 때문에 해당 함수는 lodash의 debounce 함수의 효과를 제대로 발휘하지 못하게 됩니다.
import React from "react";
import { Text, TouchableOpacity } from "react-native";
import { View } from "react-native";
import _ from "lodash";
const TestScreen = () => {
const onPressTest = _.debounce(() => {
console.log("[+] 여러번 반복 터치를 수행합니다.");
}, 5000)
// or
const pressHandler = (() => {
return {
onPressTest: _.debounce(() => {
console.log("[+] 여러번 반복 터치를 수행합니다.");
}, 5000)
}
})();
return (
<View>
<TouchableOpacity onPress={onPressTest} style={{ backgroundColor: "red", height: 200 }}>
<Text>버튼 테스트를 수행합니다.</Text>
</TouchableOpacity>
</View>
)
}
export default TestScreen
4. useCallback을 이용한 해결방법
💡 useCallback을 이용한 해결방법
- useCallback을 사용하여 onPressTest 함수를 메모이제이션하였습니다. 이로써, 컴포넌트가 리렌더링 되더라도 onPressTest 함수는 새롭게 생성되지 않고 메모리에 저장된 이전 함수가 반환됩니다.
- 이를 통해서 디바운스 효과를 유지하는 데 필요한 조건을 충족시킵니다.
import React, { useCallback } from "react";
import { Text, TouchableOpacity } from "react-native";
import { View } from "react-native";
import _ from "lodash";
const TestScreen = () => {
const onPressTest = useCallback(_.debounce(() => {
console.log("[+] 여러번 반복 터치를 수행합니다.");
}, 5000), [])
// or
const pressHandler = (() => {
return {
onPressTest: useCallback(_.debounce(() => {
console.log("[+] 여러번 반복 터치를 수행합니다.");
}, 5000), [])
}
})();
return (
<View>
<TouchableOpacity onPress={onPressTest} style={{ backgroundColor: "red", height: 200 }}>
<Text>버튼 테스트를 수행합니다.</Text>
</TouchableOpacity>
</View>
)
}
export default TestScreen
💡[참고] useCallback 이란?
- 메모이제이션이 된 콜백을 반환하는 Hook을 의미합니다. 주로 렌더링 사이에 이 함수를 기억할 때 유용합니다.
- 첫 번째 매개변수로 콜백, 두 번째 매개변수로 의존성 배열을 받습니다.
- useCallback을 사용하면, 의존성 배열에 있는 값들이 변경될 때만 콜백이 다시 생성됩니다. 이는 비용이 많이 드는 연산을 피하고, 함수의 참조 일관성을 유지하는데 도움이 됩니다.
💡 사용예시
- memoizedCallback() 함수의 경우 a 또는 b의 값이 변경되는 경우에 해당 함수가 다시 생성이 됩니다.
const memoizedCallback = useCallback(() => {
doSomething(a, b);
},[a, b]
);
4) 테스트 결과
💡 테스트 결과
- 아래와 같은 코드 내에서 버튼을 터치하면 “[+] 버튼을 반복 터치합니다.”이라는 콘솔을 출력하는 한편 onPressTest() 함수를 호출합니다.
- 실제 터치를 했을 때는 콘솔이 나오지만, onPressTest() 함수는 1초간 한 번만 호출되는 처리를 확인할 수 있었습니다.
import React, { useCallback } from "react";
import { Text, TouchableOpacity } from "react-native";
import { View } from "react-native";
import _ from "lodash";
const TestScreen = () => {
const onPressTest = useCallback(_.debounce(() => {
console.log("[+] 실제 함수의 호출")
}, 1000), [])
return (
<>
<View>
<TouchableOpacity onPress={() => {
console.log("[+] 버튼을 반복 터치합니다.")
onPressTest()
}}
style={{ backgroundColor: "red", height: 200 }}>
<Text>버튼 테스트를 수행합니다.</Text>
</TouchableOpacity>
</View>
</>
)
}
export default TestScreen
오늘도 감사합니다. 😀
반응형