[기업협업]Day7. useSearchParams로 필터링하기

Joah·2022년 7월 26일
0

기업협업

목록 보기
7/15

매장, 플랫폼 버튼에 따른 Marker 표시하기

페이지 상단에는 매장 전체보기, A매장, B매장, C매장 버튼이 있다.

header 바로 아래 왼쪽에는 전체, 배달의 민족, 요기요 버튼이 있다.

각각의 버튼을 클릭했을 때, A매장에 해당하는 정보가 지도에 그려지게 필터링을 한다.

useSearchParams를 활용하여 Query String의 형태로 서버에 GET 요청을 한다.


서버가 요청때 맞춰달라는 Query String 형식


⛳ filter를 위한 hook 따로 생성하기

useFilter.js

import { useSearchParams, useLocation } from 'react-router-dom';

const useFilter = () => {
  const location = useLocation();
  //url 정보를 가져오기 위함
  
  const [searchParams, setSearchParams] = useSearchParams();
  //주소창에 삽입할 쿼리 스트링을 searchParams에 저장

  const sortStore = storeName => {
    if (location.search.includes('application')) {
      const application = searchParams.get('application');
      setSearchParams({ store: `${storeName}`, application });
    } else {
      setSearchParams({ store: `${storeName}` });
      //key는 쿼리 스트링에서 ? 다음, value 는 = 이후
      //즉, ?store=${storeName}으로 url이 설정된다.
    }
  };

  /*if문을 작성하여 사용자가 배달 플랫폼을 클릭한 상태 or 클릭하지 않은 상태에서의 
  url 설정을 다르게 해야하기 때문*/

  const showAllStoreData = () => {
    location.search.includes('store', 'application') &&
      searchParams.delete('store', 'application');
    setSearchParams(searchParams);
  };

  //전제 매장 보기 버튼을 클릭하면 모든 쿼리스트링이 지워지게 설정

  return { sortStore, showAllStoreData };
};

export default useFilter;

⛳ 매장 필터링

Main.js

import React, { useEffect, useState } from 'react';
import { useLocation } from 'react-router-dom';
import Header from '../Header/Header';
import Map from '../Map/Map';
import './Main.scss';

const Main = () => {
  const location = useLocation();

  const [dongData, setDongData] = useState([]);

  const url = location.search;

  useEffect(() => {
    fetch(`http://서버/regions${url}`)
      .then(res => res.json())
      .then(data => {
        setDongData(data.result);
      });
  }, [url]);
  
  if (dongData.length === 0) return;
  return (
    <>
      <Header dongData={dongData} url={url} />
      <Map />
    </>
  );
};

export default Main;
  • 최상위 컴포넌트인 Main.js 에서 데이터를 넘겨주기 위해 fetch 함수를 작성한다.

  • 서버에서 Query String으로 데이터를 GET 방식으로 요청하라고 했기에 url 즉, location.search을 그대로 보낸다.

  • location.search에 대한 자세한 내용은 여기를 참고!

  • 받은 데이터는 그대로 dongData state에 저장하여 Header.js, Map.js에 넘겨준다.


📢 Blocker!!

해당 문제는 [탐나BnB] 프로젝트에서도 겪었다.
local Storage에 토큰이 있다면 로그인 버튼을 사용자 이미지로 변경.
이때 업데이트 되는 state가 없기 때문에 UI가 변경되지 않아 강제로 새로고침을 했었다.(Nav바여서 Router에 속하지 않았음)

비슷한 문제가 발생했다

초기화면에서 매장, 플랫폼, 전체버튼을 2번 눌러야 지도에 알맞게 마커가 표시된다. 첫번째 눌렀을 때 주소창에는 해당하는 Query String이 데이터를 잘 요청하고 있었다. dongData에도 버튼에 따른 정확한 데이터가 저장되었다. 문제는 UI가 변경되지 않는다. 의존성 배열에 dongData를 명시해도 비동기적으로 동작하기 때문에 문제를 해결하지 못했다.

📢 해결!!

과정을 다시 생각하자

버튼 클릭 ▶️ url 변경 ▶️ 데이터 요청 ▶️ 화면에 구현

의존성 배열에 변수 url을 작성한다.

간단하게 url이 변경될 때마다 fetch 함수를 요청하는 것이기 때문이다. 그럼 의존성 배열에 url을 삽입하면 url이 변경될 때마다 즉, 버튼이 클릭될 때마다 데이터를 요청하고 어차피 dongData에 변경된 데이터가 담기기 때문에 state는 자동으로 업데이트 된다. 그럼 UI도 변경될 것이다.


Header.js

import React from 'react';
import './Header.scss';
import useFilter from '../../hooks/useFilter';

const Header = ({ dongData }) => {
  const { applications, stores } = dongData[0];
  //매장, 플랫폼 데이터를 구조분해할당하여 사용하기 편하게!
  
  const { sortStore, sortApplication, showAllAppData, showAllStoreData } =
    useFilter();
  //useFilter hook 사용하기


  return (
    <>
      <header className="header">
        <section className="headerContext">
          <div className="logo">
            <div className="goToMain">
              <img className="logoImage" src="/images/logo.png" alt="logo" />
            </div>
          </div>
          <div className="storesButtons">
            <button className="storeBtn" onClick={showAllStoreData}>
    		//useFilter에서 미리 정의한 함수 onClick으로 사용
              매장 전체 보기
            </button>
			//매장 버튼 map 함수 활용하여 각각에 맞는 데이터 할당
            {stores.map(data => (
              <button
                className="storeBtn"
                onClick={() => sortStore(data.name)}
                //클릭되는 버튼 데이터에서 name은 매장의 이름
                //매장이름이 searchParams에 의해 그대로 query string에 삽입
                key={data.id}
              >
                {data.name}
              </button>
            ))}
          </div>
        </section>
      </header>
      <header className="platformHeader">
        <div className="btnContainer">
          <button className="btnBox" onClick={showAllAppData}>
            <span className="btnText">전체</span>
          </button>
          {applications.map(input => (
            <button
              className="btnBox"
              key={input.id}
              onClick={() => sortApplication(input.name)}
			  //매장 버튼과 마찬가지로 플랫폼 버튼에도 플랫폼 이름
            >
              <span className="btnText">{input.name}</span>
            </button>
          ))}
        </div>
      </header>
    </>
  );
};

export default Header;

순서정리!

  1. 매장 버튼을 클릭하면 onClick 함수로 url을 변경한다.
  2. 변경된 url의 Query String을 가져온다.
  3. url이라는 변수에 Query String을 담아서 서버에 데이터 요청한다.
  4. useEffect에 작성하여 url이 변경될 때마다 fetch 함수를 호출한다.
  5. 즉, 버튼을 누르면 url이 변경되고 변경되면 그에 따른 데이터를 가져와 저장한다.

Main.js에서 <Map />dongData를 넘겨주었기 때문에 Marker에서 dongData에 담긴 값만 지도에 나타낸다.


⛳ 결과물

초기화면

A매장클릭

B매장클릭

C매장클릭

배달의 민족 클릭

요기요 클릭

매장과 플랫폼 버튼 클릭 시


⛳ Long Story Short

블로커를 해결하는 일은 짜릿하다. 블로커를 만나면 왜 안되지???를 수천번 반복한다.

오! 이거 해볼까? 저거 해볼까? 시도도 하고 검색도 해보는 과정에서 정말 많은 지식이 쌓이게 된다. 블로커는 마냥 스트레스 주는 존재가 아니라 개발 능력을 한 단계 성장시켜준다.

이미 함수와 메서드의 기능, 역할을 알고 있음에도 단순한 해결법이 있어도 해당 로직을 하루종일 보고 있으면 그 간단한게 보이지 않는다. 그 구멍에서 빠져나와 좀 더 넓게 볼 필요가 있다. 이렇게 간단한 문제를 왜 못보고 있었지? 라는 순간이 너무 많다. 하지만 그만큼 집중했다는 뜻이니깐 성장했다고 생각하자.

블로커를 해결하고 화면에 딱 나타나는 순간은 경험해본 사람은 다 알 것이다.

profile
Front-end Developer

0개의 댓글