Frontend Study - 2/React

React : Path Parameter / Query Parameter 를 이용한 동적 라우팅 & 페이지네이션 (Pagination)

갓데미 2022. 7. 23. 14:24

이전에 라우터를 통해서 경로에 따라 각기 다른 화면을 보여주는 방법에 대해서 배웠었습니다. 

하지만 화면 하나를 구현하기 위해서 일일이 라우터의 패스를 지정해 주어야 한다면 무수히 많은 라우터가 생성되어야 합니다. 

 

이 때 사용할 수 있는 것이 Path Parameter를 이용한 동적라우팅 입니다.

 

 

Path Parameter

 

localhost:3000/product/2
localhost:3000/product/45
localhost:3000/product/125

 

라우트 경로 끝에 각기 다른 id 값들이 들어갑니다. 2, 45, 125.

url 경로에서 달라지는 부분을 저장하는 매개 변수를 Path Parameter 라고 합니다.

 

Path Parameter 는 Router 컴포넌트에서 다음과 같이 정의됩니다.

<BrowserRouter>
  <Routes>
    <Route path='/product/:id' element={<ProductDetail />} />
  </Routes>
</BrowserRouter>
  • : 는 Path Parameter 가 올 것임을 의미합니다.
  • id 는 해당 Path Parameter 의 이름을 의미합니다. 변수 명을 짓듯이, 임의의 이름을 지정해줄 수 있습니다. ex) :productId

 



useNavigate : useNavigate을 통해 url을 변경시킵니다

useParams : useParams 를 통해 url안의 Id값을 받아옵니다

 

 

  • 리스트 페이지의 개별 상품을 클릭 → navigate("/product/1") 로 상세 페이지로 이동합니다.
 (X) onClick={
        navigate(`./detail/${props.id}`);
      }

 (O) onClick={() => {
        navigate(`./detail/${props.id}`);
      }}

+ 컴포넌트에 navigate 온클릭 이벤트를 줄 때 애로우함수를 이용하여 콜백을 실행시켜야 했다. 

+ navigate 경로 지정시 .을 안찍어서 페이지 경로 오류가 났었다. `/detail/${props.id}` --> './detail/${props.id}` 

 

  • 상세 페이지로 이동하면 url은 "http://localhost:3000/product/1" 과 같이 변경되어 있습니다.
  • 페이지에 필요한 데이터를 useEffect 에서 fetching 합니다.
  • 필요한 id는 URL에 존재하므로 useParams().id 에서 가져올 수 있습니다.

+ params.id 를 사용해야 하는데 params자체로 이용하려고 하여 에러가 났다. 

 

  • 해당 id를 가지고 백엔드에서 만들어준 API를 호출합니다.

 

function ProductDetail() {
  const params = useParams();
  
  useEffect(() => {
    const productId = params.id;
    fetch(`http://123.456.789:8000/products/${productId}`) // 1
      .then(res => res.json())
      .then(res => setData(res));
  },[]);

  return (
    ...
  )
}
  • 서버에서 가져온 데이터(res)를 컴포넌트의 data state 에 저장해 줍니다.
  • state 에 담긴 데이터로 컴포넌트 UI 를 render 해줍니다.

 

 

Query Parameter

 

백엔드에서 가지고 있는 데이터는 많고, 그 데이터를 나눠서 보여줘야 할 때 사용됩니다. 일정 길이로 끊어서 데이터를 전달합니다. 

ex) 총 40개의 상품이라면, 1페이지에 20개의 상품, 2페이지에 20개의 상품.

 

프론트엔드에서 현재의 위치(Offset)과 추가로 보여줄 컨텐츠의 수(Limit)를 백엔드에 전달합니다.

백엔드에서는 그에 해당하는 데이터를 끊어 보내주는 방식으로 구현하게 됩니다.

 

이 과정에서 Query Parameter(혹은, Query String, 쿼리 스트링)를 사용하게 됩니다.

쿼리 스트링이란 말 그대로 해당 엔드포인트에 대해 질의문(query)를 보내는 요청을 뜻합니다.

쿼리 파라미터는 페이지네이션, 필터 기능에 자주 쓰입니다.

 

예를 들어, localhost:8000/products?limit=10&offset=5 라는 주소가 있다고 가정해보겠습니다.

API 뒷 부분에 붙어있는 ? 로 시작하는 텍스트가 쿼리스트링 입니다.

?limit=10&offset=5 의 경우, "limit이 10이면서 offset이 5일 경우의 product 페이지를 보여달라" 는 요청으로 해석됩니다.

  • ? 기호는 쿼리스트링의 시작을 알립니다. url 에서 ? 기호는 유일무이 합니다.
  • limit : 한 페이지에 보여줄 데이터 수
  • offset : 데이터가 시작하는 위치(index)
  • parameter=value 로 필요한 파라미터의 값을 적습니다.
  • 파라미터가 여러개일 경우 &를 붙여서 여러개의 파라미터를 넘길 수 있습니다.

Path Parameter 에 대한 정보가 useParams 훅이 반환한 객체 안에 담겨있었듯이,

쿼리스트링에 대한 정보는 useLocation 훅이 반환한 객체의 search 프로퍼티 안에 담겨있습니다.

 

 

useLocation 

function ProductDetail(props) {
  const location = useLocation();
  console.log(location);
  
  return (
    ...
  )
}

useLocation 훅을 실행하면 경로 정보를 담고 있는 객체 를 반환합니다.

위 코드에서 해당 객체를 location 이라는 변수에 할당해 주었습니다. location 변수를 콘솔로 출력해 보면 아래의 로그가 출력 됩니다.

{
  pathname: '/product/1', 
  search: '', 
  hash: '', 
  state: null, 
  key: 'default'
}

여러가지 정보가 있는데, 여기서 주목할 부분은 pathname  search 프로퍼티 입니다.

  • pathname: 현재 경로 값
  • search: 현재 경로의 query parameter 값

 

// current url -> localhost:3000/products?offset=10&limit=10

function ProductList() {
  const location = useLocation();
 
  console.log(location.search) // ?offset=10&limit=10

  return (
      ...
  )
}

이를 통해 url 에서 쿼리스트링 정보를 받아와서, 해당 정보를 통해 데이터를 요청할 수 있습니다.

 

fetch(`${API}${location.search}`)
  .then(res => res.json())
  .then(res => ...)

 

const LIMIT = 20;

export default function Users() {
  const [users, setUsers] = useState([]);
  const navigate = useNavigate();
  const location = useLocation();

  const updateOffset = (indexNumber) => {
    const offset = indexNumber * LIMIT;
    const queryString = `?limit=${LIMIT}&offset=${offset}`;
    navigate(queryString);
  };

  // 데이터 로딩
  useEffect(() => {
    fetch(
      `http://localhost:8000/users${
        location.search || `?limit=${LIMIT}&offset=0`
      }`
    )
      .then((res) => res.json())
      .then((res) => setUsers(res.users));
  }, [location.search]);

  return (
    <div className="photos">
      <h1>Mini Project - Pagination</h1>
      <Buttons updateOffset={updateOffset} />
      <CardList users={users} />
    </div>
  );
}

 

동적 라우팅

  1. 리스트 페이지에서 카드를 클릭 합니다.
  2. url 이동을 합니다. 이때, 카드의 고유한 id 값이 url 에 포함됩니다.
  3. 이동한 페이지에서, url 에 담겨있는 id 값을 useParams 훅을 이용하여 가져옵니다.
  4. 가져온 id 값을 이용하여 데이터를 요청합니다.

페이지네이션

  1. 리스트 페이지에서 페이지 이동 버튼을 클릭 합니다.
  2. url 이동을 합니다. 이때 url 에는 각 버튼에 해당하는 쿼리스트링이 포함됩니다.
  3. 이동한 페이지에서, url 에 담겨있는 쿼리스트링을 useLocation 훅을 이용하여 가져옵니다.
  4. 가져온 쿼리스트링을 이용하여 데이터를 요청합니다.