넥스트 프로젝트를 진행하며 배포된 사이트에서 메인 페이지를 로드하는데 8초 이상 걸리는 문제가 발생했습니다. 처음엔 로그인 로직을 수정한 뒤, 문제가 발생해 엑시오스 인터셉터를 수정해야 하는 줄 알고 한참을 헤맸는데요 .. 결과적으로는 제가 넥스트의 이점을 하나도 !!! 활용하지 못 해 발생한 문제였습니다
부끄럽지만 이 프로젝트에 합류하기 전, 넥스트에 대해 제대로 공부하지 않았고 .. 제가 아는 넥스트의 이점은 seo에 좋대~ 정도뿐이었습니다. 결국 핑계지만 .. 기능들을 구현하기 바빠 .. 넥스트 공부는 하지 않고 .. 넥스트를 리액트처럼 쓰는 만행을 저질러버렸는데요 ..
문제를 해결하기 위해 넥스트를 공부하고 수정한 코드를 공유하며 반성의 시간을 갖겠습니다.
문제가 발생한 페이지는 메인 페이지였습니다. 다른 페이지는 모두 2초 내외로 컨텐츠를 보여주지만 메인페이지는 엄청난 사이즈의 배너 이미지, 베스트 식물 데이터 등 수많은 데이터를 패치하고 있어 빈화면을 띄우는 시간이 길었습니다.
<>
<Slider />
<Category />
<ShopPrev />
<BestPlant />
<BestReview />
<News />
</>
처음 코드를 작성할 땐 Slider, News 컴포넌트의 데이터는 변할 확률이 희박하기 때문에 SSG를, BestPlant, BestReview 컴포넌트는 사용자의 구매/후기 작성에 따라 가변하는 데이터이기 때문에 SSR을 사용해 페이지를 렌더링하고 싶었습니다.
하지만 저희 프로젝트는 넥스트의 페이지 라우팅 방식을 사용하기 때문에 컴포넌트가 아닌 페이지 별로 렌더링 방식을 결정해야했습니다. 따라서 변경되는 데이터에 우선순위를 둬 메인 페이지는 getServerSideProps를 사용했습니다.
그러나 .. 넥스트를 공부하고 난 뒤, 데이터를 패치하는 것부터 글러먹었다는 것을 알게되었습니다.
넥스트는 크게 로그인 여부, 데이터의 변경 가능성에 따라 렌더링 방식을 선택할 수 있습니다.
사용자의 로그인이 필요한 경우, 사용자별 데이터를 제공하기 위해 CSR, SSR, 또는 CSR과 SSR을 결합한 하이브리드 방식을 사용할 수 있습니다.
데이터의 경우 변하지 않는다면 SSG를, 변경 빈도가 낮다면 ISR을 선택할 수 있습니다. 그리고 자주 변경되는 데이터는 SSR 또는 ISR과 SSG/CSR을 결합한 하이브리드 방식을 사용할 수 있습니다.
export const getStaticProps: GetStaticProps<IndexPageProps> = async () => {
const banner = await getMainBanner();
const bestPlant = await getBestProduct();
const bestReview = await getBestReview();
const news = await getNews();
return {
props: {
banner,
bestPlant,
bestReview,
news,
},
revalidate: 60 * 60 * 24,
};
};
저의 경우 메인 페이지는 로그인 분기가 필요 없고, 유저가 많지 않아 데이터가 자주 변경되지 않습니다. 따라서 클라이언트가 요청할 때 마다 렌더링하는 SSR을 하루에 한 번 revalidate하는 ISR로 수정해 로딩 시간을 줄였습니다.
메인 페이지에서는 50개 이상의 이미지를 불러옵니다. 특히 가장 상단에 있는 Slider 컴포넌트는 1920 X 801 사이즈의 이미지를 여러 장 불러옵니다. 이로 인해 페이지 콘텐츠가 예기치 않게 이동하는 현상인 Layout Shift가 발생하고, 당장 보여주지 않아도 되는 이미지까지 한 번에 불러와 로딩 시간이 길어졌습니다. 이러한 문제들은 Next/Image를 통해 해결했습니다.
Next/Image는 넥스트에서 제공하는 컴포넌트로 lazy loading, 이미지 사이즈 최적화 등의 기능을 제공합니다.
lazy loading은 이미지 로드하는 시점을 필요할 때까지 지연시키는 기술입니다. 화면 밖에 있는 이미지들은 로딩을 지연시키고, 화면 안에 있는 이미지만을 로드해 필요한 이미지만 빠르게 로드합니다. Next/Image는 lazy loading을 자동으로 적용하기 때문에 별도의 설정은 하지 않아도 됩니다. (중요한 이미지에 lazy loading을 적용하고 싶지 않은 경우, Image 컴포넌트의 priority를 true로 설정하하면 됩니다.)
또한 Next/Image는 이미지 공간을 미리 만들어놓기 때문에 소스가 로드된 후 레이아웃이 흔들리는 Layout Shift도 방지할 수 있습니다.
img 태그 혹은 background image로 사용되던 이미지 소스들을 모두 Next/Image 컴포넌트로 수정했습니다. 그 결과 CLS 수치가 개선되고, 약 50개의 이미지 중 노출되는 이미지 절반만을 불러와 페이지 로딩 시간이 빨라졌습니다.
짜잔 ! 보이시나요 😭
첫 페이지 로드시 8초 이상 걸리던 수치가 모두 3초 내외로 줄어들었습니다.
사실 진작 넥스트를 공부하고 프로젝트를 진행했다면 이런 오류는 겪지 않았을텐데요 .. 소잃고 외양간 고친 덕에 이번에 공부한 내용들은 절대 잊지 못 할 것 같아요 ..
ㅇㅏ무것도 모른채 어영부영 넥스트 프로젝트를 진행한 제가 한심하긴 하지만 ㅡㅡ 성능에 대해 고민하고 넥스트를 진지하게 공부할 기회를 얻었다 생각하고 ! 개발 공부에 더욱 정진하도록 하겠습니다 ㅎㅎ 아좌좌