static rendering, dynamic rendering, cache
build 작업
우선 Next.js 로 만든 서버를 어디 배포하기 위해 터미널에서 npm run build 명령어를 실행해주어야 합니다.
리액트 문법으로 작성한 코드들을 브라우저 친화적인 html, js, css 파일로 바꿔주는 작업입니다.
그 다음 npm run start 해두면, 실제로 유저 요청을 처리할 수 있는 Next.js 서버가 완성됩니다.
물론 실제 운영할 사이트면 AWS 같은 클라우드에 올려서 npm run start 해놓으면 됩니다.
Dynamic rendering / Static rendering
Next.js 옛날 버전에서 SSG ISR 어쩌구를 복잡하게 설명했었는데 이번 버전부터 용어와 설명이 약간 바뀌었습니다.
1. Next.js 에서 페이지를 하나 만들면, 기본적으로 static rendering 식으로 페이지를 보여줍니다.
페이지 안에 fetch 라든지 그런 함수가 없는 일반 페이지들은 기본적으로 static rendering 으로 동작하게 되어있습니다.
자세히 말해, npm run build 을 실행해 html 페이지들을 만들어줍니다. 그래서 유저가 접속할 때마다 그걸 그대로 보내준다는 이야기입니다. 페이지 안에 별 기능이 없어서 매번 html 페이지를 새로 만들 필요가 없으니까 그냥 그대로 보내는 것이고, 이 경우 매우 빠르게 페이지 전송이 가능합니다.
2. 하지만, 페이지 안에
- fetch(...) 로 데이터 가져오는 문법
- useSearchParams(), cookies(), headers()
- [dynamic route]
를 사용한다면, dynamic rendering 으로 페이지를 보여줍니다.
npm run build 로 html 페이지를 만들어놨지만, 유저가 페이지 접속 시 html 에 변동사항이 들어가야 하기 때문에 유저가 펭지에 들어갈 때마다 html 페이지를 서버에서 다시 그려준다는 이야기입니다.
Next.js 는 이런 2가지 방법으로 웹페이지들을 유저에게 보내줍니다.
static rendering / dynamic rendering 강제로 바꾸려면
npm run build 하면 λ(람다) 표시와 ○ 표시가 나오는데,
- λ 로 표시하면, dynamic rendering,
- ○ 로 표시하면, static rendering 을 해준다는 이야기입니다.
참고로 최상위 layout.js 에서 회원기능에서 배울 getServerSession() 을 사용하면, 모든 페이지가 아마 λ 일 수 있습니다.
근데 잘 보면 /list 페이지는 λ dynamic rendering 해야하는데, 글 작성하고 삭제하면 /list 페이지에 바로 업데이트되어야 하는데, 저러면 안 보일 것 같네요.
그걸 방지하고자 이 페이지를 dynamic rendering 을 하고 싶다면,
export const dynamic = 'force-dynamic'
export default function 페이지(){
(생략)
}
이런 변수를 페이지에 추가하면 됩니다.
'force-dynamic' 넣으면 dynamic rendering을 해주고
'force-static' 넣으면 static rendering을 해줍니다.
'auto' 넣으면 자동으로 알아서 판단해줍니다. (디폴트)
그래서 결론은 배포하기 전에 npm run build 할텐데
그 때 페이지들이 dynamic / static rendering으로 원하는대로 동작중인지 잘 살펴보면 좋습니다.
근데 Dynamic rendering 은 비효율적인 것 같은데?
이번 프로젝트는 DB입출력 기능을 많이 만들어놔서 dynamic rendering 페이지들이 많습니다.
dynamic rendering 식으로 동작하는 페이지가 많으면, 유저가 해당 페이지 방문마다 계속 다시 페이지를 그려야되니까 서버 부담이 심해질 수 있습니다.
그 경우, dynamic rendering 시 서버자원을 절약할 수 있는 방법이 캐싱기능이 있습니다.
캐싱
캐싱은 데이터를 잠깐 몰래 저장해두고, 그걸 재사용한다는 것입니다.
Next.js 에선, 페이지 캐싱과 GET요청결과 캐싱이 쉽게 가능합니다.
1초마다 변하는 실시간 데이터를 보내주는 게 중요한 페이지인 경우, 캐싱을 사용하면 안되겠지만, 대부분의 페이지들은 1초 단위의 실시간 데이터가 크게 중요하지 않습니다. 심지어 게시판도 새로운 데이터가 몇 초 늦어도 별상관없죠.
그러니 사이트를 만들 때 비용절약 + 속도향상에 도움되는 캐싱을 도입해보도록 하겠습니다.
GET 요청결과 캐싱
컴포넌트 안에서 데이터를 가져올 때 fetch() 를 이렇게 사용하면, 캐싱기능을 자동으로 이용할 수 있는데, 이러면 fetch() 명령 줄 때마다 서버에서 데이터를 새로 가져오는 게 아니라 한 번 가져온 결과를 어딘가에 몰래 저장해두고, 그걸 몰래 꺼내옵니다. 그럼 서버API 또는 응답을 기다릴 필요가 없으니 훨씬 빠르게 데이터를 가져올 수 있습니다.
export default async function 페이지(){
let result = await fetch('/api/어쩌구', { cache: 'force-cache' })
fetch() 사용시 cache: 'force-cache' 설정을 넣어두면 캐싱해주고, 앞으로 /URL 로 요청할 때마다 계속 캐싱된 결과를 가져와줍니다.
fetch('/URL', { cache: 'force-cache' })
다시 npm run build 하기 전까지 캐싱된 걸 평생 보여주죠. 참고로 위 코드를 작성하지 않아도 디폴트값으로 설정되어 있어 적어도 안 적어도 무관합니다.
fetch()안에서 revalidate 옵션도 적용할 수 있는데, 이러면 캐싱 결과를 60초 동안만 보관하고 사용합니다.
fetch('/URL', { cache: 'force-cache' })
60초가 지나면 다시 /URL 로 새로 요청애서 결과를 가져오고 캐싱해줍니다.
{ cache : 'no-store' } 을 넣어두면, 캐싱기능을 쓰지 않겠다는 뜻입니다.
fetch('/URL', { cache: 'no-store' })
그래서 매번 저 코드 읽을 때마다 서버로 요청해서 데이터를 새로 가져옵니다. 실시간 데이터가 중요할 때 사용할 수 있죠.
- Next.js 에선 바닐라 자바스크립트의 fetch() 기본함수를 업그레이드해놔서 사용가능한 문법입니다.
- server component 안에서만 캐싱기능이 사용가능합니다.
페이지단위 캐싱은 revalidate 변수 사용
Q. 그럼 connectDB 를 써서 DB입출력 코드 써놓은 건 캐싱할 수 없나요?
- 방법1. GET요청시 DB데이터 보내주는 서버 API 만들어두고, fetch()로 바꾸면 캐싱기능 사용가능.
- 방법2. revalidate 옵션을 켜놓아도 페이지 단위로 캐싱이 가능.
(아무 page.js 파일)
export const revalidate = 60;
export default function Page() {
DB입출력하는코드~~
return (
<div>어쩌구</div>
)
}
page.js 파일 위쪽에 revalidate 변수 하나 만들고, 원하는 초 단위를 넣으면, 특정 페이지를 원하는 시간만큼 캐싱해둘 수 있습니다. 그래서 위처럼 적으면 60초 동안은 이 페이지 접속시 아무리 새로고침해도 미리 캐싱된 페이지를 보여줍니다. 69초가 지나면 페이지를 재생성해서 캐싱해줍니다.
60초가 지나기 전에 페이지 강제로 새로고침을 해줄 수 있습니다.
on-demand revalidation 을 사용할 수 있는데요. 예를 들어 /list 페이지에 60초 캐싱이 켜져있으면 유저가 글을 하나 발행해도 60초간 /list 페이지에 새로운 글이 안 보일 수 있습니다. 근데, on-demand revalidation 기능을 사용하면, /list 페이지의 캐시를 새로 생성하라고 명령을 내릴 수 있습니다.