[Next.js] Router Query에 값이 없는 이유

yes·2023년 10월 6일
0

조사하게된 배경

UseEffect 내부에서 useRouterquery object의 값을 사용하는 경우가 많았습니다. 예를 들어서, query obejct의 값을 이용해서 mount시에 api 호출을 해야하는 경우입니다. 이러한 상황에서 query object에 원하는 값이 들어오지 않고 undefined가 담겨서 오는 문제가 발생했는데 해결 방법은 알고 있지만, 왜 이렇게 동작하는지에 대해서 deep dive하고 싶어서 조사를 하게 되었습니다.

  • 위에서 말한 해결 방법)

useRouter에서 제공해주는 isReady를 사용

// url - localhost:3000/fe?genius=eunchong

useEffect(() => {
	if(isReady) {
		console.log(query.genius) // eunchong
	}
, [isReady])

isReady: boolean - router fields가 client-side에 업데이트되어 사용할 준비가 되었는지에 대한 여부.

Next.js Automatic Static Optimization

Next.js는 blocking data requirements가 없는 경우 페이지가 static(prerendered가 가능)인지 자동으로 결정합니다. This determination is made by the absence of getServerSideProps and getInitialProps in the page.

blocking data requirements
"Blocking data requirements"는 Next.js에서 페이지가 미리 렌더링되기 전에 가져와야 하는 데이터를 가리키는 개념입니다. 이 데이터가 가져와지는 동안 브라우저가 기다려야 하므로 "blocking"이라는 용어가 사용됩니다. 이러한 데이터 요구사항이 없는 경우 페이지는 정적으로(pre-rendered) 생성될 수 있습니다.
결론적으로, Next.js에서 getServerSidePropsgetInitialProps를 사용하여 server-side에서 데이터를 fetch하는 경우 발생합니다.

만약 page에서 getServerSideProps or getInitialProps를 사용하고 있다면, Next.js는 요청에 따라 페이지를 요청 시 렌더링하도록 전환합니다. (서버 사이드 렌더링을 의미)

만약 위 케이스가 아닌 경우에는 Next.js는 정적으로 생성된 html을 pre-redering 함으로써 statically optimize할 것입니다.

pre-rendering하는 동안에는 router's query object가 비어 있습니다. 이는 pre-rendering 단계에서 query 정보를 제공할 수 없기 때문입니다. 하지만 하이드레이션(hydration) 이후에 Next.js는 애플리케이션을 업데이트하여 query object에 route parameters를 제공합니다.

hydration은 클라이언트 측에서 서버로부터 전달된 HTML 및 자바스크립트 코드를 받아 렌더링된 페이지를 동적으로 만드는 과정을 말합니다. pre-rendring된 페이지는 초기에는 query 정보를 가지고 있지 않기 때문에 query object가 비어 있습니다. 하지만 클라이언트에서 페이지가 로드되고 자바스크립트가 실행될 때, Next.js는 query 정보를 포함한 URL을 읽어와서 페이지를 업데이트합니다. 이렇게 함으로써 route parameter나 query parameter 같은 정보를 사용하여 페이지를 동적으로 조작하거나 업데이트할 수 있습니다.

next build will emit .html files for statically optimized pages. For example, the result for the page pages/about.js would be:

.next/server/pages/about.html

And if you add getServerSideProps to the page, it will then be JavaScript, like so:

.next/server/pages/about.js

결론

getServerSideProps or getInitialProps를 사용하지 않는 정적으로 pre-rendering(SSG)된 페이지는 hydration이 완료되기 이전에는 query에 정보가 담겨서 오지 않습니다. pre-rendering 단계에서는 query 정보를 사용할 수 없기 때문입니다.

직접 테스트

URL : http://localhost:3000/upa/use-case/7

  • getServerSideProps or getInitialProps 사용하지 않은 페이지
    useEffect(() => {
        console.log('test query', query);
    }, [query]);
    • console 결과
      1. {}
      2. {useCaseId: '7'}
    • 처음에는 값이 비어있다가 hydration이 완료되면 값이 생성
  • getServerSideProps or getInitialProps를 사용하지 않은 페이지
    useEffect(() => {
        console.log('test query', query);
    }, [query]);
    
    export const getServerSideProps = () => {
    	//
    }
    • console 결과
      • {useCaseId: '7'}

Next.js 라이브러리 코드로 알아보기

next.js/packages/next/src/shared/lib/router/router.ts

export default class Router implements BaseRouter {
	this.isReady = !!(
      self.__NEXT_DATA__.gssp || // getServerSideProps
      self.__NEXT_DATA__.gip || // getInitailProps
      (self.__NEXT_DATA__.appGip && !self.__NEXT_DATA__.gsp) ||
      (!autoExportDynamic &&
        !self.location.search &&
        !process.env.__NEXT_HAS_REWRITES)
    )
	
	...
}

__NEXT_DATA__는 페이지에 관한 정보를 가지고 있다.

gssp는 getServerSideProps gip는 getInitailProps라는 의미이고 사용 여부를 나타낸다.

Router가 생성될 때 isReady의 초기값은 gssp or gip가 사용 여부에 따라 하나라도 사용을 했으면 true로 결정된다.

물론, 다른 값도 검사를 하는데 다른 값들의 의미는 아직 파악하지 못했다. 이 부분은 추후에 알아보고 추가하기로..

0개의 댓글