쇼핑몰에서 하는 것처럼 검색어나 필터를 입히면 query param이 추가되도록 하고 싶었다. 나중에 query param을 통해 정보를 불러올 것이므로 이 방법을 쓰는 것이 적합할 것 같았다.
그 전에 next의 `usePathname`과 `useSearchParams`에서 필요한 부분만 살펴보고 가려고 한다.
`usePathname()`
먼저 쿼리 스트링이 입력된 경우 pathname이 어떻게 출력될지 궁금했다.
`http://localhost:3000/mypage?myKey=hihi` 이와 같은 경로에서 `usePathname`을 통해서 경로를 가져오면 어떤 값을 줄까? 쿼리 스트링은 모두 제거한 path만 깔끔하게 반환해준다.
'use client'
import { usePathname } from "next/navigation";
const Page = () => {
const pathname = usePathname();
console.log(pathname); // '/mypage'
return (
...
)
};
`useSearchParams()`
이제 쿼리 스트링이 입력된 경우 해당 내용들을 key-value 쌍으로 가져와보자.
자주 사용할만한 메서드들을 next 공식 문서에서 소개하고 있다. 내 생각에는 `.has()`과 `.get()`을 많이 쓸 것 같다. `.size`라는 속성도 있는데, 필요한 쿼리 파라미터 개수가 정해져 있다면 size를 통해 빠르게 확인할 수 있을 것 같다.
메서드 이름에서 알 수 있듯이, `.has('myKey')`라고 하면 myKey가 쿼리 파라미터로 입력되었는지 확인할 수 있도록 boolean을 반환해준다. `.get('myKey')`라고 하면 myKey가 어떤 값과 대응을 이루는지 알려준다. 이때 myKey가 쿼리 파라미터로 입력되지 않았으면 `null`을 반환하고 다른 경우에는 대응된 값을 반환한다. `?myKey=`과 같은 경우에는 빈 문자열 `''`을 반환하고, 반환 값들은 모두 문자열이다.
'use client'
import { useSearchParams } from "next/navigation";
const Page = () => {
const searchParams = useSearchParams();
console.log(searchParams.size); // 1
console.log(searchParams.has('myKey')); // true
console.log(searchParams.get('myKey')); // 'hihi'
console.log(searchParams.get('keykey')); // null
return (
...
)
};
`useRouter()`
이제 검색 조건을 쿼리 파라미터로 넣어보자.
먼저 `.push()` 메서드를 사용하면 `/mypage`일 때 새로운 파라미터들이 잘 추가된다. 그런데 이미 기존 검색 조건을 바꿔서 입력하면 새로 추가된 조건은 잘 추가되지만, 변경된 내용은 반영되지 않았다. 즉 `myKey=123`이 있는데 `myKey=321`로 바꿔서 추가하기위해 `.push('${pathname}?myKey=321')`을 하게 되면 여전히 `myKey=123`으로 남아있었다.
이를 해결하기 위해 `.replace()` 메서드를 사용했다. 이름에서처럼 기존에 입력된 것이 있으면 대체해서 쿼리 파라미터를 입력해준다. `${pathname}?myKey=123`을 `.replace(' ${pathname}?myKey=321')로 바꿔주면 `myKey=321`로 변경된다.
윗 내용은 dev로 실행했을 때 실행이 느려서 발생했던 오류 같다. `.replace()`를 쓰면 현재 route가 해당 route로 대체돼서 뒤로가기를 누르면 그 전전route로 변경된다. 검색 내용과 별개로 검색 페이지만 유지하고 뒤로가기를 누르면 검색 페이지 밖으로 가고 싶으면 `.replace()`를 사용하면 된다.
이렇게 `useRouter()`를 통해 넣어준 쿼리 파라미터를 받아오려면 `useSearchParams()`를 사용하면 된다.
따라서 위 과정을 적용해서 대략적인 코드를 작성해보면 아래와 같다.
"use client";
import { usePathname, useRouter } from "next/navigation";
const SearchForm = (...) => {
const router = useRouter();
const pathname = usePathname();
...
const setQueryParams = (params: {[key: string] : string}) => {
let newParams = `?brtcCd=${params.brtcCd}&sggCd=${params.sggCd}`;
if (params.addr) {
newParams += `&addr=${params.addr}`
}
if (params.org) {
newParams += `&org=${params.org}`
}
if (params.disease) {
newParams += `&disease=${params.disease}`
}
router.replace(`${pathname}${newParams}`);
return ;
}
return (
<div className="w-full flex flex-col ">
<form className="hospital-search">
<Select>
...
</Select>
<Select>
...
</Select>
<Input
placeholder="도로명/동 주소"
value={addr}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
// 입력값 전후 공백 제거
setAddr(e.target.value.trim());
}}
/>
<Input
placeholder="병원명"
value={org}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
// 입력값 전후 공백 제거
setOrg(e.target.value.trim());
}}
/>
<Button
onClick={(e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
// 입력값 유지하기 위해 preventDefault()
e.preventDefault();
const params = { brtcCd: brtc[0], sggCd: sgg[0], addr, org, disease: '' };
setQueryParams(params)
}}
>
검색
</Button>
</form>
{children}
</div>
);
};
export default SearchForm;
'Frontend > Today I Learned' 카테고리의 다른 글
[React] TanStack Query로 custom hook 만들기 (0) | 2024.10.31 |
---|---|
[CSS] Tailwindcss에 색상 커스터마이징하기 (0) | 2024.10.29 |
[React, TS] Input 이미지 파일 미리보기 (0) | 2024.10.14 |
[Next.js] Link 태그에 버튼을 넣었을 때 페이지 이동 막기 (1) | 2024.10.13 |
[기타] supabase storage 'bucket not found' 에러 (0) | 2024.10.12 |