요구사항
- 검색 키워드를 입력하면 추천 검색어 API를 이용해 추천 검색어 보여주세요.
- 검색어 입력 후엔 엔터키 등 별도의 추가 액션이 없어도 검색어 목록을 보여줘야 해요.
- 키보드, 마우스로 추천 검색어를 선택할 수 있게 해주세요.
- esc를 누르면 검색창이 닫힙니다.
- 키보드 위, 아래를 누르면 추천 검색어 하이라이트가 옮겨지고 엔터를 누르면 하이라이트 처리된 검색어가 반영됩니다.
- 마우스로는 클릭한 검색어가 반영됩니다.
- 검색된 결과에 따라 고양이 사진이 화면에 렌더링 되어야 합니다.

HTML 구조를 어떻게 짜야할지 고민해야 나중에 혼선이 없음.
요점 정리
동작 원리
- 키보드를 누르면
onKeywordInput
함수를 호출하여 api를 request함.
- 이후 setState로 현재 상태 + 키워드를 nextState로 전달함.
- 현재 상태에 있는 keywords로
suggestKeywords
함수를 호출함.
suggestKeywords
에서onKeywordSelect
를 호출함.
onKeywordSelect
는 keyword를 매개변수로 받아 내부 함수의 setState를 그림.
- SuggestKeyword.js는 innerHTML로 검색 결과를 보여줌.
state의 변화가 필요한 모든 곳에
setState
의 상태값을 변경해주어야 함.검색 결과 키보드로 조작하기
- App.js의 suggestKeywords 컴포넌트 initalState에
cursor
를 추가함. 기본값은 -1.
- window의
addEventListener
로 keydown이 일어나면 숫자를 가감하며 cursor의 위치를 조절함.
- Enter를 누르면 cursor의 위치한 키워드를
onKeywordSelect
의 파라미터로 전달, 함수 호출함.
window.addEventListener("keydown", (e) => { if ($suggest.style.display !== "none") { //arrow down, arrow up, enter를 입력했을 때 각각 동작해야 함. const { key } = e; if (key === "ArrowDown") { const nextCursor = this.state.cursor + 1; this.setState({ ...this.state, cursor: nextCursor > this.state.keywords.length - 1 ? 0 : nextCursor, }); } else if (key === "ArrowUp") { const nextCursor = this.state.cursor - 1; this.setState({ ...this.state, cursor: nextCursor < 0 ? this.state.keywords.length - 1 : nextCursor, }); } else if (key === "Enter") { onKeywordSelect(this.state.keywords[this.state.cursor]); } } });
검색 결과 그리기
- 키워드 선택시 추천 검색어 지우기 → fetch 함수 끝에
keywords: []
추가
- 검색을 하면(엔터, 추천 검색어 클릭)
fetchCatsImage
함수 호출.
fetchCatsImage
는 api를 호출해 data를 받아온 후 해당 데이터로 setState를 함.
- 만약 data가 제대로 들어왔다면 searchResult 컴포넌트에 해당 image data를 파라미터로 setState를 전달함.
- searchResult.js에선 받은 state로 innerHTML을 이용해 이미지를 그림.
debounce - 사용자 검색 지연
- 사용자의 오타를 대비함.
- App.js에서 header 컴포넌트의
onKeywordInput
함수 통채로 debounce를 적용함.
export default function debounce(fn, delay) { let timer = null; return function () { const context = this; const args = arguments; clearTimeout(timer); timer = setTimeout(() => { fn.apply(context, args); }, delay); }; }