개요

CRA Next.js 로 마이그레이션한 뒤 발생한 ESLint와 TypeScript 오류를 수정하는 첫 번째 작업입니다.


router.query 타입 문제

// workout/[uuid].tsx
//...
 
	const {uuid}  = router.query;
 
//TS2345: Argument of type string | string[] is not assignable to parameter of type string Type string[] is not assignable to type string 
	
	useEffect(()=>{
		//...
		const fetchedWorkout = await requestWorkout(uuid);  
	setWorkout(fetchedWorkout);  
		//...
	},[uuid])

Next.js의 router.query 값이 string | string[]인 이유는 여러 종류의 동적 라우팅을 모두 지원하기 때문입니다.

  • [slug] (기본): /workout/123과 같은 단일 경로 값은 string 이 됩니다.
  • [...slug] (Catch-all Segments): /workout/a/b/c와 같은 여러 경로 값은 string[] (배열)이 됩니다.

문제 해결

타입 단언

// workout/[uuid].tsx
//...
	const {uuid}  = router.query as {uuid:string};

타입 단언을 통해 uuid 의 타입을 개발자가 미리 정해줘 타입스크립트 오류를 잠재울 수 있지만 런타임에 어떤 일이 일어날지 모르니 이 방법은 사용하지 않기로 했습니다.

타입 가드

// workout/[uuid].tsx
//... 
	const rawUuid = router.query.uuid;  
	const uuid = typeof rawUuid === "string" ? rawUuid : undefined;

타입 가드를 통해 uuid가 문자열이 아닌 경우, undefined 에 대입합니다.


useQuery

// workout/[uuid].tsx
//...
	const {data:workout, isLoading, isError} =useQuery({  
	    queryKey: ['workout',uuid],  
	    queryFn: ()=>requestWorkout(uuid),  
	    staleTime: 2*60*1000,  
	    enabled: !!uuid  
	    // 여기에 typeof uuid === "string" 을 해도 될 듯 
	});

useEffect로 백엔드 서버로 부터 데이터를 받아오던 부분도 useQuery 를 이용하도록 바꿉니다. 이 때 enabled 옵션에 !!uuiduuid 가 문자열 일 때만 데이터를 요청할 수 있도록 합니다.


회고

  • router.query 값은 string | string[] | undefined 타입을 가질 수 있음
  • 타입 단언(as { uuid: string })은 간단하지만 안전하지 않음
  • 타입 가드를 통해 uuid가 문자열일 때만 사용하도록 범위를 좁히는 것이 안전한 방법
  • React Query의 useQueryenabled 옵션을 적용하면, uuid가 유효할 때만 서버 요청이 실행되도록 제어할 수 있음
  • useQuery 커스텀 훅으로 감싸서 관리 하는 것이 좋으니 나중에 수정

참고

타입 가드, 단언