우리는 개발자 모드에서 프로젝트를 build를 했었다 npm run dev
하지만, 사용자에게 애플리케이션을 제공하기 위해서는 npm run build 를 통해서 프로젝트를 build를 해준 다음, npm run start 로 페이지를 열어볼 수 있었다.
nextjs의 building을 살펴봐야 하는데, building이란, production mode(운영 모드) 프로젝트 를 빌드하는 것을 의미한다.

npm run build 를 했을 때의 모습이다.
정적 페이지: Next.js에서 "static" 모드는 정적 페이지를 의미하며, 이 페이지는 빌드 시점에 HTML 파일로 미리 생성된다.
이러한 페이지는 요청 시 서버에서 별도의 처리가 필요 없으며, 매우 빠르게 로드될 수 있다.
동적 페이지: Next.js에서 "dynamic" 모드는 동적인 페이지를 의미하며, 이 페이지는 각 요청 시마다 서버에서 처리되어 생성됩니다. 즉, 빌드 시점이 아닌 요청 시점에 데이터를 가져오거나 페이지를 생성한다.
그럼 여기서, profile은 동적 페이지인데, 왜 home페이지는 정적 페이지일까?
여기서 질문을 던져보면 된다. "사용자가 누구든 페이지가 변경이 되는가?"
여기서 profile -> 네, home -> 아니요 라고 대답을 할 수 있다.
profile은 사용자가 누구인지에 따라서 프로필 페이지가 동적으로 변경이 되지만,
home은 사용자가 누구인지간에 똑같은 상품들을 볼 것이다.
(home page -> cookie를 사용하지 않는다)
nextjs는 application을 build하는 동안, getInitialProducts()함수를 실행할 것임. -> 그리고 이 output을 사용해서 nuext.js가 html을 만듬
현재, home페이지에서는 cache에 저장하지 않고, getInitialProducts()함수를 직접 호출하고 있다.
이때, 프로젝트가 build하는 시점에, getInitialProducts()가 한번 호출이 되고, 호출이 되면서 console에 hit이 출력이 되고, 이게 정적 페이지로 저장이 되는 것이라고 생각하면 된다.
여기서 확인할 수 있는 것은 -> cache와 관련이 없다는 것이다. cache를 사용하지 않았음에도 getInitialProducts() 다시 호출하지 않는다. 여기서 database를 다시 호출하는 유일한 시점은 우리가 revalidate를 클릭했을때만을 나타낸다.
정리하자면
Next.js에서 프로덕션 모드로 빌드했을 때, 어떤 페이지가 어떤 사용자가 접근하더라도 동일한 페이지를 보여준다면, 그 페이지는 정적(static) 페이지로 빌드된 것이다.
왜 정적 페이지로 빌드될까?
정적 페이지의 특징: 정적 페이지는 빌드 시점에 HTML 파일로 미리 생성되며, 모든 사용자에게 동일한 콘텐츠를 제공한다. 즉, 데이터가 변경되지 않거나 모든 사용자에게 동일하게 제공될 때 적합하다.
데이터 가져오기: 만약 Home 페이지에서 getStaticProps를 사용하여 데이터를 가져오고 있다면, 이 데이터는 빌드 시점에 한 번만 가져와서 해당 페이지에 포함된다. 이렇게 생성된 정적 페이지는 모든 사용자에게 동일하게 제공된다.
정적 페이지로 빌드되는 경우
이제부터는 next js가 어떻게 우리의 모든 route를 cache하는지를 알아낼 것이다.
또한, 그리고 기본적으로 이 route가 어떻게 동작하는지도 알아낼 예정이다.
Next.js에서 Route Segment Config는 경로 세그먼트에 대해 특정 설정을 지정하는 기능을 말한다. 경로 세그먼트는 URL 경로의 각 부분을 의미하며, Next.js에서는 이러한 세그먼트에 대한 구성 옵션을 설정할 수 있다.
Route Segment Config의 옵션 중, dynamic에 대해서 배워볼 건데,
dynamic: 이 옵션은 동적 렌더링을 제어한다.
force-dynamic 은 사용자가 페이지를 방문할 때마다 이전 버전의 HTML을 볼 수 없다는 뜻을 의미한다.
즉, 홈페이지가 static이 아니였으면 한다고 말하는 것을 의미한다고 이해하면 된다.
--> 사용자가 방문할 때마다 generate되기를 원한다고 말하는 것과 같다.
원래의 home/page.tsx에서는 getInitialProducts()를 build를 하기 전 한번 호출한 뒤, static page로 생성해서 정적페이지로 사용자에게 보여주게 된다.
위에서 설명했듯이, 사용자가 달라도, 보이는 화면은 똑같기 때문이다.
여기에 export const dynamic = 'force-dynamic' 속성을 추가해 주었다. 그럼 어떻게 될 까?
화면을 새로고침할 때마다
getInitialProducts() 함수가 계속 호출되고 있는 것을 확인할 수 있다.
즉, 사용자가 refresh 할 때마다 database를 호출하고 있다는 것을 의미한다.
force-dynamic 을 사용할 때 nextCache를 사용하기에 알맞은 곳임.
왜? -> refresh할 때마다 database를 호출하는 것을 원하지 않기 때문이다.
revalidate
이 옵션은 페이지가 일정 시간 이후 다시 검증되도록 설정한다.
ISR(Incremental Static Regeneration)을 통해 캐시된 페이지를 일정 시간 후에 다시 생성할 수 있다.
ISR은 Next.js에서 정적 페이지를 주기적으로 재생성하여 최신 상태를 유지할 수 있는 기능이다. 이를 통해 정적 페이지의 장점을 살리면서도 페이지가 최신 상태를 유지할 수 있도록 한다
revalidate옵션을 설정하여 업데이트 주기를 정의할 수 있다.
이건 우리가 production mode에서 빌드했을 때 작동하게 된다.
export const revalidate = 60
revalidate -> 원래 home 페이지는 static 페이지이다. 내가 설정해놓은 60초 후에 재검증을 하게 될 것이고, 페이지는 새로운 request를 받게 될 것이다.
60초가 지나면, request를 만들 것이다 -> database에 가서 내 데이터를 가지고 올 것이다.
이는 사용자가 접근하면 컨텐츠는 이미 준비되어 있고 database를 건드리지도 않은 static 페이지를 제공해준다. 그리고 우리는 60초 혹은 1시간 정도의 revalidation 타임을 지정해준다.
우리의 페이지가 업데이트 되길 원하는 시간을 정하면 된다. 그럼 정해진 시간에 next js는 자동으로 database를 호출하고, 모든 작업을 다시 수행하게 된다.
개발 모드 (npm run dev):
프로덕션 모드 (npm run build + npm run start):
/products/[id] 는 dynamic page다.
왜 ? [id]에는 무엇이 들어갈지를 모르기 때문이다.
string이 들어간다면, notFound()를 return 해주어야 하고,
해당하지 않는 number이 들어간다면, notFound()를 return 해주어야 하고,
정확한 products의 id가 들어가야 -> products/11 와 같이 해당 UI를 보여주게 된다.
이로써 어떤 페이지가 나올지 모르기 때문에 -> dynamic page가 된다.
여기서, 이미 데이터베이스에 존재하는 product의 id를 안다면, 빌드 시점에 HTML 파일로 생성해서 static 페이지로 변경할 수 있지 않을 까? (물론, 지금은 products의 개수가 적기 때문에 가능하지만, 만약 10000개가 넘어간다면 빌드 과정에서 데이터베이스가 다운될 수 도 있으니까 조심히 사용해야 한다)
이때 사용하는 함수가 바로 generateStaticParams() 이다
export async function generateStaticParams() {}
함수의 이름을 무조건 generateStaticParams()로 해주어야 하는데, 이 함수는 무조건 array를 return 해야 한다.
--> ProductDetail 함수가 받을 가능성이 있는 parameter들이 들어있는 array를 말한다.
우리의 함수가 받을 가능성이 있는 parameter objects 리스트를 reture 해야 한다. --> 이 페이지에서 not found 이외의 것을 표시할 가능성이 있는 parameter는 현재 데이터베이스에 저장되어있는 products이 id를 의미한다.
export async function generateStaticParams() {
const products = await db.product.findMany({
select: {
id: true,
},
});
return products.map((product) => {
return {
id: product.id,
};
});
}

ProductDetail에서 받는 것과 동일한 방식이다({ params }: { params: { id: string } }) -> 문자열로 받고 있다
-> 같은 모양, 같은 타입으로 제공을 해주어야 한다 -> 나중에 이걸 숫자로 바꿔주기 때문이다.
generateStaticParams()의 역할은 "이 페이지는 이런 종류의 parameter들을 받을 수 있어, 이것들이 가능한 것들이야" 라고 이야기 해주는 역할을 한다.
이후 npm run build를 하게 되면, product/[id]가 static이 되게 되는데, 이때, 데이터베이스에 저장되어있는 products의 id를 받아와서 static 페이지로 저장하게 된다.
NextJS 가 이전에는 여기에 뭐가 올지 전혀 모르므로 dynamic 페이지를 주었지만,
이제는 우리가 여기에 뭐가 올 수 있는지 알려주었다 ->그리고 이 정보를 바탕으로 static 페이지로 변경시켜주었다.
따라서 유저가 products/[id]를 방문하게 되면,데이터베이스는 이제 호출되지 않는다. 왜? 이 페이지는 이미 미리 구축되어 있을 것이기 때문이다!
현재 내 데이터베이스에 id : 11인 product만 저장이 되어있는데, id:13인 상품을 하나 추가했다.
어? 그럼, products/[id]는 현재 static 페이지가 되었으니까 id:13인 상품은 볼 수 없는거 아닌가?
아니다 ! products/13 은 미리 생성되지 않았기 때문에 기본 동작은 이것을 dynamic페이지로 간주하게 된다.
-> 데이터베이스를 호출하고, 이때 사용자는 아마 skeleton을 보게 될 것이다.
-> 하지만 이는 오직 이 페이지를 요청한 첫번째 유저에게만 발생하게 된다.
-> 첫번째 request 후에는 HTML페이지로 바뀌게 될 것이기 때문이다(다시 static 페이지가 된다)
이러한 속성은 어떻게 발생하는걸까 ? --> dynamicParams 덕분이다.
export const dynamicParams : true 기본값은 true이다.
dynamicParams가 true일 때, 미리 생성되지 않은 페이지들이 dynamic페이지들로 간주되게 된다. 그 페이지들은 render될 것이고, 데이터베이스를 호출이 될 것이다.
그 후엔 그 페이지의 HTML에 저장이 된 후, static 페이지로 변경되게 된다.
dynamicParmas : false : 오직 빌드할 때 미리 생성된 페이지들만 찾을 수 있게 된다.
만약 유저가 빌드할 때, 미리 생성되지 않은 페이지로 이동하면 notFound()페이지로 이동한다. 왜? 이미 빌드가 이루어진 후, 상품을 추가해도, 빌드가 이루어지지 않기 때문이다!
1 . 우리의 제품 페이지 (/home) 을 위한 caching전략을 고르자.
구현하고 제품 업로드 server action에 연결시켜보자.
우리가 caching을 사용하고 있지만, 캐시를 재검증하는 전략을 가지고 있지 않다면 유저가 제품을 업로드할 때마다 그들은 홈페이지에서 최신화된 페이지를 볼 수 없을 것임.