JavaScript 이벤트 완벽 가이드 - 이벤트 핸들링 마스터하기

JavaScript 이벤트 완벽 가이드 - 이벤트 핸들링 마스터하기 안녕하세요! 오늘은 웹 개발의 핵심인 JavaScript 이벤트에 대해 자세히 알아보겠습니다. 사용자의 클릭, 키보드 입력, 마우스 움직임 등 모든 상호작용은 이벤트를 통해 처리됩니다. 이 포스팅에서는 이벤트의 기본 개념부터 고급 활용까지 체계적으로 정리해보겠습니다! 이벤트란? 기본 개념 **이벤트(Event)**는 웹 페이지에서 발생하는 모든 상호작용을 의미합니다. 사용자의 마우스 클릭, 키보드 입력, 페이지 로드 등이 모두 이벤트입니다. 이벤트의 구성 요소 이벤트 타입: 어떤 종류의 이벤트인지 (click, keydown, load 등) 이벤트 타겟: 이벤트가 발생한 요소 이벤트 핸들러: 이벤트 발생 시 실행될 함수 주요 이벤트 타입들 1. 마우스 이벤트 이벤트 타입 설명 특징 click 마우스 클릭 가장 일반적인 클릭 이벤트 dblclick 더블 클릭 빠르게 두 번 클릭 mousedown 마우스 버튼 누름 버튼을 누르는 순간 mouseup 마우스 버튼 해제 버튼을 떼는 순간 mousemove 마우스 이동 커서가 움직일 때 mouseenter 요소 진입 요소 안으로 들어올 때 (버블링 없음) mouseover 요소 진입 요소 안으로 들어올 때 (버블링 있음) mouseleave 요소 이탈 요소 밖으로 나갈 때 (버블링 없음) mouseout 요소 이탈 요소 밖으로 나갈 때 (버블링 있음) 2. 키보드 이벤트 이벤트 타입 설명 특징 keydown 키 누름 키를 누르는 순간 keyup 키 해제 키를 떼는 순간 keypress 키 입력 문자 키 입력 시 (폐지됨) 3. 폼 이벤트 이벤트 타입 설명 submit 폼 제출 reset 폼 초기화 input 입력값 변경 change 값 변경 완료 focus 포커스 획득 blur 포커스 상실 4. 문서/윈도우 이벤트 이벤트 타입 설명 load 페이지 로드 완료 DOMContentLoaded DOM 로드 완료 resize 윈도우 크기 변경 scroll 스크롤 beforeunload 페이지 이탈 전 이벤트 핸들러 등록 방법 1. 인라인 이벤트 핸들러 (비권장) <button onclick="handleClick()">클릭하세요</button> <script> function handleClick() { console.log('버튼이 클릭되었습니다!'); } </script> 단점: ...

July 14, 2025 · 7 min · Haeun

JSON 완벽 가이드 - 웹 개발 필수 데이터 포맷 정복하기

JSON 완벽 가이드 - 웹 개발의 핵심 데이터 포맷 웹 개발을 하다 보면 정말 자주 마주치는 게 JSON이다. API 통신할 때도, 설정 파일 만들 때도, 데이터 저장할 때도 JSON을 쓴다. 하지만 정작 JSON이 뭔지, 어떻게 제대로 활용하는지 모르는 경우가 많다. 오늘은 JSON의 기본부터 실제 활용까지 한번에 정리해보겠다. JSON이 뭔가? **JSON(JavaScript Object Notation)**은 클라이언트와 서버 간 데이터 교환을 위한 텍스트 기반 포맷이다. 이름에 JavaScript가 들어가지만 대부분의 프로그래밍 언어에서 사용할 수 있다. ...

July 11, 2025 · 3 min · Haeun

디바운스 vs 쓰로틀 완벽 비교 가이드 - 언제 어떤 것을 사용할까?

디바운스 vs 쓰로틀 완벽 비교 가이드 - 언제 어떤 것을 사용할까? 이번에는 **디바운스(Debounce)**와 **쓰로틀(Throttle)**의 차이점을 명확히 이해하고, 실전에서 언제 어떤 것을 사용해야 하는지 알아보겠습니다. 핵심 차이점 특징 디바운스 쓰로틀 실행 시점 마지막 이벤트 후 일정 시간 지난 후 일정 시간마다 한 번씩 실행 횟수 그룹의 마지막에 1번만 주기적으로 여러 번 적합한 상황 입력 완료 후 실행 실시간 반응 필요 시각적 비교 디바운스 (Debounce) 이벤트: |--|--|--|--|--|--|--|--|--|--| 실행: | 연속된 이벤트를 그룹화하여 마지막에 한 번만 실행 쓰로틀 (Throttle) 이벤트: |--|--|--|--|--|--|--|--|--|--| 실행: | | | | | | 일정 주기마다 한 번씩 실행 언제 어떤 것을 사용할까? 디바운스 사용 시기 검색창 자동완성: 사용자가 타이핑을 멈춘 후 검색 폼 자동 저장: 입력 완료 후 저장 윈도우 리사이즈: 크기 조정 완료 후 레이아웃 재계산 쓰로틀 사용 시기 스크롤 이벤트: 무한 스크롤, 고정 헤더 게임 캐릭터 이동: 마우스/키보드 입력 처리 실시간 차트 업데이트: 주기적인 데이터 갱신 실전 예제: 검색 vs 스크롤 // 디바운스: 검색창 (입력 완료 후 실행) const searchInput = document.getElementById('search'); searchInput.addEventListener('input', debounce((e) => { fetchSearchResults(e.target.value); }, 300)); // 쓰로틀: 스크롤 (주기적 실행) window.addEventListener('scroll', throttle(() => { updateHeaderPosition(); }, 100)); 고급 활용: React Hook // 커스텀 디바운스 Hook function useDebounce(value, delay) { const [debouncedValue, setDebouncedValue] = useState(value); useEffect(() => { const handler = setTimeout(() => { setDebouncedValue(value); }, delay); return () => { clearTimeout(handler); }; }, [value, delay]); return debouncedValue; } // 커스텀 쓰로틀 Hook function useThrottle(callback, delay) { const lastRun = useRef(Date.now()); return useCallback((...args) => { if (Date.now() - lastRun.current >= delay) { callback(...args); lastRun.current = Date.now(); } }, [callback, delay]); } // 사용 예시 function SearchComponent() { const [query, setQuery] = useState(''); const debouncedQuery = useDebounce(query, 300); useEffect(() => { if (debouncedQuery) { fetchSearchResults(debouncedQuery); } }, [debouncedQuery]); return ( <input value={query} onChange={(e) => setQuery(e.target.value)} placeholder="검색어를 입력하세요" /> ); } 성능 최적화 팁 1. 적절한 딜레이 설정 // 검색: 300ms (사용자가 타이핑을 멈출 시간) const searchDebounce = debounce(searchFunction, 300); // 스크롤: 100ms (부드러운 반응) const scrollThrottle = throttle(scrollFunction, 100); // 리사이즈: 500ms (레이아웃 재계산 비용 고려) const resizeDebounce = debounce(resizeFunction, 500); 2. 메모리 누수 방지 // 컴포넌트 언마운트 시 정리 useEffect(() => { const debouncedHandler = debounce(handleInput, 300); input.addEventListener('input', debouncedHandler); return () => { input.removeEventListener('input', debouncedHandler); }; }, []); 3. 조건부 적용 // 네트워크 상태에 따른 동적 조정 function adaptiveDebounce(callback, baseDelay = 300) { const isSlowConnection = navigator.connection?.effectiveType === 'slow-2g'; const delay = isSlowConnection ? baseDelay * 2 : baseDelay; return debounce(callback, delay); } 실제 프로젝트 적용 사례 1. 쇼핑몰 검색 기능 class SearchManager { constructor() { this.searchInput = document.getElementById('search'); this.resultsContainer = document.getElementById('results'); this.setupEventListeners(); } setupEventListeners() { // 디바운스: 검색 요청 최적화 this.searchInput.addEventListener('input', debounce((e) => { this.performSearch(e.target.value); }, 300)); // 쓰로틀: 검색 결과 스크롤 this.resultsContainer.addEventListener('scroll', throttle(() => { this.handleInfiniteScroll(); }, 200)); } async performSearch(query) { if (query.length < 2) return; try { const results = await fetch(`/api/search?q=${query}`); this.displayResults(await results.json()); } catch (error) { console.error('검색 실패:', error); } } handleInfiniteScroll() { const { scrollTop, scrollHeight, clientHeight } = this.resultsContainer; if (scrollTop + clientHeight >= scrollHeight - 100) { this.loadMoreResults(); } } } 2. 대시보드 실시간 업데이트 class DashboardManager { constructor() { this.charts = document.querySelectorAll('.chart'); this.setupRealTimeUpdates(); } setupRealTimeUpdates() { // 쓰로틀: 차트 업데이트 (실시간성 유지) window.addEventListener('resize', throttle(() => { this.resizeCharts(); }, 100)); // 디바운스: 설정 저장 (입력 완료 후) this.setupAutoSave(); } setupAutoSave() { const settingsForm = document.getElementById('settings'); settingsForm.addEventListener('change', debounce(() => { this.saveSettings(); }, 1000)); } async saveSettings() { const formData = new FormData(settingsForm); try { await fetch('/api/settings', { method: 'POST', body: formData }); console.log('설정 저장 완료'); } catch (error) { console.error('설정 저장 실패:', error); } } } 디버깅 팁 1. 실행 횟수 모니터링 function createMonitoredDebounce(callback, delay) { let callCount = 0; const debouncedFunction = debounce((...args) => { callCount++; console.log(`디바운스 실행 횟수: ${callCount}`); callback(...args); }, delay); return debouncedFunction; } function createMonitoredThrottle(callback, delay) { let callCount = 0; const throttledFunction = throttle((...args) => { callCount++; console.log(`쓰로틀 실행 횟수: ${callCount}`); callback(...args); }, delay); return throttledFunction; } 2. 성능 측정 function measurePerformance(func, name) { return function (...args) { const start = performance.now(); const result = func.apply(this, args); const end = performance.now(); console.log(`${name} 실행 시간: ${end - start}ms`); return result; }; } // 사용 예시 const optimizedSearch = measurePerformance( debounce(searchFunction, 300), '디바운스 검색' ); 핵심 포인트: 디바운스: 입력 완료 후 실행 (검색, 저장) 쓰로틀: 주기적 실행 (스크롤, 실시간 업데이트) 성능 최적화: 적절한 딜레이 설정과 메모리 관리 실전 적용: 프로젝트 특성에 맞는 패턴 선택

July 9, 2025 · 4 min · Haeun

쓰로틀(Throttle) 완벽 가이드 - 스크롤/이벤트 최적화의 핵심

쓰로틀(Throttle) 완벽 가이드 - 스크롤/이벤트 최적화의 핵심 안녕하세요! 개발에서 자주 쓰이는 쓰로틀(Throttle) 패턴에 대해 알아보겠습니다. 쓰로틀이란? 쓰로틀은 짧은 시간 간격으로 연속해서 발생하는 이벤트를 일정 시간마다 한 번씩만 실행되도록 제한하는 기법입니다. 언제 사용할까? 스크롤 이벤트 최적화 (무한 스크롤, 고정 헤더 등) 윈도우 리사이즈 이벤트 최적화 마우스 이동, 드래그 등 고빈도 이벤트 처리 동작 원리 이벤트가 발생하면, 지정한 시간(delay) 동안 추가 이벤트를 무시합니다. delay가 지난 후 다시 이벤트가 발생하면 콜백이 실행됩니다. 이 과정을 반복하여, 최대 1초에 한 번 등으로 호출 빈도를 제한할 수 있습니다. 실전 예제: 스크롤 이벤트 최적화 <div class="container" style="width:300px;height:300px;overflow:scroll;background:#eee;"> <div style="height:1000px;"></div> </div> <div>일반 이벤트 핸들러 호출 횟수: <span id="normal-count">0</span></div> <div>쓰로틀 이벤트 핸들러 호출 횟수: <span id="throttle-count">0</span></div> <script> const container = document.querySelector('.container'); const normalCount = document.getElementById('normal-count'); const throttleCount = document.getElementById('throttle-count'); // 쓰로틀 함수 구현 function throttle(callback, delay) { let waiting = false; return function (...args) { if (!waiting) { callback.apply(this, args); waiting = true; setTimeout(() => { waiting = false; }, delay); } }; } let normal = 0; let throttled = 0; // 일반 이벤트 (매번 실행) container.addEventListener('scroll', () => { normalCount.textContent = ++normal; }); // 쓰로틀 적용 (최대 1초에 한 번만 실행) container.addEventListener('scroll', throttle(() => { throttleCount.textContent = ++throttled; }, 1000)); </script> 설명: ...

July 6, 2025 · 2 min · Haeun

리액트를 써야 할까? 개발자의 기술 선택에 대해

“리액트? 써봤지. 근데 SPA 구조가 영 맘에 안 들더라고. 오히려 개발이 더 복잡해지는 느낌이야.” 정말 강력한 광역도발이 아닐 수 없습니다. 최근 대부분의 웹 프론트 개발은 vue, react, svelte 와 같은 라이브러리와 프레임워크들을 사용합니다. 이러한 상황은 이미 5년 가량 지속되어 왔기 때문에, 어떤 신입 개발자들은 순수 HTML / CSS / JS 만으로는 서비스를 아예 만들어본적이 없다고 말하는 경우도 있었습니다. 우리는 왜 특정 기술을 선택하는 걸까요? 트렌드라서? 아니면 정말 필요해서? ...

October 27, 2024 · 4 min · Haeun