Velog의 검색 디자인을 좀 따랐다.
Query Selector를 이용하여 검색을 하였다.
Tag같은 경우는 String[] 형식으로 되어있기에 배열을 String으로 전환후 검색하였다.
const postList = await this.posts
.createQueryBuilder('post')
.innerJoinAndSelect('post.user', 'user')
.orderBy('post.createdAt', 'DESC')
.where('post.title ILIKE :search', { search: `%${search}%` })
.orWhere(`array_to_string(post.tags,',') ILIKE :search`, {
search: `%${search}%`,
})
.orWhere('post.text ILIKE :search', { search: `%${search}%` })
.skip(skipFrom)
.take(9)
.getMany();
우선 Velog의 디자인을 따랐기 때문에, 검색창에 검색 버튼이 없다. 따라서 onChange할 때 마다 Input의 value를 검색해 줘야 하는데, 예를 들어 서울을 검색할 경우
이렇게 총 5번의 검색이 이뤄진다. 만약 더 긴 글자를 입력하면 더 많은 API를 요청할 것이다. 이를 해결하기 위해 디바운싱을 적용하였다.
디바운싱과 쓰로틀링은 짝궁처럼 같이 다니는 개념인것 같다.
디바운싱 : 연속적으로 호출되는 함수들중 가장 마지막 함수만 실행되게 하는 것
쓰로틀링 : 함수가 호출된 후 일정 시간 동안 함수가 호출되지 않게 하는 것
여기서는 가장 마지막 VALUE를 검색해 줘야 하므로 디바운싱을 적용해주었다.
NPM throttle-debounce 모듈
https://www.npmjs.com/package/throttle-debounce
을 적용했다.
여기서 주의할 점은 React가 debounce나 throttle을 사용할 때, 함수가 바뀌지 않도록 주의해야 한다.
const onInputChanged = debounce(searchTerm, 500);
이렇게 만들어 놓으면 컴포넌트라 리렌더링 될때마다 debounce를 새로 만들어서 그냥 500ms뒤에 실행하게 된다.
const onInputChanged = useMemo(() => debounce(searchTerm, 500), []);
useMemo를 이용하여 리렌더 되더라도 사라지지않게 해야 정상적으로 적용이 된다.
검색하는 컨테이너엔 이전에 해준것 처럼 Infinite Scroll을 적용해 주었다.