Path parameter 활용법

정적 라우팅

지금까지 라우터를 아래와 같은 방식으로 작성했다. 그런데 이렇게 해서는 정해진 경우에 대해서만 경로를 표현할 수 있었다! 그 말인즉슨, Login 컴포넌트를 보여주려면 내가 직접 “/login”이라는 주소를 설정해주고 다른 페이지에서 Login으로 이동할 때, navigate(”/login”)과 같은 형태로 지정된 path를 써줘야 한다는 것이다. 이것을 바로 정적라우팅(static)이라고 한다.

function Router() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/login" element={<Login />} />
        <Route path="/" element={<Main />} />
        <Route path="/product-list" element={<ProductList />} />
        <Route path="/product-detail" element={<ProductDetail />} />
      </Routes>
    </BrowserRouter>
  );
}

그렇다면 수많은 페이지들의 경로를 다 Route로 일일이 지정해줘야하는가??

당연히 그렇지 않다! 상품전체페이지가 있고, 상품 상세페이지가 있다고 할 때, 상세페이지를 클릭했다고 해보자. 무엇인보이는가?

1) 보이는 컴포넌트는 같고 그에 따라 들어오는 데이터만 다른 것을 볼 수 있다.

2) url 끝에 들어오는 번호만 다르다.

  • url 을 살펴보면 url 마지막에 특정 id 값이 들어가고(/32692/53424), 해당 id 값에 따라 서로 다른 상세 페이지 정보가 화면에 그려지는 것을 볼 수 있습니다. id 값에 따라 무수히 많은 url 이 나타날 것이고, 각각의 모든 url 에 대해 미리 경로의 형태와 갯수를 결정할 수 없게 됩니다.
  • 즉, URL에 들어갈 id를 변수처럼 다뤄야 할 필요성이 생긴 것입니다.
  • 이처럼 정적이지 않은, 동적일 수 있는 경로에 대하여 라우팅을 하는 것을 동적 라우팅(Dynamic Routing)이라고 부릅니다.
  • 이는 다음 두 가지 개념(Path ParameterQuery Parameter)을 통해 적용해볼 수 있습니다.

2-1. Path parameter

내가 전체페이지(카드 여러개가 나열되어 있음)에서 라는 컴포넌트를 클릭해서 페이지로 이동하고 싶다고 해보자. 어떻게 해야 하는가?

function Card(props) {
  const navigate = useNavigate();
  const goToDetail = () => {  //(2)
    navigate(`/monsters/detail/${props.id}`);
  };

  console.log(props);

  return (
    <div className="cardContainer" onClick={goToDetail}> // (1)
      <img
        src={`https://robohash.org/${props.id}?set=set2&size=180x180`}
        alt=""
      />
      <h2>{props.name}</h2>
      <p>{props.email}</p>
    </div>
  );
}

(1) 이벤트가 걸려야 하는 곳에 onClick 함수를 걸어준다.

(2) onClick에 내가 보여주고 싶은 곳으로 navigate를 연결해준다. 이 때 props.id를 넘겨줘야 한다.

여기가까지 보면 이제 클릭 시에 url이 바뀌고 있는 것을 알 수 있다. 그렇지만, 내가 route에서 별도로 지정해 주지 않기 때문에 아무런 페이지도 나오지 않는다. 그렇다면 이제 route를 연결해줘야 한다.

<Route path="/monsters" element={<UrlParameters />} />
<Route path="/monsters/detail/:monsterId" element={<MonsterDetail />} /> (3)

(3) 내가 카드 클릭시에 /monsters/detail/${props.id}라는 url로 이동하라고 했기 때문에 props.id를 받을 부분을 route에 추가해 줘야 한다. 따라서 detail/뒤에 :monsterId 라고 지정해주어 보여줄 컴포넌트와 연결을 해줘야 한다.

자 이제, 몬스터 컴포넌트와 연결을 해주었다! 그러나, 내가 원하는!! 내가 클릭한 몬스터의 정보가 나오지 않는다! 당연하다, 왜냐! 내가 해당 몬스터의 정보를 불러오지 않았기 때문이다! 이제 내가 url에 넘겨주었던 monsterId를 통해 detail페이지에서 해당 몬스터의 정보를 불러오자.

const [monster, setMonster] = useState({});
const params = useParams();  // (4)
useEffect(() => {
    fetch(`https://jsonplaceholder.typicode.com/users/${params.monsterId}`)//(5)
      .then((res) => res.json())
      .then((res) => setMonster(res));
  }, [monster]);

(4) useParams를 사용하면 route에 넣어줬던 객체를 받아올 수 있다. useParams를 위와 같이 작성해주고, 콘솔에 params를 찍어보자. console.log(params) 를 찍었을 때, 콘솔 창에 monsterId : 1과 같은 값이 나오는 것을 볼 수 있다.

(5) 따라서 params.monsterId를 fetch에 넣어준다.

여기가지 하면 완벽하게 화면에 렌더링 될 것 같지만, 그렇지 않다! **매우 중요한 포인트인데, useEffect의 실행순서에 따라, 화면이 렌더링이 될 때 무조건 한 번 실행된다! 그렇기 때문에 위에까지만 실행하고 보면 화면이 한번 이전 데이터를 보여줬다가 내가 원하는 데이터를 보여주기 위해 깜빡! 하는 것을 볼 수 있다. 따라서 && 연산자를 이용해 이 부분에 대해 데이터가 있을 때에만 렌더링 되도록 처리를 해준다.

return (
    <div className="urlParameters">
      {monster.name && ( (6)
        <>
          <div className="btnWrapper">
            <button onClick={goToMonsterList}>Back to Monsters List</button>
          </div>
          <Card name={monster.name} email={monster.email} id={monster.id} />
          <div className="btnWrapper">
            <button onClick={goToPreviousMonster}>Previous</button>
            <button onClick={goToNextMonster}>Next</button>
          </div>
        </>
      )}
    </div>
  );

(6) && 연산자를 이용해 반드시 조건부렌더링 처리를 해줘야 한다.

해 주지 않을 경우, 만약 데이터 타입이 다르다면 아예 화면이 렌더링 되지 않는 수가 있다 ^_ㅠ

좋은 웹페이지 즐겨찾기