[week7 클론 코딩 프로젝트] 검색 기능 구현하기

G-NOTE·2022년 8월 27일
0

항해99

목록 보기
33/36

검색 기능 구현 고민

검색 기능 구현 방식에 대해 여러 가지 방식을 고민하게 되었다.

  1. GET 요청으로 검색 대상 데이터를 한번에 불러온 다음에 그 데이터 내부에서 검색창 입력 키워드로 찾는다.
  2. 검색 키워드마다 GET 요청을 보낸다.

검색 키워드마다 GET 요청을 보내면 불필요한 서버 통신이 많지 않을까 해서 1번 방식을 사용했다.
하지만 검색 키워드 데이터가 수만 개가 된다고 가정하면 한번에 수만 개의 데이터를 GET 요청으로 불러와서 그 안에서 검색하는 것보다, 계속해서 입력한 검색어 GET 요청을 보내는게 서버에 덜 부담될 수 있다는 피드백을 받았다.
그래서 다음엔 검색 입력 이벤트를 lodash로 관리하고 GET 요청을 보내봐야겠다는 생각이 들었다.

검색 기능 구현

const SearchBar = () => {
  const [searchText, setSearchText] = useState("");
  const [dataList, setDataList] = useState([]);
  const [contentNum, setContentNum] = useState(0);

  const { posts, hasMore, keyword } = useSelector((state) => state.posts);

  const dispatch = useDispatch();

  useEffect(() => {
    fetchData();
  }, [dispatch, posts]);

  const fetchData = async () => {
    const res = await axios({
      method: "get",
      url: `${BASE_URL}/api/articles/hashtag`,
      headers: {
        "Content-Type": "application/json",
        Authorization: getCookie("mycookie"),
      },
    });
    setDataList(res.data);
  };

  const handleChange = (e) => {
    setSearchText(e.target.value);
    setContentNum(0);
    dataList.map((data) => {
      const text = data.hashtagList.toString();

      // 영문 대소문자 구분 없이 검색 결과 조회
      if (text.toLowerCase().includes(e.target.value.toLowerCase())) {
        // 해시태그 목록에 내가 입력한 검색어가 있으면 해시태그 게시글 개수 카운트 증가
        setContentNum((contentNum) => contentNum + 1);
      }
    });

    e.target.value === "" && __getPosts(1);
  };

  const handleClick = () => {
    dispatch(__getHashtagPost(searchText));
    setContentNum(0);
    setSearchText("");
  };

  return (
    <StSearch>
      <Input
        variant="header"
        text="검색"
        value={searchText}
        onChangeHandler={handleChange}
      />
      {searchText ? (
        <StResult onClick={handleClick}>
          <StHashtag>
            <BsHash size="20px" />
          </StHashtag>
          <StText>
            <StSearchText>{`#${searchText}`}</StSearchText>
            <StNum>{`게시물 ${contentNum}`}</StNum>
          </StText>
        </StResult>
      ) : null}
    </StSearch>
  );
};

export default SearchBar;
  • 미리 게시글 별 해시태그 리스트와 게시글id가 포함된 배열을 GET 요청으로 받고 검색창에 입력한 값을 받아 입력값이 해당 배열에 있는지 여부를 확인했다.
  • 그리고 입력한 해시태그가 포함된 게시글 개수를 표시하고, 클릭하면 해당 해시태그 게시글 목록을 조회할 수 있다.
  • 영문 검색의 경우, 영문 대소문자 구분 없이 검색이 가능하도록 구현했다.

Trouble Shooting

  • 해시태그 검색 결과를 조회할 때 [#여행일기 #여행스타그램] 이런식으로 같은 키워드('여행')가 n번 중복될 경우 서버로부터 같은 게시글이 n번 응답되어 들어오는 문제가 있었다.
  • 백엔드에서도 해결할 방법을 찾고 나도 해결할 방법을 찾고 있었는데 일단 프론트엔드 쪽에서 객체간 비교를 통해서 문제를 해결할 수 있었다.

해결 방법

postSlice.jsx

export const __getHashtagPost = createAsyncThunk(
  "getHashtagPost",
  async (payload, thunkAPI) => {
    try {
      const response = await axios({
        method: "get",
        url: `${BASE_URL}/api/articles/search?hashtag=${payload}`,
        headers: {
          "Content-Type": "application/json",
          Authorization: getCookie("mycookie"),
        },
      });
      return thunkAPI.fulfillWithValue({
        data: response.data,
        keyword: payload,
      });
    } catch (error) {
      return thunkAPI.rejectWithValue(error);
    }
  }
);

// ...

[__getHashtagPost.fulfilled]: (state, { payload }) => {
  state.isLoading = false;
  state.posts = payload.data.filter((val, index, arr) => {
    const jsonArr = arr.map((val) => JSON.stringify(val));
    return jsonArr.indexOf(JSON.stringify(val)) === index;
  });
  state.keyword = payload.keyword;
},
  • 객체는 객체 내 key-value가 일치해도 참조값이 달라 비교할 수 없다.
  • 다라서 배열을 순회하면서 JSON 문자열의 배열로 만들고, 이 배열의 값을 비교하는 방식으로 새로운 배열을 만들어서 return했다.

참조

profile
FE Developer

0개의 댓글