[Next.js 13] 이모저모

Sming·2023년 10월 16일
18
post-thumbnail

그냥 이모저모..

Next.js 13 으로 개발할때 알면 좋은것들을 적으려 했는데 하나하나 적기에는 짜쳐서(?) 그냥 이모저모 모아봤습니다.

  • isr vs ssr
  • searchParams
  • error boundary
  • 느린 api를 호출하는 ssr (streaming ssr)
  • 서버컴포넌트가 너무 빨라요..
  • Next.js with Node.js
  • 서버컴포넌트 zero-bundle 써먹기

isr vs ssr

먼저 isr, ssr를 비교하는 것을 알아볼 예정입니다. 하지만 대부분 Next.js 13까지 도달하신 분들은 이 개념자체는 다 알고 계시겠죠?

그래서 간단하게만 설명하자면 isr은 일정 시간을 기준으로 api를 fetch하여 데이터를 갱신하는것이고 ssr은 사용자 요청때 마다 api를 서버에서 fetch하는 것입니다.

여기서 중요한것이 뭘까요?

바로 isr과 ssr의 요청 시점입니다.

ssr은 사용자의 요청시점에, isr은 빌드 타임에 생성이 됩니다.

이 때문에 자주 변하지 않는 데이터라도 isr을 사용하지 못할때가 존재합니다. 바로 사용자의 request header를 참조해야될때입니다.


주로 request header에 존재하는 cookie, userAgent등을 참조하여 무언가 처리가 필요할때는 isr을 사용할 수 없는것이죠.

이럴때는 어쩔수 없지만 ssr을 사용하시면 됩니다. 🐳

+) 여담이지만 제 옛날 글에 Next.js 13.3기준으로 컴포넌트 별 isr이 동작하지 않는다라고 글을 작성한적이 있는데 Next.js 13.4 (app dir stable)이 되면서 컴포넌트 별 isr이 동작하더군요. 이용할 수 있는곳에 많이 이용하시는 것을 추천드립니다.

searchParams

개발하다보면 url에 상태를 저장해야할때가 있습니다. 예를 들어서 내가 url을 친구에게 공유하였을때 내가 보는 화면과 친구가 공유받은 url을 열었을때 보는 화면이 동일해야 할때 이용할 수 있죠.

Next.js에서는 searchParams를 다루는 방법이 2가지 상황이 존재합니다.

먼저 서버 컴포넌트일때입니다. page.tsx에서 props로 searchParams를 받아서 사용할 수 있습니다.

export default function Main({searchParams}) {
	reutrn ...
}

다음은 클라이언트 컴포넌트입니다.

클라이언트 컴포넌트에서는 useSearchParams라는 훅을 이용하여 searchparams를 조회할 수 있습니다. 참고로 이 훅은 읽기 전용이라 이것을 통해서 queryString을 set하는 동작은 할 수 없습니다.


export const ClientComponent = () => {
  const searchparams = useSearchparams();
  
  return ...
}

error boundary

다음은 에러 바운더리입니다. 사실은 isr vs ssr 파트에 있어도 될 내용이지만 따로 얘기를 하고 싶어 분리를 했습니다.

에러가 발생했을때 그 에러에 대한 fallback처리를 할 수 있는 것이 에러 바운더리인데 기존에는 client에서 fetch하는 컴포넌트에서만 이용했었습니다.

하지만 이제는 서버 컴포넌트의 등장으로 컴포넌트 단위의 ssr, isr이 가능해서 여기에도 에러 바운더리를 적용할 수 있게 되었습니다.

하지만 서버 컴포넌트에서의 에러바운더리는 ssr에서만 이용할 수 있습니다.

아까 isr vs ssr에서 얘기했듯이 isr은 빌드타임에 호출을 하고 에러도 빌드타임에 발생하기 때문에 에러바운더리에서 잡을 수가 없습니다.

하지만 약간의 꼼수로 isr에서도 에러바운더리같은 동작을 할 수 있도록 할 수 있습니다.

바로 에러를 메모리(코드 내 변수)에 저장하는것입니다.

빌드타임에서 발생한 에러를 변수에 저장을 한뒤 실제 런타임이 됬을때 그 변수의 상태 (ex. error: true)를 참조하여 fallback을 보여주도록 구현할 수 있습니다.

느린 api를 호출하는 ssr

만약 서버 컴포넌트가 5개이고 각각 fetch를 하는 페이지가 있다고 가정해보겠습니다. 만약 Link태그를 이용하여 이 페이지에 오면 어떻게 될까요?

사용자는 바로 완성된 페이지를 보게됩니다. 이는 Link태그의 prefetch기능때문인데요.

하지만 여기서 5개 중에 하나의 컴포넌트가 10초가 걸리는 api가 있다고 가정해보겠습니다. 이런 경우에서는 어떻게 동작을 할까요?

사실 그 느린 api를 부르는 컴포넌트만 늦게 불러와지고 나머지는 이미 완성되어있는 페이지를 바로 보여주는게 베스트지만 실제로는 10초동안 멈춰있는듯이 있다가 10초 뒤에 완성된 페이지를 보게됩니다.


이를 해결하기 위해서 Next.js 13부터는 Streaming Ssr이라는 것을 이용할 수 있습니다.

매우 좋은 기능에 비해서 사용방법도 간단합니다. 바로 느린 api를 부르는 서버컴포넌트에 Suspense태그를 감싸면 저절로 streaming ssr이 동작하게 됩니다.

이 경우에는 페이지가 바로 넘어가게 되며 느린 api를 부르는 서버 컴포넌트는 fallback (주로 스켈레톤 ui를 보여줍니다.)을 보여주며 나머지는 완성된 페이지를 볼 수 있습니다.

제가 Next.js 13에서 가장 애용하는 기능중 하나입니디.🎉🎉

서버컴포넌트가 너무 빨라요..

Next.js 13으로 개발을 하다보면 이러한 상황을 겪을 때가 있습니다. 서버 컴포넌트로 이루어져있는 페이지에서 클라이언트 fetch를 하는 컴포넌트가 존재하면 눈에 띄게 느려보이는 경우죠.

사실 이게 느리다기 보다는 정상속도인데 서버 컴포넌트에서 미리 보여주는 속도가 너무 빠른 탓이죠..🥲

저는 보통 이러한 상황에서 2가지 상황으로 해결을 합니다.

  • client 상태를 이용하는 fetch의 경우 react-query의 hydrate를 이용하여 prefetch를 진행
  • 클라이언트 컴포넌트의 결과물과 동일한 ui의 스켈레톤 ui 만들기 (그냥 회색 스켈레톤 ui도 가능)

실제로 client에서 fetch를 이용해야만 하는 경우는 client의 상태에 fetch가 의존성이 있을때 입니다. 예를 들어서 무한스크롤정도가 있을것 같네요.

이런 경우에는 react-query의 hydrate를 이용하여 prefetch를 진행한 후에 client compoenent에서 fetch를 진행하면 클라이언트가 다운로드 받을때까지 컴포넌트가 안보이는 부자연스러운 동작을 없앨 수 있습니다.


두번째 방법은 많이 사용되지는 않지만 script태그등으로 ui를 불러와서 그려주는 경우에 이용을 할 수 있습니다.

script태그가 불러오는 동안 스켈레톤 ui를 서버 컴포넌트로 그려준뒤 script태그가 로드가 되면 서버 컴포넌트를 날려버리고 불러온 ui를 대체하여 보여주는 것이죠.

Next.js with Node.js

Next.js를 도커와 같은 컨테이너 환경으로 빌드를 할때 ci머신(jenkins, github action)에서 볼 수 있는 에러가 있습니다.

> Build error occurred
TypeError [ERR_INVALID_ARG_TYPE]: The "id" argument must be of type string. Received undefined
    at new NodeError (node:internal/errors:387:5)
    at validateString (node:internal/validators:162:11)
    at Module.require (node:internal/modules/cjs/loader:1093:3)
    at require (node:internal/modules/cjs/helpers:108:18)
    at Object.verifyTypeScriptSetup (/web/src/node_modules/.pnpm/next@13.4.19_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/lib/verifyTypeScriptSetup.js:115:42) {
  type: 'TypeError',
  code: 'ERR_INVALID_ARG_TYPE'
}

바로 다음과 같은 에러인데요. 이 에러는 노드 버전이 Next.js와 잘 맞지 않아서 발생하는 문제입니다.

이 경우 노드버전을 확인하신후 docker의 노드버전을 18.18(lts) 버전 이상으로 맞춰주세요.

서버컴포넌트 zero-bundle 써먹기

서버컴포넌트의 큰 장점중 하나인 zero-bundle..!

사실 실 개발에서 크게 체감을 느끼면서 이용한 경우가 많이 없을것입니다.

data를 fetch해주는 매개체로 이용을 하는게 대부분이고 실제로 client상태가 안들어가는 컴포넌트는 매우 적기 때문에 서버컴포넌트로 인한 zero-bundle의 이점을 누리기는 쉽지가 않죠.


하지만 data를 파싱할때 이 서버 컴포넌트의 zero-bundle이 유용하게 이용될 수 있습니다. 사실 데이터의 파싱, 가공을 하는 것은 백엔드에서 해주는 것이 최고의 케이스이긴하지만 그렇게 이상적으로 모든 계산이 백엔드에서 되는 경우는 많이 없습니다.

특히 날짜에 대한 처리같은것을 프론트쪽에서 많이 해야될 경우가 있죠.

예를 들어 dayjs라는 날짜 파싱 라이브러리를 이용한다고 했을때 이를 기존처럼 클라이언트 컴포넌트에서 처리를 한다면 dayjs 패키지의 용량만큼 초기번들에 추가되게 됩니다.

하지만 서버 컴포넌트에서 dayjs를 이용하여 날짜파싱을 진행하게 된다면 dayjs의 용량은 초기번들에 포함되지 않게됩니다. 말그대로 서버에서 일어난 일이기 떄문이죠.

그래서 이러한 데이터를 가공을 하는 라이브러리를 이용을 할때 서버 컴포넌트에서 import하여 처리하고 클라이언트에 내려주게 된다면 초기 번들의 사이즈를 경량화할 수 있습니다.

결론

Next.js 13은 파도파도 뭔가 나오는것 같네용..

Next.js 13 잡기술 모음집은 아마 이 글로 마무리 되지 않을까 싶습니다. 혹시 Next.js 13 개발을 하시면서 자신이 주로 이용하는 팁같은게 있으시면 댓글로 공유해주시면 감사하겠습니다. ㅎㅎ

profile
딩구르르

4개의 댓글

comment-user-thumbnail
2023년 10월 19일

다음은 무슨 주제로 가십니까 ?

1개의 답글
comment-user-thumbnail
2023년 10월 20일

좋은글 읽고갑니다~

답글 달기
comment-user-thumbnail
2023년 11월 2일

혹시 프로덕트 레벨에서 13을 사용하고 계신건가요? ㅎㅎ

답글 달기