- 현재 URL의 쿼리 파라미터를 읽기 쉽게 객체로 변환
- 특정 쿼리를 업데이트하면서 URL을 갱신 (router.push or router.replace)
- 모든 쿼리를 초기화 (resetSearchParams)
코드
import { useSearchParams as useNextSearchParams, usePathname, useRouter } from 'next/navigation';
import { useCallback } from 'react';
interface UpdateSearchParamsOption {
replace?: boolean;
}
export const useSearchParams = () => {
const router = useRouter();
const pathname = usePathname();
const searchParams = useNextSearchParams();
const setSearchParams = useCallback(
(params: Record<string, string | string[]>, options: UpdateSearchParamsOption) => {
const search = new URLSearchParams(searchParams);
for (const [key, value] of Object.entries(params)) {
search.set(key, Array.isArray(value) ? value.join(',') : value);
}
for (const key of Array.from(search.keys())) {
if (search.get(key) === 'undefined' || search.get(key) === null || search.get(key) === '') {
search.delete(key);
}
}
const url = `${pathname}?${search.toString()}`;
if (options.replace) {
router.replace(url);
} else {
router.push(url);
}
},
[router, pathname, searchParams]
);
const resetSearchParams = useCallback(() => {
router.push(pathname);
}, [router, pathname]);
return { searchParams: Object.fromEntries(searchParams), setSearchParams, resetSearchParams } as const;
};
useNextSearchParams() — 쿼리 읽기 전용 훅
Next.js의 useSearchParams()는 현재 URL의 쿼리스트링을 ReadonlyURLSearchParams 형태로 반환합니다.
const searchParams = useNextSearchParams();
console.log(searchParams.get('page')); // "1"
- 하지만 읽기 전용이라 set()이나 delete() 같은 메서드는 사용할 수 없습니다.
- 따라서 수정 가능한 복사본을 만들어야 합니다
new URLSearchParams(searchParams) — 복제해서 수정 가능한 버전 만들기
const search = new URLSearchParams(searchParams);
- 이 코드는 현재 쿼리 상태를 그대로 복사하여 편집 가능한 새로운 객체를 만듭니다.
- 이제 아래처럼 자유롭게 수정할 수 있습니다.
search.set('page', '2');
search.delete('keyword');
쿼리 업데이트 로직 (setSearchParams)
for (const [key, value] of Object.entries(params)) {
search.set(key, Array.isArray(value) ? value.join(',') : value);
}
- params는 { key: value } 형태의 객체입니다.
- 값이 배열이면 join(',')으로 묶어서 "a,b,c" 형태로 저장합니다.
- 기존에 같은 key가 있으면 덮어씁니다.
예시:
setSearchParams({ page: '1', tags: ['ui', 'react'] })
// 결과 → ?page=1&tags=ui,react
불필요한 값 정리
for (const key of Array.from(search.keys())) {
if (search.get(key) === 'undefined' || search.get(key) === null || search.get(key) === '') {
search.delete(key);
}
}
- 빈 문자열(''), 'undefined', null 값은 URL에 남겨둘 필요가 없으므로 자동으로 삭제합니다.
- 결과적으로 ?q= 같은 쓸모없는 쿼리가 깔끔하게 제거됩니다.
URL 문자열 조합
const url = `${pathname}?${search.toString()}`;
- pathname은 현재 경로(/works 등)
- search.toString()은 내부 쿼리 데이터를 직렬화하여 'page=1&genre=romance' 형태의 문자열을 반환합니다.
즉, 최종적으로 /works?page=1&genre=romance&keyword=love 같은 URL이 만들어집니다.
push vs replace
if (options.replace) {
router.replace(url);
} else {
router.push(url);
}
- router.push() : 브라우저 히스토리에 추가 (뒤로 가기 가능)
- router.replace() : 현재 기록을 대체 (뒤로 가도 이전 쿼리로 돌아가지 않음)
검색어 입력처럼 기록을 남길 필요가 없는 경우엔 replace,
페이지 이동처럼 뒤로 가기 기능이 필요한 경우엔 push를 사용하는 게 일반적입니다.
쿼리 초기화 (resetSearchParams)
const resetSearchParams = useCallback(() => {
router.push(pathname);
}, [router, pathname]);
- 모든 쿼리를 삭제하고 현재 경로만 남깁니다.
- 즉, /works?page=1&genre=romance → /works
객체 형태로 반환하기
return { searchParams: Object.fromEntries(searchParams), setSearchParams, resetSearchParams } as const;
Object.fromEntries(searchParams)
URLSearchParams는 [key, value] 쌍의 iterable입니다.
이를 일반 객체로 변환하면, 코드에서 훨씬 쉽게 접근할 수 있습니다.
Object.fromEntries(searchParams);
// { page: '1', genre: 'romance', keyword: 'love' }
이제 다음처럼 접근 가능
searchParams.page // "1"
searchParams.genre // "romance"
사용 예시
'use client';
import { useSearchParams } from '@/hooks/useSearchParams';
export default function WorkList() {
const { searchParams, setSearchParams, resetSearchParams } = useSearchParams();
const onChangePage = (page: number) => {
setSearchParams({ page: String(page) }, { replace: false });
};
const onSearch = (keyword: string) => {
setSearchParams({ keyword }, { replace: true });
};
return (
<>
<div>현재 페이지: {searchParams.page ?? '1'}</div>
<button onClick={() => onChangePage(2)}>2페이지로 이동</button>
<button onClick={() => onSearch('love')}>검색</button>
<button onClick={resetSearchParams}>초기화</button>
</>
);
}
URL 동작 예시
/works?page=2
/works?keyword=love
/works
'개발 공부 > React' 카테고리의 다른 글
| Next.js Route Handler (0) | 2025.11.22 |
|---|---|
| Clerk 인증 정보와 Prisma User 테이블 동기화(sync) (0) | 2025.11.06 |
| custom modal 만들기 (0) | 2025.10.21 |
| React App Router, Page Route (0) | 2025.10.19 |
| Server Components, Client Components (0) | 2025.09.03 |