[원티드 프리온보딩 프론트엔드] [3주차 2차 과제] 검색창 구현하기: 검색 로직 뜯어보기

GY·2022년 2월 20일
0

원티드 프리온보딩

목록 보기
10/12
post-thumbnail
post-custom-banner

검색어 입력

  • 검색어를 입력하거나 삭제할 때마다 각기 다른 액션을 발생시켜야 하기 때문에 length를 사용했다.
  • Hangul 라이브러리를 사용해 disassemble 메서드로 각각의 모음과 자음을 분리시켰다. 초성까지만 입력해도 작동하도록 만들기 위해서였다.

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const { value } = e.target;
    const valueLength = Hangul.disassemble(value).length;
    const searchValueLength = Hangul.disassemble(searchValue).length;

    // 입력값을 추가할때
    if (valueLength > searchValueLength) {
      dispatch(filterProducts(value));
    } else if (valueLength < searchValueLength) {
      dispatch(rollbackProducts(value));
    }

    dispatch(filterBrands(value));
    setSearchValue(value);
  };

초기 상태 정의

  • mockData
  • brandList
  • filterList
    • products
    • brands
const initialState = {
  mockdata: [] as searchInfo[],
  brandList: [] as string[],
  filterList: {
    products: [] as searchInfo[],
    brands: [] as string[],
  },
};

여기서 searchInfo타입으로 type assertion을 주었다.
searchInfo 인터페이스를 살펴보면,

그냥 : searchInfo[]로 하면 안되나?

검색어 입력

  • 데이터와 검색어 모두 공백 포함 여부와 상관없이 검색이 되어야 하므로 둘 다 replaceAll을 통해 공백을 없앴다.
  • 또한 영어일 경우 대소문자 구분 없이 검색해야했으므로 toLowerCase로 소문자로 통일했다.
const filterProduct = ({
  targetList,
  searchValue,
}: {
  targetList: searchInfo[];
  searchValue: string;
}) =>
  targetList.filter((item) => {
    const modifiedMockData = item.제품명.replaceAll(' ', '').toLowerCase();
    const modifiedSearchValue = searchValue.replaceAll(' ', '').toLowerCase();

    if (Hangul.search(modifiedMockData, modifiedSearchValue) < 0) {
      return false;
    } else {
      return true;
    }
  });

이 filterProduct함수를 사용해 리듀서를 작성했다.

  • 공백만 입력되었을 경우: 공백의 길이와 상관없이 공백만 입력되었는지를 판별하기 위해 trim()을 사용했다. 공백만 입력되엇을 때는 state의 products를 빈 배열로 만들어주었다.
  • 공백이 아닌 검색어가 입력되었을 때는 다음과 같이 리듀서를 작성했다.
    • isProduct: 검색어를 모두 지우고 다시 입력할 때는 새로 filter한 대상을 다시 전체 데이터로 채워야 한다. 따라서 state의 products에 아무것도 없으면 다시 mockData로 채워주고, 그렇지 않으면 filter된 products를 채운다.
    filterProducts(state, action: PayloadAction<string>) {
      const isEmptyValue = action.payload.trim();
      if (!isEmptyValue) {
        state.filterList.products = [];
      } else {
        const isProduct =
          state.filterList.products.length === 0
            ? state.mockdata
            : state.filterList.products;

        state.filterList.products = filterProduct({
          targetList: isProduct,
          searchValue: action.payload,
        });
      }
    },

검색어 삭제

  • 검색어를 삭제하고 공백만 남았을 경우에는 아무것도 검색되지 않도록 state.filterlist.products에 빈 배열을 넣어준다. 그렇지 않으면 공백을 포함한 모든 제품이 검색되기 때문이다.
  • 다른 문자가 아직 남아있다면 mockdata에서 현재입력한 값을 검색한다.
    rollbackProducts(state, action: PayloadAction<string>) {
      const isEmptyValue = action.payload.trim();
      if (!isEmptyValue) {
        state.filterList.products = [];
      } else {
        state.filterList.products = filterProduct({
          targetList: state.mockdata,
          searchValue: action.payload,
        });
      }
    },

브랜드 명 검색

  • 제품명 검색과 동일한 로직이다. 다만, 팀에서 정한 UI특성상 제품명과 브랜드명을 별도로 상태값을 관리해 겁색해야했기 때문에 별도의 리듀서를 정의했다.
    filterBrands(state, action: PayloadAction<string>) {
      const isEmptyValue = action.payload.trim();
      if (!isEmptyValue) {
        state.filterList.brands = [];
      } else {
        state.filterList.brands = findBrandinBrands({
          brandList: state.brandList,
          searchValue: action.payload,
        });
      }
    },

데이터 호출

목데이터 호출

fetchMockData(state, action: PayloadAction<searchInfo[]>) {
      state.mockdata = action.payload;
    },

브랜드명 별도 호출

    getBrandsList(state) {
      state.brandList = getBrandsInData(state.mockdata);
    },
profile
Why?에서 시작해 How를 찾는 과정을 좋아합니다. 그 고민과 성장의 과정을 꾸준히 기록하고자 합니다.
post-custom-banner

0개의 댓글