Next.js 제대로 알고 사용하기🧐 적절하지 못한 SSR

Taehyun Nam·2022년 8월 27일
1

nextjs

목록 보기
1/1
post-thumbnail

안녕하세요👋 저는 마트장보고라는 서비스에서 프론트엔드를 담당하는 남태현입니다.
저희 마트장보고 앱은 Flutter로 만들어져있고, 신규 기능 개발은 Next.js 기반의 Webview를 도입하고 있습니다.
여러 가지 시행착오를 거치고 있는데, 그중 첫 번째 적절하지 못한 SSR이 불러온 이슈를 공유 드리겠습니다.
혹시라도 잘못된 정보가 있다면, 댓글로 피드백 주시면 감사하겠습니다.

첫 페이지 출력이 너무 느려요 😭

처음 Next.js를 도입한 이유는 부분적으로 SSR을 적용하여, 첫 페이지 로딩이 빠르게 출력되기를 원했습니다.
첫 페이지 로드 이후에는 CSR으로 동작하여 좋은 사용자 경험을 제공하고 싶었구요. 물론 SSG가 성능이 좋겠지만, 데이터를 자주 갱신해줘야 했습니다.

저희 마트장보고 앱에서 webview로 진입 페이지가 몇가지 있는데, 해당 페이지를 SSR으로 동작하도록 개발했습니다.
SSR 적용하는 여러가지 목적(SEO, 캐싱, 빠른 첫 페이지 등)이 있겠지만, 이번 프로젝트에서는 오로지 빠른 첫 페이지가 필요했습니다.

Next.js에서 SSR를 하게 되면, getServerSideProps에서 API를 호출하고 props로 넘겨주고 html이 만들어지면 유저는 완성된 첫 페이지를 만나게 됩니다. 반대로 생각하면, API 응답이 느리다면 첫 화면을 보는데 많은 시간이 소요될 수 있습니다. 유저는 좋지 못한 경험을 얻게 됩니다.

저희는 빠른 첫 페이지를 원했지만, SSR은 데이터를 포함해서 완성 된 첫 페이지를 제공하기 때문에 당연히 API에 영향을 받습니다.

이때, 저는 Next.js를 제대로 알지 못하고 사용하는 걸 반성하게 되고, 공식 문서를 처음부터 다시 보게 됩니다.

데이터(API)와 상관없는 부분은 바로 출력 해줘 🤖

데이터는 나중에 출력해도 되니 그냥 첫 페이지가 빠르게 출력되면 좋겠다고 생각했습니다. 그러던 중 공식 문서에서 아래 내용를 발견합니다. 참고

By default, Next.js pre-renders every page.
ℹ️ 기본적으로 Next.js는 모든 페이지를 미리 렌더링합니다.

This means that Next.js generates HTML for each page in advance, instead of having it all done by client-side JavaScript.
ℹ️ 즉, Next.js는 클라이언트 측 JavaScript에서 모든 작업을 수행하는 대신 각 페이지에 대해 미리 HTML을 생성합니다.

Each generated HTML is associated with minimal JavaScript code necessary for that page.
ℹ️ 생성된 각 HTML은 해당 페이지에 필요한 최소한의 JavaScript 코드와 연결됩니다.

When a page is loaded by the browser, its JavaScript code runs and makes the page fully interactive.
ℹ️ 브라우저에서 페이지를 로드하면 해당 JavaScript 코드가 실행되고 페이지가 완전히 대화식(interactive 가능한 상태)으로 만들어집니다.

This process is called hydration.
ℹ️ 이 과정을 hydration이라고 합니다.

필요한 부분을 정리하면, Next.js에서는 기본적으로 pre-renders를 해주고, 생성된 각 html은 해당 페이지 필요한 최소한의 JavaScript 코드와 자동으로 연결 되어서 더 나은 성능을 낸다고 합니다.

React로 CSR은 유저가 페이지를 요청하면 번들 파일을 로드 후 JavaScript에서 페이지를 렌더링하게 됩니다. document를 살펴보면 body에 아무것도 없는걸 확인할 수 있습니다. 즉, 번들 파일 커지면 커질수록 첫 페이지 로딩 속도가 느려집니다. 물론 별도의 작업을 하지 않은 상태일 경우입니다.

Next.js에서 CSR을 확인해보면, 별도의 작업을 하지 않아도 데이터(API)와 상관없는 요소(아이콘, 텍스트, input, button 등)는 이미 body에 채워진 상태로 출력됩니다. 또한 필요한 최소한의 JavaScript 코드를 자동으로 연결 해준다고 합니다. 👍

공식문서를 제대로 안 읽어 본 거죠. 결국 데이터 상관없이 빠른 첫 페이지를 얻기 위함이면, SSR보다 CSR이 더 빠르게 렌더링 될꺼라고 생각합니다. 만약 데이터가 필요 없는 페이지인데 정적 생성을 하려면 그냥 아무것도 하지 않으면 됩니다. Static Generation without data

페이지 전환이 느린것 같아요.. 🥱

CSR에서의 페이지 전환은 매우 빠릅니다. 모든 페이지를 재렌더링하지 않고 필요한 부분만 변경시켜서 좋은 유저 경험을 얻습니다.

저희는 처음에 언급해 드린 대로 SSR을 통해 빠른 첫 페이지를 출력하고, 이후에는 CSR로 빠릿빠릿하게 동작하여 좋은 유저 경험을 얻고 싶었습니다.
하지만, 부분부분 Webview 진입점에 SSR이 있어서인지 페이지 전환이 굉장히 답답하게 느껴졌습니다.

SSR이 적용된 페이지로 전환해보면 getServerSideProps 호출 되는걸 알 수 있습니다. next/link 혹은 next/router를 이용하여 페이지를 전환하면, SSR이 적용된 페이지는 Next.js가 알아서 서버에서 getServerSideProps를 호출하고 데이터를 JSON으로 반환합니다. 참고

getServerSideProps는 브라우저가 아닌 서버에서 호출하기 때문에 응답이 올 때까지 페이지가 멈춰 있는듯한 느낌을 받게 됩니다. 느린 API가 아닌데도 불구하고 답답하게 느껴졌습니다. 회사 내부적으로도 동일한 피드백을 받았구요.

첫 페이지가 아니면 getServerSideProps가 동작하지 않았으면 했는데, 항상 호출 되었습니다. 지금 생각해보면 서버 성능에도 영향을 받겠네요.

테스트한다고 getServerSideProps를 제거해 봤는데, 거짓말처럼 빠른 페이지 전환을 보여주었습니다.

나의 생각 🤫

CSR, SSR에 대한 정보를 찾아보면, 대부분이 CSR은 첫 페이지 로딩이 느리고, SSR은 첫 페이지 로딩이 빠르다. Next.js를 통해 첫 페이지는 SSR로 렌더링하고 이후 페이지는 CSR로 렌더링해서 많은 문제를 해결한다고 나와있습니다. 저 또한 그렇게 생각했구요. 하지만, 저와 같이 SSR이 필요하지 않은데, 첫 페이지 로딩이 빠르면 좋겠다고 생각한다면 이 글이 도움되지 않을까 생각합니다.

결국 이번 프로젝트에서 Next.js SSR은 아쉬운 선택이라고 생각합니다. 물론 바로 전 프로젝트 편의점 할인정보를 제공하는 페이지에서는 SSR이 유효했습니다. 편의점 할인정보는 한달동안 변하지 않아서 캐싱 처리가 되어있거든요. 각 페이지마다 적절하게 사용하면 됩니다.

추후에 더 포스팅을 하겠지만, 성능 개선을 위해 앞으로 가야할 길이 매우 많습니다.
공식문서를 보다보면 몇가지 보이는데, router.prefetch를 통해 다음 페이지를 미리 fetch하여 더 빠른 페이지 전환을 할 수 있는 것과 dynamic-import를 통해 꼭 필요한 javascript만 우선 로드하여, 첫 페이지 렌더링 성능을 개선할 수 있습니다. 지연 로딩이라고 하죠.

사람들이 많이쓰고 아무리 좋은 프레임워크라도 제대로 알지 못하고 사용한다면, 오히려 독이 되는것 같습니다.
앞으로는 공식문서를 조금 더 꼼꼼히 읽어보고 어떻게 동작하는지 왜 필요한지 정확히 알고 사용하는 개발자가 되도록 더 노력해야겠습니다.

감사합니다 🙏

profile
Frontend Developer

2개의 댓글

comment-user-thumbnail
2022년 9월 8일

남선비님 최고.....

답글 달기
comment-user-thumbnail
2023년 3월 19일

글 잘 읽었습니다. 좋은 글 감사합니다.
저랑 같은 생각을 하셨군요, 저도 Next 의 SSR이 엄청 답답하더라고요 더 빠르게 첫 페이지를 랜더링해준다고 하지만, 오히려 사용자 경험이 떨어지는것을 많이 경험했습니다.

답글 달기