자바스크립트
검색어를 입력할 때마다 바로 검색을 실행하면 서버 자원이 낭비될 수 있습니다. 이를 방지하기 위해 다음과 같은 방법을 사용할 수 있습니다:
- 디바운싱(Debouncing): 사용자가 입력을 멈춘 후 일정 시간 동안 대기한 다음 검색 요청을 보내는 방법입니다.
- 스로틀링(Throttling): 일정 시간 간격으로만 검색 요청을 보내는 방법입니다.
일반적으로 디바운싱이 검색에 더 자주 사용됩니다. 이는 사용자가 입력을 멈춘 후에만 검색 요청을 보내기 때문입니다.
디바운싱을 사용한 예제
아래는 JavaScript와 디바운싱을 사용하여 검색 요청을 효율적으로 처리하는 예제입니다
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Search with Debouncing</title> </head> <body> <input type="text" id="searchInput" placeholder="Search..."> <div id="results"></div> <script> // 디바운스 함수 function debounce(func, delay) { let debounceTimer; return function(...args) { clearTimeout(debounceTimer); debounceTimer = setTimeout(() => func.apply(this, args), delay); }; } // 검색 함수 async function search(query) { if (!query) return; try { const response = await fetch(`/search?q=${encodeURIComponent(query)}`); const data = await response.json(); displayResults(data); } catch (error) { console.error('Error fetching search results:', error); } } // 결과 표시 함수 function displayResults(data) { const resultsDiv = document.getElementById('results'); resultsDiv.innerHTML = ''; data.results.forEach(result => { const resultItem = document.createElement('div'); resultItem.textContent = result; resultsDiv.appendChild(resultItem); }); } // 디바운스를 적용한 검색 함수 const debouncedSearch = debounce(search, 300); // 입력 이벤트 리스너 document.getElementById('searchInput').addEventListener('input', (event) => { debouncedSearch(event.target.value); }); </script> </body> </html>
설명
- 디바운스 함수: debounce 함수는 입력이 멈춘 후 일정 시간(delay) 동안 대기한 다음에 지정된 함수를 실행합니다. 입력이 계속되면 타이머를 재설정합니다.
- 검색 함수: search 함수는 실제로 검색 요청을 서버에 보냅니다.
- 결과 표시 함수: displayResults 함수는 서버에서 받은 검색 결과를 화면에 표시합니다.
- 디바운스를 적용한 검색 함수: debouncedSearch는 debounce 함수를 사용하여 search 함수에 디바운싱을 적용한 것입니다.
- 입력 이벤트 리스너: input 이벤트가 발생할 때마다 debouncedSearch 함수를 호출합니다.
이 방법을 사용하면 사용자가 입력을 멈춘 후에만 검색 요청이 발생하므로, 불필요한 서버 요청을 줄일 수 있습니다.
리액트-
하드처리
리액트에서 검색어 입력 시 디바운싱을 적용하여 서버 자원 낭비를 줄이는 방법을 설명하겠습니다. 이 예제에서는 useState와 useEffect 훅을 사용하여 상태를 관리하고, useCallback 훅을 사용하여 디바운스 함수를 생성합니다.
React 컴포넌트 예제
아래는 디바운싱을 적용한 React 컴포넌트의 예제입니다:
import React, { useState, useEffect, useCallback } from 'react'; function debounce(func, wait) { let timeout; return function(...args) { const context = this; clearTimeout(timeout); timeout = setTimeout(() => func.apply(context, args), wait); }; } const SearchComponent = () => { const [query, setQuery] = useState(''); const [results, setResults] = useState([]); // 디바운스된 검색 함수 const debouncedSearch = useCallback( debounce(async (searchQuery) => { if (!searchQuery) return; try { const response = await fetch(`/search?q=${encodeURIComponent(searchQuery)}`); const data = await response.json(); setResults(data.results); } catch (error) { console.error('Error fetching search results:', error); } }, 300), // 300ms 디바운스 시간 [] ); // query 상태가 변경될 때마다 debouncedSearch 호출 useEffect(() => { debouncedSearch(query); }, [query, debouncedSearch]); // 입력 핸들러 const handleInputChange = (event) => { setQuery(event.target.value); }; return ( <div> <input type="text" value={query} onChange={handleInputChange} placeholder="Search..." /> <div> {results.map((result, index) => ( <div key={index}>{result}</div> ))} </div> </div> ); }; export default SearchComponent;
설명
- 디바운스 함수: debounce 함수는 입력이 멈춘 후 일정 시간(wait)이 지난 다음에 주어진 함수를 호출합니다. useCallback을 사용하여 메모이제이션된 디바운스 함수를 생성합니다.
- 상태 관리: query와 results 상태를 관리합니다. query는 입력된 검색어를, results는 검색 결과를 저장합니다.
- 검색 함수: debouncedSearch 함수는 디바운싱을 적용하여 서버에 검색 요청을 보냅니다.
- useEffect 훅: query 상태가 변경될 때마다 debouncedSearch 함수를 호출합니다. debouncedSearch 함수는 디바운싱을 적용하여 서버 요청을 최적화합니다.
- 입력 핸들러: handleInputChange 함수는 입력값이 변경될 때마다 query 상태를 업데이트합니다.
- 렌더링: input 태그와 검색 결과를 표시하는 div 태그를 렌더링합니다. results 배열을 맵핑하여 각 결과를 화면에 출력합니다.
이 방법을 사용하면 검색어 입력 시 디바운싱을 적용하여 서버 요청 빈도를 줄이고, 효율적인 검색 기능을 구현할 수 있습니다.
리액트 - lodash 이용
React와 TypeScript에서 lodash의 debounce 함수를 사용하여 디바운싱을 구현하는 코드를 다시 정리하겠습니다.
1. lodash 설치
먼저, lodash와 해당 타입 정의 파일을 설치합니다. 프로젝트 루트 디렉토리에서 다음 명령어를 실행합니다:
npm install lodash npm install @types/lodash npm install --save-dev @types/lodash
2. React 컴포넌트 코드
updateItem 함수에 디바운스를 적용한 전체 코드는 다음과 같습니다:
import React, { useCallback, useEffect, useState } from "react"; import "./App.css"; import Todo, { ItemType } from "./components/Todo"; import { Container, List, Paper } from "@mui/material"; import AddTodo from "./components/AddTodo"; import Call from "./service/ApiService"; import { debounce } from "lodash"; const DUMMY_DATA = [ { todoId: "0", title: "Todo 컴포넌트 만들기", done: false, }, { todoId: "1", title: "Todo 컴포넌트 만들기2", done: false, }, { todoId: "2", title: "Todo 컴포넌트 만들기3", done: false, }, ]; const App: React.FC = () => { const [items, setItems] = useState<ItemType[]>([]); const [errorMessage, setErrorMessage] = useState<string>(""); const requestOptions = { method: "GET", headers: { "Content-Type": "application/json", }, }; useEffect(() => { const fetchData = async () => { try { const response = await fetch( "http://localhost:5000/api/todo", requestOptions ); if (response.ok) { const res = await response.json(); setItems(res.data); } else { console.error("Failed to fetch data"); } } catch (error) { console.error(" 에러 :", error); setErrorMessage("데이터를 가져오지 못했습니다."); } }; fetchData(); }, []); // 추가 처리 const addItem = (item: ItemType) => { Call("/todo", "POST", item).then((response) => { setItems(response.data); }); }; // 삭제 처리 const deleteItem = (item: ItemType) => { Call("/todo", "DELETE", item).then((response) => { setItems(response.data); }); }; // 디바운스 적용된 수정 처리 함수 const debouncedUpdateItem = useCallback( debounce((item: ItemType) => { Call("/todo", "PUT", item).then((response) => { setItems(response.data); }); }, 300), [] ); const updateItem = (item: ItemType) => { debouncedUpdateItem(item); }; let todoItems; if (items && items.length > 0) { todoItems = ( <Paper className="mt50"> <List> {items.map((item) => ( <Todo item={item} key={item.todoId} deleteItem={deleteItem} updateItem={updateItem} /> ))} </List> </Paper> ); } else if (errorMessage) { todoItems = <h3 className="mt50">{errorMessage}</h3>; } return ( <div className="App"> <Container maxWidth="md" className="mt5"> <AddTodo addItem={addItem} /> <div className="TodoList">{todoItems}</div> </Container> </div> ); }; export default App;
설명
- lodash 설치: lodash와 해당 타입 정의 파일을 설치합니다.
- debounce 가져오기: import { debounce } from "lodash"; 구문을 사용하여 lodash에서 debounce 함수를 가져옵니다.
- 디바운스 적용된 수정 처리 함수: debouncedUpdateItem 함수는 debounce를 사용하여 생성됩니다. 이 함수는 300ms의 지연 시간을 가지며, 호출이 연속적으로 발생해도 마지막 호출 후 300ms가 지나야 실행됩니다.
- updateItem 함수: updateItem 함수는 debouncedUpdateItem을 호출하여 디바운스된 함수를 실행합니다.
- useCallback 사용: debouncedUpdateItem 함수는 useCallback을 사용하여 메모이제이션됩니다. 이렇게 하면 함수가 재생성되지 않고, 컴포넌트가 리렌더링되더라도 동일한 함수 인스턴스를 유지합니다.
- 렌더링: items 배열을 맵핑하여 각 Todo 컴포넌트를 렌더링합니다. Todo 컴포넌트는 item, deleteItem, updateItem props를 받습니다.
이 코드를 통해 updateItem 함수 호출 시 디바운싱이 적용되어 불필요한 서버 요청을 줄일 수 있습니다.
댓글 ( 0)
댓글 남기기