버전업을 미루다 보면 부채가 된다. 나중에 되면 더 버전업을 하기 힘들어진다. 새로운 기술을 적용하기 어려워질 것이고, 문서도 나중에는 없어져서 찾아보기 힘들 가능성도 있다.
우리는 서버에서 fetch
할 때 캐싱 기능이 매력적이라 생각했고, 필요해서 버전업을 하게 되었다. 운영자가 설정해놓은 값에 따라 이벤트 페이지를 있게한 프로젝트인데, 사용자마다 다른 화면을 보여줄 필요가 없었음에도 기존에는 매번 새로운 요청이 가고 있었기 때문이다. fetch
에 적용된 캐싱 기능으로 api 서버로 호출하는 양을 많이 줄일 수 있겠다는 생각이 들었다.
app router로 바꾸면서 getServerSideProps 에서 data fetching을 해오던 부분을 Server Component에서 fetching 하도록 바꾸었다. axios
를 사용하던 걸 기본 fetch
로 바꾸어 서버에서 caching이 되도록 하였다.
const response = await fetch(url, {
next: {
revalidate: 10,
},
});
revalidate option을 사용하여 caching 시간을 정할 수 있다. 짧게만 줘도 한번에 몰리는 이벤트의 특성상 많은 요청을 줄일 수 있을 것 같아 짧게두었다. 길게 하면 운영쪽에서 실수를 했을 때 빠르게 변경이 불가능할 것으로 판단하여, 짧게 두었다.
revalidateTag
나 revalidatePath
로 revalidate 를 할 수 있는 방법도 있어서 여러가지 실험을 해보고 성공했었지만, 우리는 pod을 여러개 띄워놓고 쓰고 있기 때문에 모든 서버들이 다 revalidate가 되지 않을 것으로 판단하고 포기했다.
처음에는 무조건 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점 후반으로 상승)
이전 버전에서는 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
를 만들 수 있었다.
이전 버전에서는 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를 사용하는 부분만 새로 동작하도록 할 수 있었다.