Code
코드는 링크의 샌드박스에서 확인하실 수 있습니다.
Background
nextjs 에서 pathParam 과 queryParam 값에 접근 하고자 한다. URI는 다음과 같다.
http://doamin/examples/params/1?queryKey=queryKeyValue
pathParam과 queryParam에 접근하는 코드는 다음과 같다.
export const UseQueryParamPage:React.FC<Props> = () => {
const router = useRouter();
const queryValue = router.query.queryKey;
const id = router.query.id;
return <div tw='flex justify-center items-center h-screen' >
<div css={tw`text-center m-auto`}>id: {id}<br />queryKey2: {queryKey2}</div>
</div>
}
pathParam 인 id
와 queryParam 인 queryValue
는 아래와 같은 타입으로 추론된다.
pathParam
의 경우 명시적으로 url 에 포함되어야 하고, SSR을 사용할 경우 router.isReady 상태와 무관하게 router.query
에는 pathParam
값이 담겨 있다.
queryParam
의 경우 queryParam
의 사용 용도에 따라 string | undefined
, string[]| undefiend
, string | string[] | undefined
로 사용될 수 있다.
Problem
pathParam
값이 undefined
가 아님이 보장됨에도 불구하고 router.query
의 값이 string | string [] | undefined
으로 추론되기 때문에 type narrowing을 위해 불필요한 분기문이 생긴다.
export const UseQueryParamPage:React.FC<Props> = () => {
const router = useRouter();
const id = router.query.id;
const queryValue = router.query.queryKey;
if (!isString(id) || !isArray(queryValue)) {
return null
}
return <div tw='flex justify-center items-center h-screen' >
<div css={tw`text-center m-auto`}>id: {id}<br />queryKey2: {queryValue}</div>
</div>
}
NextJs 의 타입은
router.isReady
가false
될때, pathParam이undefined
가 될 수 있는 점을 고려하여string | string [] | undefined
타입으로 추론된다.
Solution
목표는 다음과 같다.
각 queryKey가 특정 타입으로 추론될 것이 확실한 경우 각 key에 맞는 타입으로 추론되게 한다.
usePathParam
import { useRouter } from 'next/router';
/**
* @description In the case of pathParam, there is no chance of it being undefined,
* so we can cast it to a string type.
*/
export type ParamKeys = 'id'|'id2';
export const usePathParam = (paramKey: ParamKeys) => {
const { query } = useRouter();
return query[paramKey] as string;
};
pathParam
의 경우 string
타입이 보장되므로 string
으로 타입 캐스팅해준다.
usePathParam
을 사용할 땐 위와 같이 key가 suggestion 된다.
pathParam
인 id
는 string
으로 추론되고 불필요한 분기 코드를 삭제할 수 있다.
useQueryParam
export type UrlQueryParams = {
'queryKey'?: stirng[];
};
export const useQueryParam = <T extends keyof UrlQueryParams>(queryKey: T) => {
const { query } = useRouter();
return (query as UrlQueryParams)[queryKey];
};
queryParam
의 경우 각 key에 따라 리턴타입이 달라져야 한다. 따라서 generic(T
) 을 써서 각 key 에 따라 미리 설정한 타입이 추론되도록 한다.
useQueryParam
을 사용할 때 위와 같이 key가 suggest 된다.
queryValue
는 UrlQueryParams
에서 정의한 대로 stirng[]
으로 추론된다.
결과적으로 불필요한 분기문을 삭제하고, 의도한대로 타입을 추론하여 사용할 수 있다.
Caveat
usePathParam
, useQueryParam
은 query 의 타입이 확실하게 보장될 때 사용할 수 있다. 타입이 확실 보장되지 않는 경우에 위 hook의 사용은 런타임 오류로 이어진다. query 타입이 보장되지 않는 상황은 다음과 같다.
페이지에 getServerSideProps 또는 getInitialProps가 없는 경우 Next.js는 페이지를 정적 HTML로 prerendering 하여 page를 statically optimize
한다. prerendering 이 진행되는 시점에는 router query
object 가 비어있게 된다. 따라서 prerendering 시점에 router.query.queryKey
는 undefined 가 될 수 있다. 따라서 이때는 router.isReady
상태를 확인하여 true 가 되었을 때 비어있는 queryKey가 업데이트 됬음을 보장받아야 한다. 보다 자세한 내용은 Next.js 의 문서를 참고하자.