쿼리 스트링은 URL의 한 부분으로서, 요청하고자 하는 URL에 부가적인 정보를 포함하고 싶을 때 사용한다.
URL에 따라서 다양한 응답을 제공해주는 백엔드 API도 마찬가지로 /signin API를 호출하면 로그인에 대한 응답을, /products API를 호출하면 상품들에 대한 정보를, /product API를 호출하면 단일 상품에 대한 정보를 응답해주는 것처럼 특정 리소스에 대한 정보를 얻고자 한다는 단순한 형태의 요청만 URL을 통해서 할 수 있었다.
이런 상황에서 우리는 단순히 “상품 리스트를 보여줘”가 아니라 “상품 리스트를 최신순으로 상위 10개만 보여줘” 처럼 구체적으로 요청을 할 수 있어야 하는데 이때 활용할 수 있는 것이 쿼리 스트링입니다.

Query String의 형태(출처: https://www.semrush.com/)
쿼리 스트링은 이름 그대로 문자열의 형태를 띠고 있으며 key=value로 표현된다.
또한 URL의 일부분이기 때문에 여기서부터는 쿼리 스트링이 시작된다고 표시가 되어야 하며 ?를 통해서 이를 표현한다.
쿼리 스트링을 포함해서 Routing을 할 때도 특별히 다른 방법을 통해서 할 필요는 없다.
<Link to="/list?sort=popular" />, navigate("/list?sort=popular")처럼
단순하게 Link 컴포넌트나 navigate 함수의 인자에 쿼리 스트링이 포함된 URL을 전달해주면 된다.
react-router-dom에서는 쿼리 스트링의 값을 편하게 가져올 수 있는 hook 들을 제공해준다.
이렇게 대표적인 두가지가 있는데 활용하기 더 좋은 useSearchParams 에 대해 알아보자
react-router-dom에서는 쿼리 스트링의 값을 이용해서 URLSearchParams 객체를 리턴해주는 useSearchParams라는 hook을 제공해 주고 있다.
const [searchParams, setSearchParams] = useSearchParams();
페이지 네이션을 만들어보자.
그냥 요청을 하면 전체 포스트 데이터를 응답해 주지만,_start , _limit 두 개의 쿼리 스트링을 이용해서 요청을 보내면 _start에 적힌 숫자 이후의 포스트부터 _limit에 적힌 개수만큼의 포스트만 응답해 준다.
import React, { useEffect, useState } from 'react';
import { useSearchParams } from 'react-router-dom';
const List = () => {
const [searchParams, setSearchParams] = useSearchParams();
searchParams.get() 메서드를 통해서 쿼리 스트링에서 offset과 limit의 값을 각각 offset, limit 변수에 저장한다.import React, { useEffect, useState } from 'react';
import { useSearchParams } from 'react-router-dom';
const List = () => {
const [searchParams, setSearchParams] = useSearchParams();
const offset = searchParams.get('offset');
const limit = searchParams.get('limit');
그냥 fetch를 하는 것이 아니라.url에 변수를 지정해서 불어온다.
또한 의존성 배열에 offset과 limit를 넣어서 둘이 값이 변경될 때마다 useEffect가 실행되게 한다.
import React, { useEffect, useState } from 'react';
import { useSearchParams } from 'react-router-dom';
const List = () => {
const [searchParams, setSearchParams] = useSearchParams();
const offset = searchParams.get('offset');
const limit = searchParams.get('limit');
const [posts, setPosts] = useState([]);
useEffect(() => {
fetch(
`https://jsonplaceholder.typicode.com/posts?_start=${offset}&_limit=${limit}`
.then((response) => response.json())
.then((result) => setPosts(result));
}, [offset, limit]);
이로써 /list?offset=0&limit=10으로 유저가 접속했을 때 첫 번째 포스트부터 10개의 포스트를 보여줄 수 있다.
import React, { useEffect, useState } from 'react';
import { useSearchParams } from 'react-router-dom';
const List = () => {
const [searchParams, setSearchParams] = useSearchParams();
const offset = searchParams.get('offset');
const limit = searchParams.get('limit');
const [posts, setPosts] = useState([]);
useEffect(() => {
fetch(
`https://jsonplaceholder.typicode.com/posts?_start=${offset}&_limit=${limit}`
)
.then((response) => response.json())
.then((result) => setPosts(result));
}, [offset, limit]);
return (
<section>
<h1>This is Posts</h1>
{posts.map(
(
{ id, title }
) => (
<article key={id}>
<p>
<div>id:{id}</div>
<div>title:{title}</div>
</p>
</article>
)
)}
</section>
);
};
<button>1</button>
<button>2</button>
<button>3</button>
1페이지 버튼을 누르면 1~10번 포스트를, 2페이지 버튼을 누르면 11~20번 포스트를, 3페이지 버튼을 누르면 21~30번 포스트를 보여주도록 구현
const movePage = (pageNumber) => {
// 1
searchParams.set('offset', (pageNumber - 1) * 10);
setSearchParams(searchParams);
};
<button onClick={() => movePage(1)}>1</button>
<button onClick={() => movePage(2)}>2</button>
<button onClick={() => movePage(3)}>3</button>