이제 커머스 서비스에서 가장 중요한 상품을 다뤄보려고 합니다.
서버에는 총 4개의 상품이 등록되어 있습니다.
- 스킨케어 > 스킨 > 촉촉가득 스킨 50ml
- 스킨케어 > 스킨 > 촉촉가득 스킨 100ml
- 헬스케어 > 영양제 > 기운펄펄 홍삼스틱 30개입
- 헬스케어 > 영양제 > 기운펄펄 홍삼스틱 60개입
먼저 4개의 상품이 사이트 메인화면에 전부 노출되도록 구성했습니다.
이때 getStaticProps 함수를 사용하여 검색엔진 최적화와
revalidate 주기를 적절히 지정하여 요청횟수를 최소화하면서 상품 내용의 변경을 반영할 수 있어야 합니다.
export async function getStaticProps() {
const res = await fetch(--상품조회 API URL--);
const prds = await res.json();
return {
props: { prds },
revalidate: 30,
};
}
메인화면에 실제 상품이 반영되었습니다!
이제 보여지는 이미지를 클릭하면 상품의 상세 페이지로 이동할 수 있도록 해야합니다.
하지만 그 전에 카테고리별 상품목록을 먼저 구성하고자 하는데요.
구현중인 서비스에는 심플하게 아래와 같이 카테고리가 구성되어 있습니다.
스킨과 영양제 두 개의 상품 조회 페이지를 구성해야 할까요?
그러면 카테고리가 하나씩 늘어날때마다 페이지도 하나씩 늘어나야 하기때문에
부담스럽고 파일의 갯수도 너무 많아집니다.
같은 페이지 안에서 요청 카테고리에 해당하는 상품들만 가져와서 보여주면
될 것 같은데요.
Next.js에서는 이럴 경우를 위해 Dynamic Routing 기능을 제공하고 있습니다.
다이나믹 라우팅을 사용하려면 [파일명].js의 형태로 파일을 생성하면 됩니다.
예를들어 pages/product/[code].js와 같이 생성한다면
products/1로 접근할 경우 페이지에서 1이라는 스트링을 얻을 수 있습니다.
url상의 String값을 얻어오는 방법은 간단합니다.
useRouter를 사용하면 됩니다.
const router = useRouter();
const {code} = router.query;
<div>Product: {code}</div>
그렇다면 라우터로부터 얻은 카테고리 네임을 서버에 전달하여
카테고리 범주안의 상품 리스트를 가져오면 될텐데요.
그렇게 하면 렌더링이 모두 끝난 후에야 카테고리 네임을 가져올 수 있기 때문에
Client-Side에서 데이터 페칭을 해야만 하는 단점이 있습니다.
커머스에서 상품관련 데이터(특히 이미지)는 SEO에 매우 중요한 역할을 하기 때문에 반드시 SSG, SSR 방식 중 하나를 채택해야 합니다.
그럼 어떻게 해야 페이지가 렌더링 되기 전 사용자가 어떤 path로 접근했는지를 미리 알고 서버사이드에서 데이터가 실린 화면을 제공해줄 수 있을까요?
이전 포스팅에서 Next.js 데이터 페칭 방법을 소개한 바 있는데,
getStaticPaths에 대해 자세히 언급하지는 않았습니다.
왜냐하면 그때는 구현 초기단계라 다이나믹 라우팅을 쓰기 전이었기 때문인데요.
페이지가 다이나믹 라우팅되면서 getStaticProps를 사용하고자 한다면 반드시 getStaticPaths를 사용하는 것을 고려해볼 수 있습니다.
import { useRouter } from "next/router";
export default function Product({ props }) {
const router = useRouter();
const { code } = router.query;
return <div>Product: {code}</div>;
}
export async function getStaticPaths() {
const res = await fetch("get-paths");
const paths = await res.json();
return { paths, fallback: false };
}
export async function getStaticProps({ params }) {
return {
props: { key: params.code },
};
}
getStaticPaths는 서버로부터 path에 관한 데이터를 가져옵니다.
그때 path 데이터는 아래의 형태를 가집니다.
paths: [{ params: { id: 'path1' } }, { params: { id: 'path2' } }],
getStaticPaths는 getStaticProps에게 값을 전달해줍니다.
(= 사용자가 접근한 path를 전달)
그럼 getStaticProps는 전달받은 path정보를 활용해 서버에 알맞은 데이터를 요청하게 됩니다.
그로 인해 SSG방식으로 화면이 렌더링 되게 됩니다.
이때 fallback을 false로 지정하면 서버에서 가져온 path값 외에 다른 경로로 접근 시 404페이지를 리턴합니다.
fallback을 false로 설정한다면 빌드 시 가져온 path이외엔 전부 블록됩니다. (404 페이지로 fallback)
그렇다면 path(여기서는 상품 카테고리)가 하나 추가될 때마다 re-build를 수행해야 하는 문제가 발생하게 되는데요.
그래서 fallback을 true로 설정 후 다시 빌드를 수행하였습니다.
그랬더니 빌드 과정 중 오류가 발생하였습니다.
참조 오류: outRec이 정의되지 않음 오류가 발생하였고
오류 로그 중 제공되는 https://nextjs.org/docs/messages/prerender-error에서 관련 내용을 확인해 볼 수 있었습니다.
해당 오류를 개선하기 위해 총 6가지의 방법을 안내하고 있는데요.
- 페이지가 아닌 파일은 페이지 디렉토리에서 빼세요.
- 사용 가능하지 않은 prop이라고 가정하는 코드가 있는지 확인하세요.
- 모든 다이나믹 페이지의 default값을 설정하세요. (undefined가 나오지 않도록, null이 할당 되어야 직렬화가 가능해집니다.)
- 사용기한이 지난 모듈 여부를 체크하세요.
- getStaticPaths을 사용한다면 fallback 속성을 체크하세요.
- getServerSideProps를 사용하는 페이지를 export 시도하였는지 확인하세요.
저 같은 경우는 2,3번의 문제였습니다.
상품 페이지가 getStaticProps를 통해 상품 리스트를 props로 받는데
렌더링 전 props의 값이 무엇인지 알 수 없으니 참조오류가 발생하였습니다.
위와 같이 prop를 읽는 과정 중 undefined가 발생하지 않도록 기본값 처리를 해주니 오류가 사라졌습니다.
하지만 이런 문제는 궁극적으로 typescript를 사용하면 자연스럽게 없어집니다.
빌드를 실행 후 기존에 없던 로션 카테고리를 추가하고 상품을 추가하였습니다.
빌드 이후에 새로 생성된 path에 대해서 아래와 같이 처리되는 것을 볼 수 있습니다.