클라이언트 단 에서 최근 검색어 저장하기!(+ expire date 지나면 자동 삭제)

dev.horang🐯·2023년 3월 10일
1
post-thumbnail

프로젝트를 진행하면서 최근 검색어 최대 50개 7일 지나면 자동삭제 의 로직을 가진 최근 검색어 기능이 필요했다.
최근 검색어 동향을 보면 큰 엔진(네이버, 구글..) 의 경우엔 서버쪽에서 해당 유저의 최근검색어를 가지고 있다가 유저에 맞게 최근검색어를 보여주는 케이스가 대부분이었다. 하지만 이러한 서비스들을 살펴보면 문자 하나하나 변화에 따라 연관검색어가 뜨고 해당 연관 검색어를 클릭하거나 연관 검색어에 없는 단어라면 엔터 혹은 검색 버튼 클릭시 해당 단어가 저장되는 모습을 볼 수 있다.

하지만 내가 진행한 프로젝트의 경우

  1. 문자를 칠 때 마다 화면에 해당 문자와 일치하는 리스트가 보여져야 함 : 일반적인(큰 엔진) 최근 검색어와 다름.
  2. 서버에서 연관검색어를 가져와야 할 만큼 검색 기능의 중요성이 높지 않음

이러한 이유 때문에 프론트 단에서 최근 검색어를 저장하고 보여주기로 했다.
서버를 사용하지 않기 때문에 유저에 따른 리스트를 가져올 수는 없었고 기기의 스토리지에 저장하여 보여주는 방법을 선택했다. ( React native 프로젝트 였기 때문에 asyncStorage를 사용했다.)

검색버튼을 클릭하거나 연관검색어를 클릭해서 저장되는 것이 아니었기 때문에 저장 시점에 대해서 기획자 분과 논의하다가 원하는 리스트의 이름 클릭 시 해당 제목을 저장하기로 했다.

  const [recentlySearch, setRecentlySearch] = useState([]);

//useEffect 로 recentlySearch변화 할 때마다 스토리지에 넣어줌
  useEffect(() => {
    setStorage('recentSearch', recentlySearch);
  }, [recentlySearch]);

const renderItem = ({ item }) => <Item item={item} />;

  const Item = React.memo(function Item({ item }) {
    return (
      <Pressable
        style={styles.item}
        onPress={() => {
          recentlySearch.unshift({ name: item?.name });
        }}
      >
      </Pressable>
    );
  });
...
  <View style={styles.view}>
      <FlatList
              data={listPerPage}
              renderItem={renderItem}
              keyExtractor={(item) => item.id}
              style={{ width: '100%' }}
            />
    </View>

위와 같이 unshift로 배열에 클릭한 아이템의 이름을 가장 상단에 넣었다.
근데 최근 검색어라면 같은걸 검색 시 중복되는 경우 예전의 데이터를 지우고 가장 상단으로 올라와야 하는 조건이 발생했다.
그래서 클릭시 중복값이 있으면 사라지는 Set 함수를 사용해서 해당 문제를 해결했다.

  const Item = React.memo(function Item({ item }) {
    return (
      <Pressable
        style={styles.item}
        onPress={() => {
          recentlySearch.unshift( item?.name );
          setRecentlySearch(new Set(recentlySearch));
        }}
      >
      </Pressable>
    );
  });

이렇게 하면 1차로 원하는 내용을 구현할 수 있었다.

다음 난관은 expire date를 설정하는 부분에 있었다. 서버에서 설정한 만료기한이라면 클라이언트와 관계없이 만료시에 사라지겠지만 클라이언트 단에서 구현한 만료기한은 클라이언트가 구동할 때에만 체크할 수 있다는 점...

일단 만들기 시작했다.

  const today = dayjs();
  const expireDay = today.add(7, 'day');

  const Item = React.memo(function Item({ item }) {
    return (
      <Pressable
        style={styles.item}
        onPress={() => {
            recentlySearch.unshift({ name: item?.name, expireDate: expireDay });
        setRecentlySearch(new Set(recentlySearch));
        }}
      >
      </Pressable>
    );
  });

하지만 이렇게 하고나니 문제가 발생했다. expireDate가 name이 같더라도 다르기 때문에 Set함수가 중복 인식을 하지 못했다... 그래서 reduce 함수로 name 중복시 이전걸 제거시켜주었고

 const today = dayjs();
 const expireDay = today.add(7, 'day');

 const Item = React.memo(function Item({ item }) {
   return (
     <Pressable
       style={styles.item}
       onPress={() => {
           recentlySearch.unshift({ name: item?.name, expireDate: expireDay });
         const a = recentlySearch.reduce((acc, cur) => {
           if (acc.findIndex(({ name }) => name === cur.name) === -1) {
             acc.push(cur);
           }
           return acc;
         }, []);
         // console.log(a, 'save')
         setRecentlySearch(a);
       }}
     >
     </Pressable>
   );
 });

다시한번 원하는 새 배열을 얻었다.
마지막으로 해당 화면을 불러올 때 리스트에서 dayjs함수를 사용해서 expire date가 지나면 삭제 되게 useEffect로 함수를 짜 주었다.

 useEffect(() => {
   const getRecentSearch = async () => {
     await getStorage('recentSearch')
       .then((v) => {
         v === null
           ? null
           : (a = v.filter(
               (ex) => dayjs(ex.expireDate).isAfter(today) == true,
             ));
         // console.log(a, 'a')
         setRecentlySearch(a.slice(0, 15));
       })
       .catch((e) => {});
   };
   getRecentSearch();


 }, []);

프로젝트가 커지게 되면 클라이언트 단에서 최근 검색어 기능을 사용할 일은 드물겠지만 재미가 있었다!

profile
좋아하는걸 배우는건 신나🎵

0개의 댓글