NextJS 12 에서 14로 업데이트 하면서 간단한 꿀팁

IT공부중·2024년 8월 21일
0

Next

목록 보기
11/11

버전업 하게 된 계기

버전업을 미루다 보면 부채가 된다. 나중에 되면 더 버전업을 하기 힘들어진다. 새로운 기술을 적용하기 어려워질 것이고, 문서도 나중에는 없어져서 찾아보기 힘들 가능성도 있다.
우리는 서버에서 fetch 할 때 캐싱 기능이 매력적이라 생각했고, 필요해서 버전업을 하게 되었다. 운영자가 설정해놓은 값에 따라 이벤트 페이지를 있게한 프로젝트인데, 사용자마다 다른 화면을 보여줄 필요가 없었음에도 기존에는 매번 새로운 요청이 가고 있었기 때문이다. fetch 에 적용된 캐싱 기능으로 api 서버로 호출하는 양을 많이 줄일 수 있겠다는 생각이 들었다.

getServerSideProps 에서 fetching 하던 부분을 Server Component로

app router로 바꾸면서 getServerSideProps 에서 data fetching을 해오던 부분을 Server Component에서 fetching 하도록 바꾸었다. axios를 사용하던 걸 기본 fetch 로 바꾸어 서버에서 caching이 되도록 하였다.

 const response = await fetch(url, {
   next: {
     revalidate: 10,
   },
 });

revalidate option을 사용하여 caching 시간을 정할 수 있다. 짧게만 줘도 한번에 몰리는 이벤트의 특성상 많은 요청을 줄일 수 있을 것 같아 짧게두었다. 길게 하면 운영쪽에서 실수를 했을 때 빠르게 변경이 불가능할 것으로 판단하여, 짧게 두었다.
revalidateTagrevalidatePath로 revalidate 를 할 수 있는 방법도 있어서 여러가지 실험을 해보고 성공했었지만, 우리는 pod을 여러개 띄워놓고 쓰고 있기 때문에 모든 서버들이 다 revalidate가 되지 않을 것으로 판단하고 포기했다.

emotion 을 대체??

처음에는 무조건 emotion을 걷어내고, panda css 같은 라이브러리를 사용하거나, 그냥 css module 같은 방법으로 사용해야하는 줄 알았다.
하지만 우리는 운영에서 설정한 색깔이나, css string을 동적으로 사용해야하는 프로젝트의 특성상, panda css 같은 걸로 사용하기는 어려웠다.

그래서 제일 상위에서 data를 fetching하고, data를 내려주는 부분만 Server Component 로 사용하고, 나머지는 기존과 똑같이 동작하도록 Client Component로 동작하도록 하였다. Client Component 가 이름이 client라 그렇지 이전처럼 SSR 이 동작하기 때문에 아무 문제 없었고, data caching이 들어간 것이기 때문에 성능이 좋아진 것을 확인했다. (기존에 lighthouse 기준 80점 후반 90초반으로 나오던 성능이, 90점 후반으로 상승)

useSearchParams 이전 버전처럼 사용하기

이전 버전에서는 const { query } = useRouter() 로 사용할 수 있었는데 이제는

const searchParams = useSearchParams();
searhParams.get('test');

이런식으로 사용해야했다. 이렇게 하니, 기존 코드를 고쳐야할 부분이 너무 많았고, searchParams 가 많은 페이지에서는 매번 .get 을 사용하는게 정말 귀찮은 일이었다. 또한 구조분해 같은걸로 변수로도 쉽게 만들 수 있는게 아니니 여러번 사용하는 searchParams 를 한줄한줄 바꿔줘야하는데 너무 불편한 일이었다.

const useQueryParams = <SearchParamsType extends Record<string, string> = Record<string, string>>() => {
  const searchParams = useSearchParams();
  
  const queryParams = useMemo(() => {
    const searchParamsEntries = [...searchParams.entries()] as [
      keyof SearchParamsType,
      SearchParamsType[keyof SearchParamsType],
    ][];

    return searchParamsEntries.reduce((acc, [key, value]) => {
      acc[key] = value;

      return acc;
    }, {} as SearchParamsType);
  }, [searchParams]);
 
  return { queryParams }
}

이렇게 하면 generic으로 넣어준 타입을 잘 추론해내는 queryParams 를 만들 수 있었다.

shallow routing

이전 버전에서는 router.push( { pathname: pathname, query: nextParams, }, undefined, {shallow: true}) 이런식으로 하면 query만 바뀌고 기존것들은 그대로 유지되는 shallow push를 할 수 있었는데 이번에는 옵션이 사라졌다.

그래서 같은 기능을 하는 걸 만들었다. 커스텀하게 만든 useRouter의 일부다.. 참고 공식문서

  const push = useCallback((path: string, options?: NavigationOptions<SearchParamsType>) => {
    if (options?.shallow) {
      window.history.pushState(null, '', searchParamsToString(options?.searchParams));

      return;
    }

    router.push(`${path}${searchParamsToString(options?.searchParams)}`, { scroll: options?.scroll });
  }, []);

이렇게 만들어주니 shallow 옵션을 줬을 때는 화면 갱신이 되지 않고 바뀐 searchParams를 사용하는 부분만 새로 동작하도록 할 수 있었다.

profile
4년차 프론트엔드 개발자 문건우입니다.

0개의 댓글