Nextjs13의 Pre-rendering의 장점[Nextjs]

김대운·2023년 4월 14일
0

Nextjs

목록 보기
3/4
post-thumbnail

먼저 CSR이란 ?

CSR (Client-Side Rendering)은 클라이언트(브라우저)에서 JavaScript를 이용하여 동적으로 페이지를 생성하는 방식입니다. CSR은 브라우저에서 서버로부터 받은 최소한의 HTML, CSS, JavaScript를 기반으로 페이지를 로드하고, 그 후에 클라이언트 사이드에서 JavaScript를 사용하여 서버와 데이터 통신을 수행하고, 페이지 컨텐츠를 동적으로 생성합니다.

CSR의 동작 과정은 다음과 같습니다.

  1. 브라우저가 서버에 요청을 보냅니다.
  2. 서버는 요청을 받아 HTML, CSS, JavaScript 등을 포함한 정적 파일들을 클라이언트에 전송합니다.
  3. 브라우저는 받은 파일들을 파싱하고 화면에 렌더링합니다.
  4. 클라이언트 측에서 JavaScript를 사용하여 서버에 데이터를 요청하고, 필요한 데이터를 받아와 페이지를 업데이트합니다.

즉, CSR은 초기 로딩 시간이 빠르고, 서버 부하가 적은 장점이 있지만, 페이지 로드 후에 추가적인 데이터를 가져오기 위해 클라이언트 측에서 서버와 데이터 통신을 수행하므로 초기 로딩 이후에는 느린 속도와 불안정성을 보일 수 있습니다.
또한, SEO에 대한 문제가 발생할 수 있습니다.

그럼 SSR이란 ?

SSR(Server-Side Rendering)은 클라이언트의 요청에 대해 서버 측에서 HTML, CSS, JS 등의 리소스를 이용하여 완전한 페이지를 구성한 후에 클라이언트에게 전달하는 방식입니다. 이는 CSR(Client-Side Rendering)과 대조적입니다.

SSR의 동작 과정은 다음과 같습니다.

  1. 사용자가 서버로 요청(Request)을 보냅니다.
  2. 서버는 요청에 대한 HTML, CSS, JS 등의 리소스를 구성하여 완전한 페이지를 생성합니다.
  3. 서버에서 생성한 페이지를 클라이언트에게 전달(Response)합니다.
  4. 클라이언트는 전달받은 페이지를 보여주고, 필요한 경우 추가적인 데이터나 리소스를 요청합니다.

이렇게 SSR을 이용하면 초기 로딩 속도가 느릴 수 있지만, 검색 엔진 최적화(SEO)나 웹 사이트의 안정성을 향상시키는데 도움이 됩니다. 또한, 페이지를 클라이언트 측에서 구성하지 않기 때문에 보안성도 향상됩니다.

잠깐 SSR도 초기로딩 속도가 빠르다고 했던 기억이 있는데 ?

일반적으로 SSR은 최초 페이지 즉, 첫페이지 로딩속도가 빠릅니다.
반면에 CSR은 자바스크립트가 실행되어야 페이지를 렌더링하므로 빈 화면을 먼저 보여주게 되기 때문에 첫 번째 페이지 로드 속도가 느릴 수 있습니다.

그러나, CSR에서는 사용자가 다른 페이지로 이동하면 새로운 페이지에 대한 데이터만 요청되므로 더 빠르게 이동할 수 있습니다. SSR에서는 모든 페이지 요청마다 서버 측에서 페이지를 다시 렌더링해야 하므로 이동하는 데 시간이 더 소요 됩니다.

따라서, 초기 페이지 로드 속도에 초점을 맞추는 경우 SSR이 더 나은 선택일 수 있지만, 사용자가 다른 페이지로 자주 이동하는 경우 CSR이 더 나은 선택일 수 있습니다.

Nextjs는 SSR의 장점과 CSR장점을 합쳤다 ?
Next js는 기본적으로 첫 페이지를 서버 사이드 렌더링 방식으로 처리해 첫 페이지 로딩이 빠릅니다. 또 추후에 다른 페이지로 이동할 때는 CSR방식으로 브라우저에서 처리하기 때문에 SPA의 장점을 유지 가능합니다.

Pre-rendering과 No Pre-rendering

다시 정리하자면

No Pre-rendering(또는 Client-side rendering)은 사용자의 브라우저에서 JavaScript를 이용해 웹 페이지를 동적으로 생성하는 방식입니다.

즉, 사용자의 요청이 들어올 때 서버는 페이지의 구조와 스타일에 해당하는 HTML, CSS 파일과 함께 JavaScript 파일을 보내어 클라이언트 측에서 페이지를 구성합니다. 이 방식은 초기 로딩 시간이 빠르고, 인터랙티브한 기능이 많은 애플리케이션에 적합합니다.

하지만 No Pre-rendering 방식은 검색 엔진 최적화(SEO)에 취약합니다. 검색 엔진 크롤러는 HTML 파일을 기반으로 검색 결과를 만드는데, 클라이언트 측에서 페이지를 생성하는 경우 HTML 파일을 생성하지 않기 때문에 검색 결과에 노출되지 않을 수 있습니다.

반면, Pre-rendering(또는 Server-side rendering)은 서버에서 웹 페이지를 렌더링하고, HTML 파일을 생성하는 방식입니다. 사용자의 요청이 들어오면, 서버는 미리 렌더링한 HTML 파일을 보내어 초기 로딩 속도가 빠르며, 검색 엔진에서도 HTML 파일을 인식하여 검색 결과에 노출됩니다.

하지만 인터랙티브한 기능이 많은 페이지의 경우 클라이언트 측에서 추가적인 JavaScript 파일을 로딩해야 하므로 초기 로딩 속도가 느릴 수 있습니다.

Next.js는 Pre-rendering 방식 중에서도 정적 생성(Static Generation)과 서버 사이드 렌더링(Server-side Rendering)을 모두 지원하여, 초기 로딩 속도와 검색 엔진 최적화(SEO) 모두를 고려한 웹 애플리케이션을 개발할 수 있습니다.

Nextjs의 Pre-rendering

13버전 이전까지는 getServerSideProps 나 getStaticProps 와 같은 pre-rendering 메서드를 사용 해서 pre-rendering을 하였지만 13버전부터는 그럴 필요가 없어졌습니다.
(참고: Next.js Next.js의 프리 렌더링(pre-rendering) 옵션 3가지 / SSG, SSR, ISR)

왜냐하면 모든 컴포넌트가 서버에서 렌더링 되도록 바뀌었기 때문입니다!

app/page.tsx 
import Container from '@/app/components/Container'
import ListingCard from '@/app/components/listings/ListingCard'
import EmptyState from '@/app/components/EmptyState'

import getListings, { IListingsParams } from '@/app/actions/getListings'
import getCurrentUser from '@/app/actions/getCurrentUser'
import ClientOnly from './components/ClientOnly'

interface HomeProps {
  searchParams: IListingsParams
}

const Home = async ({ searchParams }: HomeProps) => {
  const listings = await getListings(searchParams)
  console.log(listings)
  const currentUser = await getCurrentUser()

  if (listings.length === 0) {
    return (
      <ClientOnly>
        <EmptyState showReset />
      </ClientOnly>
    )
  }

  return (
    <ClientOnly>
      <Container>
        <div
        ... 
        </div>
      </Container>
    </ClientOnly>
  )
}

export default Home

위 코드를 보면 getServerSideProps 나 getStaticProps 와 같은 pre-rendering 메서드를 사용하지 않고 데이터를 가져와서 listings를 console.log하였는데, 콘솔이 서버에 찍혔습니다.
그 의미는 서버에서 렌더링 됐다는 의미죠!

모든 컴포넌트가 서버에세 렌더링 되면 client에서 사용되는 useEffect나 useState, onClick이벤트는 어떻게 사용 하죠?? 라는 의문이 생깁니다.

app/error.tsx
'use client';

import { useEffect } from "react";

import EmptyState from "@/app/components/EmptyState";

interface ErrorStateProps {
  error: Error
}

const ErrorState: React.FC<ErrorStateProps> = ({ error }) => {
  useEffect(() => {
    console.error(error);
  }, [error]);

  return ( 
    <EmptyState
      title="Uh Oh"
      subtitle="Something went wrong!"
    />
   );
}
 
export default ErrorState;

다음과 같이 코드 첫줄에 'use client'를 작성하면 해당 파일은 클라이언트 컴포넌트로 취급하고 컴파일 됩니다. 굉장히 간단합니다 !

next js 공식 문서를 보시면 언제 서버컴포넌트를 사용하고 언제 클라이언트 컴포넌트를 사용해야되는지 나와 있습니다.

데이터를 가져오거나 백엔드 자료에 접근 할때는 서버 컴포넌트를 사용하고 onClick, onChange 같이 클라이언트단에서 user와 동적으로 상호작용 할 때는 클라이언트 컴포넌트를 사용하라고 나와있습니다.

또 공식문서에 따르면 우선 위와 같은 기준을 두고, 서버 컴포넌트와 클라이언트 컴포넌트를 각자 기능에 맞게 섞었는 것이 좋다고 합니다. 이를 통해 클라이언트 측에서는 더욱 빠르게 렌더링되고, 서버 측에서는 SEO 및 초기 렌더링과 같은 이점을 제공하기 때문입니다.

코드 예제를 통해 어떤식으로 써야되는지 살펴보겠습니다 !

/app/listings/[listingId]/page.tsx

import getCurrentUser from '@/app/actions/getCurrentUser'
import getListingById from '@/app/actions/getListingById'
import getReservations from '@/app/actions/getReservations'

import ClientOnly from '@/app/components/ClientOnly'
import EmptyState from '@/app/components/EmptyState'

import ListingClient from './ListingClient'

interface IParams {
  listingId?: string
}

const ListingPage = async ({ params }: { params: IParams }) => {
  const listing = await getListingById(params)
  const reservations = await getReservations(params)
  const currentUser = await getCurrentUser()

  if (!listing) {
    return (
      <ClientOnly>
        <EmptyState />
      </ClientOnly>
    )
  }

  return (
    <ClientOnly>
      <ListingClient
        listing={listing}
        reservations={reservations}
        currentUser={currentUser}
      />
    </ClientOnly>
  )
}

export default ListingPage

위 코드는 동적인 페이지 렌더링을 위한 코드입니다.

ListingPage는 라우트 페이지로, ListingClient는 해당 페이지에 보여줄 클라이언트 사이드에서 실행될 컴포넌트입니다.

getListingById와 getReservations 함수를 이용하여 페이지에 필요한 데이터를 불러와 ListingClient에 props로 전달합니다.

getCurrentUser 함수를 이용하여 현재 사용자 정보를 가져오고, ClientOnly 컴포넌트를 이용하여 서버 사이드에서는 실행되지 않는 클라이언트 사이드에서만 실행되는 코드를 작성합니다.

이렇게 서버 컴포넌트와 클라이언트 컴포넌트를 혼합하여 사용하는 이유와 장점은 다음과 같습니다.

  1. 검색 엔진 최적화 (SEO)
    서버 사이드 렌더링을 사용하면 페이지가 검색 엔진 크롤러에 의해 수집되기 쉽고, 검색 엔진에서 인덱싱 및 노출될 때 더욱 우선순위를 가지게 됩니다. 이는 클라이언트 사이드 렌더링을 사용할 때 어려울 수 있는 SEO 문제를 완화할 수 있습니다.

  2. 초기 로딩 시간 단축
    서버 사이드 렌더링은 페이지의 초기 로딩 시간을 단축시킵니다. 서버 사이드 렌더링을 사용하면 페이지가 최초 요청 시 클라이언트에서 렌더링되기 전에 서버에서 초기 HTML 마크업을 보내줍니다. 이것은 사용자가 페이지를 볼 때 필요한 최소한의 내용을 빠르게 로드하여 사용자 경험을 향상시킵니다.

  3. 유연한 데이터 로딩
    Next.js의 getStaticProps, getServerSideProps, getStaticPaths 등을 통해 미리 렌더링된 페이지에 데이터를 쉽게 제공할 수 있습니다. 또한 클라이언트 측에서 필요한 데이터를 렌더링 후 로드할 수 있습니다. 이것은 초기 로딩 시간을 단축하면서도 페이지의 유연성과 동적인 데이터를 제공할 수 있는 이점을 동시에 얻을 수 있게 해줍니다.

  4. 더 나은 사용자 경험
    서버 사이드 렌더링을 사용하면 사용자 경험이 향상됩니다. 페이지가 초기 로딩 시간을 단축하면서도 검색 엔진 최적화, 유연한 데이터 로딩 등의 이점을 동시에 얻기 때문입니다. 이러한 이점들은 최종적으로 사용자가 더 나은 웹 사이트 경험을 얻을 수 있게 해줍니다.

0개의 댓글