앱 개발과 프론트엔드 실제 서비스 개발을 하다보면, 정말 많이 필요하고 사용하게될 개념이다.
Throttle 과 Debounce 모두, 무의미하게 무수하게 발생하는 이벤트를 막기 위한 기법이다.
- Throttle : 함수 실행 후 특정 기간 동안 추가 실행을 모두 취소 (특정기간동안, 맨 처음 실행된것만 실행)
- Debounce : 특정 기간안의 함수 실행을 모두 취소하고, 마지막에만 실행 (특정기간동안, 마지막 에 실행된것만 실행)
Throttle
throttle은 영어로 조르다, 목을 조르다라는 의미를 가지고 있다.
이벤트의 목을 조른다(?)라는 느낌으로 일단 이해를 하는 것이 좋다.
throttle 의 목적은 이벤트가 무수하게 발생하더라도, 일정 시간 동안 이벤트가 1번만 발생하도록 처리하는 것을 의미한다.
보통 앱개발에서 페이징, 스크롤 기능에 throttle 을 적용해서, '데이터 중복 요청방지' 기능을 보통 작업한다.
스크롤을 할 때 최적화하지 않으면, 이벤트가 무수히 많이 발생하고, 리스트 데이터가 꼬이고 엉망이 된다.
아래 예시 코드에서는:
- Throttle 클래스를 사용하여 5초의 지연 시간을 가진 쓰로틀 객체를 생성합니다.
- _incrementCounter 메서드에서 쓰로틀을 적용합니다. 이 메서드는 버튼이 눌릴 때마다 호출되지만, 실제로는 5초에 한 번만 카운터를 증가시킵니다.
- 사용자가 버튼을 빠르게 여러 번 눌러도 카운터는 5초에 한 번씩만 증가합니다.
import 'package:flutter/material.dart';
import 'dart:async';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: ThrottleButtonExample(),
);
}
}
class ThrottleButtonExample extends StatefulWidget {
@override
_ThrottleButtonExampleState createState() => _ThrottleButtonExampleState();
}
class _ThrottleButtonExampleState extends State<ThrottleButtonExample> {
int _counter = 0;
final _throttle = Throttle(delay: Duration(seconds: 5));
void _incrementCounter() {
_throttle.run(() {
setState(() {
_counter++;
});
print('버튼이 눌렸습니다. 카운터: $_counter');
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Throttle Button Example'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'버튼을 누른 횟수:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
class Throttle {
final Duration delay;
bool waiting = false;
Timer? _timer;
Throttle({required this.delay});
void run(Function action) {
if (!waiting) {
action();
waiting = true;
_timer = Timer(delay, () {
waiting = false;
});
}
}
}
Debounce
이벤트가 무수히 발생하더라도 마지막 이벤트를 기준으로 일정 시간이 지나면 이벤트를 1번만 발생시키는 기법이다.
마지막 이벤트까지의 이벤트들을 하나의 그룹으로 생각해서, 한 개의 이벤트를 발생시키는 것이다.
다른 말로 말하면, 일정한 시간이 지나기 전에 이벤트가 계속 발생하면 이벤트를 막아버린다.
입력값 실시간 검색 기능 같은 곳에 많이 쓰인다.
이 예시 코드에서는:
- Timer? 타입의 _debounce 변수를 선언하여 디바운스 타이머를 관리합니다.
- _incrementCounter 메서드에서 디바운스를 구현합니다:
- 사용자가 버튼을 연속해서 빠르게 누르면, 마지막 클릭 후 500밀리초가 지난 후에만 카운터가 증가합니다.
- dispose 메서드에서 타이머를 취소하여 메모리 누수를 방지합니다.
이 방식으로 버튼 이벤트에 디바운스를 적용하면, 사용자의 빠른 연속 클릭을 하나의 액션으로 처리하여 불필요한 상태 업데이트나 작업 실행을 방지할 수 있습니다.
import 'package:flutter/material.dart';
import 'dart:async';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: DebounceButtonExample(),
);
}
}
class DebounceButtonExample extends StatefulWidget {
@override
_DebounceButtonExampleState createState() => _DebounceButtonExampleState();
}
class _DebounceButtonExampleState extends State<DebounceButtonExample> {
int _counter = 0;
Timer? _debounce;
void _incrementCounter() {
if (_debounce?.isActive ?? false) _debounce!.cancel();
_debounce = Timer(Duration(milliseconds: 500), () {
setState(() {
_counter++;
});
print('버튼이 눌렸습니다. 카운터: $_counter');
});
}
@override
void dispose() {
_debounce?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Debounce Button Example'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'버튼을 누른 횟수:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
'Flutter > Skills' 카테고리의 다른 글
제네릭 <T> 를 사용할때, 착각하기 쉬운 경우 (0) | 2024.11.26 |
---|---|
Pagination (0) | 2024.11.05 |
Unit Test (0) | 2022.12.01 |
gskinner (0) | 2022.11.22 |
Dio + Retrofit + JsonSerializable 통한 서버 통신 (0) | 2022.11.18 |