디벨로픽의 기존 검색 기능은 검색 결과에 대해 인기순, 최신순 두 가지의 정렬 옵션을 선택할 수 있었다.
그런데 인기순 정렬의 경우, 어쩔 수 없이 오래전에 작성한 게시글일 수록 인기도(누적 좋아요+조회수)가 높을 수 밖에 없는 상황이 발생한다면, 최신 글은 인기순 정렬에서 순위가 낮게 노출되어 유저가 원하는 검색 결과를 쉽게 얻지 못하는 문제가 나타날 수 있다고 생각했다.
사실 벨로그의 블로그 서비스를 잘 이용하면서도 검색 기능에서 기간 별로 검색 결과를 보는 것이 불가능한 데에 불편함을 느껴 이같은 옵션 기능이 있으면 좋겠다고 생각했고, 이 기능이 있는 Behance나 다른 블로그 서비스에서 편리함과 유용함을 경험했기 때문에 우리 프로젝트에서 꼭 구현해보고 싶었다.
따라서 인기순 정렬의 옵션으로 특정 기간을 필터링하여 그 기간에 해당하는 검색 결과를 출력하는 기능을 추가하기로 했다.
SearchPost.tsx
// 정렬 옵션 데이터 설정
export const sortOptionData = [
{ name: '인기순', value: 'popular' },
{ name: '최신순', value: 'recent' },
];
// 기간 필터링 옵션 데이터 설정
export const dateOptionData = [
{ name: '모든 기간', value: 'all' },
{ name: '최근 24시간', value: 'day' },
{ name: '최근 1주일', value: 'week' },
{ name: '최근 1개월', value: 'month' },
];
export default function SearchPost(): JSX.Element {
const { pageData, getSearchListDispatch, hasMore } = useList();
const [currentSort, setCurrentSort] = useState(sortOptionData[0]); // 정렬 기본값: 인기순
const [currentDate, setCurrentDate] = useState(dateOptionData[0]); // 기간 기본값: 모든 기간
const { query } = useRouter();
// 쿼리 키워드가 입력되면, 검색결과를 얻어오는 기능 디스패치 실행, 이때 전달하는 객체값으로 sort 옵션이 들어간다.
useEffect(() => {
if (query.keyword) {
getSearchListDispatch({
query: query.keyword,
sort: currentSort.value as 'popular' | 'recent',
type: 'post',
term: currentDate.value as 'all' | 'day' | 'week' | 'month',
limit: 12,
offset: page * 12,
});
}
}, [query, currentSort, currentDate, getSearchListDispatch, page]);
return (
<SearchPageWithNavLayout>
<SearchContentBox>
<div className="sort-option">
<SortOption
sortOptionData={sortOptionData}
setCurrentSort={setCurrentSort}
currentSort={currentSort}
/> // SortOption 컴포넌트로 정렬 옵션 데이터를 넘겨준다. 인기순, 최신순 정렬 옵션을 출력하는 컴포넌트가 된다.
{currentSort.value === sortOptionData[0].value && (
<SortOption
sortOptionData={dateOptionData}
setCurrentSort={setCurrentDate}
currentSort={currentDate}
/> // 현재 검색 정렬 옵션 값이 인기순이라면, 추가 SortOption 컴포넌트를 출력하고 기간 필터링 옵션 데이터를 넘겨준다. 기간 필터링 옵션을 출력하는 컴포넌트가 된다.
)}
</div>
{(pageData as SearchPageDataType).post &&
(pageData as SearchPageDataType).post.length >= 1 && (
<>
<SearchResultCount
searchTitle={SearchNavData[0].name}
resultCount={(pageData as SearchPageDataType).post.length}
/>
<SearchPostCardList />
<FetchMoreTrigger />
</>
)}
</SearchContentBox>
</SearchPageWithNavLayout>
);
}
먼저 정렬 옵션 데이터와 기간 필터링 옵션 데이터를 설정해주었다. 그리고 이 데이터를 바탕으로 현재 선택된 정렬 옵션과 현재 선택된 기간을 변화시킬 수 있는 각각의 상태를 만들어준다. 검색어가 입력되어 query.keyword
값이 존재하는 경우, useEffect
로 검색 결과를 받아오는 디스패치를 실행하는데 이때 전달해야 하는 값으로 정렬과 기간 옵션을 추가로 넣어주었다.
다음으로는 SortOption
컴포넌트로 정렬 옵션 데이터를 넘겨준다. 인기순, 최신순 정렬 옵션을 출력하며 클릭으로 선택 가능한 컴포넌트를 만들어준다.
또 현재 검색 정렬 옵션 값이 인기순일 경우, 추가 SortOption
컴포넌트를 출력하도록 하고 기간 필터링 옵션 데이터를 넘겨준다. 기간 필터링 옵션을 보여주고 선택할 수 있는 컴포넌트가 된다.
SortOption.tsx
type SortOptionPropsType = {
sortOptionData: { name: string; value: string }[];
setCurrentSort: React.Dispatch<React.SetStateAction<{ name: string; value: string }>>;
currentSort: { name: string; value: string };
};
export default function SortOption({
sortOptionData,
setCurrentSort,
currentSort,
}: SortOptionPropsType): JSX.Element {
const sortDropdownRef = useRef<HTMLDivElement>(null);
const [isSortActive, setIsSortActive] = useOutsideClick(sortDropdownRef, false);
const onClickSortBtn = useCallback(() => {
setIsSortActive(prev => !prev);
}, [setIsSortActive]);
return (
<SearhSortOptionContainer>
<div className="option__btn" onClick={onClickSortBtn}>
<span>{currentSort.name}</span>
<MdKeyboardArrowDown />
</div>
<div
className={`sort__dropdown dropdown ${isSortActive ? 'sort-active' : ''}`}
ref={sortDropdownRef}
>
<ul>
{sortOptionData.map(sortOption => (
<li
key={sortOption.value}
onClick={() => setCurrentSort(sortOption)}
className={`${
currentSort.value === sortOption.value ? 'item-active' : ' '
}`}
>
<span>{sortOption.name}</span>
</li>
))}
</ul>
</div>
</SearhSortOptionContainer>
);
}
SortOption
컴포넌트는 넘어온 정렬 옵션이나 기간 필터링 옵션을 출력하고, 클릭으로 상태를 변경시킬 수 있는 공통 컴포넌트다. 먼저 넘겨 받은 currentSort.name
값으로 옵션명을 보여준다. 마찬가지로 넘겨받은 sortOptionData
로 map()
을 실행해 옵션 리스트를 만든 뒤, onClick
이벤트 발생 시 현재 클릭된 옵션 상태로 업데이트 해주는 함수를 실행한다.
업데이트한 검색 옵션 기능은 인기순 정렬 선택 시, 원하는 기간 별로 필터링을 설정해 검색 결과를 탐색할 수 있다.
사실 이전까지는 검색 기능에 대해 깊게 생각해본 적이 없었다. 내게 검색이란 데이터와 사용자가 있는 사이트라면 으레 갖춰야 할 필수 기능 정도였다. 하지만 이번에 검색 기능을 구현하면서 내가 겪었던 검색 경험을 떠올리고, 여러 사이트를 분석해보면서 엄청난 잠재 능력을 가진 녀석이구나! 라는 생각이 들었다.
검색 결과의 질은 사이트의 가치를 판단하는 기준이 될 수 있다. 또한 사용자에게 더 나은 검색 경험을 제공하는 일은 결국 서비스의 비지니스 임팩트에도 영향을 미칠 수밖에 없을 것이다. 이에 관한 개인적인 경험 하나를 꺼내보자면, 패션 쇼핑몰 앱으로 유명한 브랜디와 지그재그를 함께 사용했던 적이 있었다. 두 앱 모두 많은 패션 아이템과 쇼핑몰을 노출하며, 혹할 만한 이벤트 푸시 알림을 자주 보내고, 여성 사용자들이 선호하는 광고 모델을 잘 선정했다. 마켓팅 측면에서 느낀 경험은 비슷했다는 얘기다. 그런데 나는 결국 지그재그의 단골 사용자가 되었다. 나의 발걸음을 잡은 건 바로 지그재그의 '비슷한 상품 이미지 검색 기능'이었다. 이 기능은 내가 보고 있는 상품의 이미지를 분석해 같거나 유사한 상품 목록을 보여줌으로써 해당 상품에 대한 좀 더 많은 사용자의 후기를 볼 수 있고, 합리적인 가격 비교를 가능하게 해주었기 때문이다.
물론 검색 기능이 사용자의 발걸음을 붙잡는 요인의 전부가 될 순 없지만 우리 서비스에서 데이터를 수집하는 게 첫 번째 단계라면, 수집한 데이터를 잘 제공하는 것은 그 다음으로 중요하게 생각해야 할 단계가 아닐까 생각했다. 아직은 작은 업데이트에 불과하더라도 검색으로 더 의미있는 가치를 얻을 수 있는 방향을 계속 고민해보고 싶다.