UI 패턴
UX 동작 및 성능 최적화 관련 기법 모음.
Debounce
연속된 이벤트 중 마지막 호출만 실행. 입력이 멈춘 뒤 일정 시간 후 실행된다.
입력: A--B--C-----------D
실행: C D
(delay 후) (delay 후)
사용 예시: 검색 자동완성, 폼 유효성 검사
function debounce<T extends (...args: any[]) => void>(fn: T, delay: number) {
let timer: ReturnType<typeof setTimeout>;
return (...args: Parameters<T>) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), delay);
};
}
const onSearch = debounce((value: string) => {
fetchResults(value);
}, 300);Throttle
연속된 이벤트를 일정 간격마다 한 번씩만 실행. 실행 횟수를 제한한다.
입력: A--B--C--D--E--F--G
실행: A-----------D-----------G
(interval) (interval)
사용 예시: 스크롤 이벤트, 리사이즈 이벤트, 버튼 연속 클릭 방지
function throttle<T extends (...args: any[]) => void>(fn: T, interval: number) {
let lastTime = 0;
return (...args: Parameters<T>) => {
const now = Date.now();
if (now - lastTime >= interval) {
lastTime = now;
fn(...args);
}
};
}
const onScroll = throttle(() => {
updateScrollPosition();
}, 100);Debounce vs Throttle
| Debounce | Throttle | |
|---|---|---|
| 실행 시점 | 입력 멈춘 후 | 일정 간격마다 |
| 중간 이벤트 | 모두 무시 | 일부 실행 |
| 적합한 상황 | 최종 값만 필요 | 중간 상태도 필요 |
| 예시 | 검색창 입력 | 스크롤, 리사이즈 |
Scroll
스크롤 이벤트 기본
window.addEventListener('scroll', throttle(() => {
const scrollY = window.scrollY;
const totalHeight = document.body.scrollHeight - window.innerHeight;
const progress = scrollY / totalHeight; // 0 ~ 1
}, 100));무한 스크롤
window.addEventListener('scroll', throttle(() => {
const { scrollTop, scrollHeight, clientHeight } = document.documentElement;
if (scrollTop + clientHeight >= scrollHeight - 200) {
loadMore();
}
}, 200));Intersection Observer (권장)
스크롤 이벤트 대신 요소의 뷰포트 진입 여부를 감지. 성능상 유리.
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
loadMore();
}
});
}, { threshold: 0.1 });
observer.observe(document.querySelector('#sentinel')!);| 방식 | 성능 | 정밀도 | 용도 |
|---|---|---|---|
| scroll 이벤트 | 낮음 (throttle 필요) | 높음 | 스크롤 위치 기반 로직 |
| IntersectionObserver | 높음 | 중간 | 요소 노출 감지, 무한 스크롤, lazy load |