상황에 따른 렌더링

CHAN·2024년 2월 15일
2

CS

목록 보기
12/14
post-thumbnail

CSR과 SSR, 그리고 상황에 따른 처리가 필요한 이유

CSR의 과정

① 사용자가 웹페이지를 요청한다.
② 서버는 요청을 받으면, 뼈대만 있는 html과 접근 할 수 있는 css/js 링크를 클라이언트에 제공한다.
③ 브라우저는 받은 HTML 파일을 다운로드하고 파싱한다. 이 HTML 파일에는 보통 애플리케이션의 기본 레이아웃 및 초기 데이터가 포함되어 있다.
④ HTML 파싱이 완료되면, 브라우저는 HTML 내에서 참조된 CSS 및 JavaScript 파일들을 추가로 다운로드한다.
⑤ CSS와 JavaScript 파일이 다운로드되면, 브라우저는 CSS를 사용하여 페이지의 스타일을 적용하고, JavaScript를 실행하여 페이지의 동적인 요소를 생성하거나 업데이트한다.
⑥ HTML, CSS, JavaScript 파일이 다운로드되고 실행된 후, 브라우저는 최종적으로 완전히 렌더링된 웹 페이지를 사용자에게 표시한다. 그리고, 사용할 수 있다.

SSR의 과정

① 사용자가 웹페이지를 요청한다.
② 서버는 요청을 받은 후, 요청된 페이지의 렌더링에 필요한 데이터를 수집하고, 서버 측 렌더링 엔진을 사용하여 초기 HTML을 생성한다.
③ 그리고, 서버에서는 HTML에 내용이 있는 즉, 렌더될 준비가 된 HTML파일을 준비한다. 그리고 브라우저에 전달한다. 추가로 CSS또한 전달한다.
④ 브라우저는 렌더될 준비가 된, 즉, 내용이 있는 HTML을 받음과 동시에 CSS을 파일도 함께 받아 적용하여 바로 웹 페이지를 사용자에게 표시한다. 그리고 JS파일을 다운로드한다.
⑤ 다운이 완료되면 JS파일을 실행시킬 수 있으며, 그때, 사용자는 사용할 수 있다.

※참고로 SSR은 보여지는것, 그리고 사용할 수 있는 것의 시간차가 길기때문에, TTV(Time to View)와 TTI(Time to Interact) 간의 시간 간격이 CSR에 비해 상대적으로, 크다라고 말한다.

브라우저 렌더링과, CSR, SSR에서 의미하는 렌더링의 차이

브라우저 렌더링에서의 렌더링 의미는 브라우저라는 프로그램이 서버로부터 HTML/CSS/JS를 받아오면, 그것을 해석, 파싱, 실행을 하고, 웹사이트를 띄워주는 과정이다. 하지만 CSR, SSR에서 사용하는 렌더링의 의미는 HTML파일안의 내용을 누가 소지하고 있느냐의 의미가 더 강하다. 즉, 브라우저 렌더링은 브라우저에서 발생하는 모든 렌더링을 의미하며, CSR과 SSR은 웹 애플리케이션에서 페이지를 생성하는 방법에 따른 차이를 나타낸다.

그럼? CSR과 SSR의 차이는

각각의 동작과정을 생각하면, 언제 웹 페이지가, 사용자에게 보여지는지가 제일 큰 차이라고 생각한다.

  • CSR의 경우에는 서버에서 빈 뼈대인 HTML파일만 제공하기 때문에 개발자 도구에서 확인을 해보면, 흰색 화면의 파일만 제공되는 것을 확인할 수 있다. 그리고, 브라우저는 예를들어, 원래 있어야할 HTML내용이 있는 chunk.js파일을 다운받와서 렌더링한다.
  • SSR의 경우에는 서버에서 이미 HTML에 내용을 채워 보여주기 때문에, 개발자 도구에서 확인을 하면, 내용이 있는 상태로 보여진다.

CSR, SSR의 특징을, 근본적인 동작과정을 통해 비교했다.

서로의 특징으로 인해, 장점이 보인다. 서비스를 개발한다면, 이러한 장점들만 빼고싶다. 그래서, 상황에 따른 적절한 CSR, SSR을 적용하는 것이다.


CSR과 SSR을 조화롭게 사용하는 예시

조화롭게 사용할 수 있는 예시를 하나 들어서 알아보자. 어떤 쇼핑몰 사이트가 있다고 가정한다.

📌홈페이지 로딩

  • 사용자가 쇼핑 서비스에 접속할 때, 초기 홈페이지는 SSR을 통해 렌더링 한다. 이는, 초기 페이지 로딩 시간이 단축되고 SEO에 유리하게 적용시키기 위함이다.

📌상품 목록 페이지

  • 사용자가 상품 목록 페이지로 이동을 한다면, CSR을 사용하여 이러한 페이지를 렌더링한다. 상품 목록 페이지는 많은 상품이 표시될 것인데, 상품 목록 페이지를 로딩한 후에 사용자가 상호작용하는 동안 서버에 추가 요청을 보내지 않고도, 필요한 부분에 따라, 무한스크롤을 내려, 요청을 효율적으로 하기 위해서이다.

📌상품 상세 페이지

  • 특정 상품을 선택하여 상세 페이지로 이동할 때는 SSR을 사용한다. 제품의 상세정보는, 자주 변경되지 않기 때문에 초기에 모든 정보를 렌더링하는 것이 효율적이다.

📌장바구니

  • 사용자가 상품을 장바구니에 계속 추가로 담게 되면, 페이지를 다시 로드하지 않고도 상호작용할 수 있어야하기 때문에 CSR을 사용한다.

사실 위의 예시는 데이터의 양, 그리고, 하나의 페이지에 속한 컴포넌트에 따라 많이 달라지지만, 개념적으로 접근을 했을때 이해하기 쉽고, 좋은 예시라 생각하여 작성했다.


실제 프로젝트에 도입했을때 구현할 방식

BLOCKER라는 프로젝트가 있다. NEXT.JS를 통해 SSR, CSR을 상황에 따라 구현할 예정이며, 간단하게 3가지를 보여주겠다.

📌최초 홈페이지

  • 비대면 계약서 서비스를 소개하는 페이지이다. 동적인 상호작용이 필요없는 페이지였기 때문에, 최초 SEO노출에 유리하기 위해, SSR을 적용할 것이다.

📌게시판

  • 실제 스크롤 반응에 따라, API 요청을 시도하며, 페이지를 불러오는 역할을 하기 때문에, CSR로 처리를 했다. 다만 게시판에 있는 JS와 관련없는 코드는, 서버사이드에 적용하여, 사용자에게 미리 띄우도록 구현하였다.

📌현황판

  • 한 번의 표시에, 전체적인 계약서 진행 상황을 보기 위한 페이지이다. 초기 로딩이 빠르면 좋지만, 한 번에, 서버로 부터 많은 데이터를 받아오는 과정에서, view와 사용자 상호작용을 동시에 하도록 하는 방안이 적절하다고 판단하여(TTV, TTI가 짧은)CSR을 사용했다.

SPA, MPA

사실 SPA, MSA에 대해 먼저 설명했어야 했다. 다만, 이번 포스팅의 목적은 CSR과 SSR의 정확한 동작 과정을 보여주기위해, 지금부터 해당 개념을 설명하겠다.

SPA

  • SPA(단일 페이지 애플리케이션)는 하나의 HTML 페이지로 구성되어 사용자가 애플리케이션을 사용하는 동안 페이지가 다시 로드되지 않는 웹 애플리케이션이다.

MPA

  • MPA(다중 페이지 애플리케이션)는 여러 개의 HTML 페이지로 구성되어 있는 웹 애플리케이션이다. 각 페이지는 서버로부터 새로운 HTML을 받아와 전체 페이지를 다시 렌더링한다.

보통 CSR과 SSR은 각각 SPA, MPA 환경에서 적용이 된다. 하지만 이들의 쌍을 동일하게 보면 안된다. 왜냐하면, 차이를 비교하는 기준이 다르기 때문이다. CSR, SSR의 경우에는 렌더링을 어디에 해서, 사용자에게 언제 보여지는지를 강조하고, SPA와 MPA의 경우, 페이지 개수에 차이가 강조되기 때문이다.


Next.js의 SSR과 SSG, 그리고 ISR

이전에 말한, BLOCKER 프로젝트는 Next.js를 도입하여, 진행한 프로젝트이다. 보통 Next.js라고 하면, ssr을 생각하지만, 이는 잘못된 발상이다. 서버클라이언트에 선언을 했다고, 모든것이 SSR이 되지 않는다. 특정한 함수를 통해, SSR을 선언하는데 우선, Next.js의 렌더링과 SSG의 개념부터 간단하게 보자. 현재는 next.js 13버전으로 인해, fetch()로 간단하게 적용이 가능하다. 아래는 이전 버전의 방식을 설명했다.

Next.js는 브라우저에 렌더링을 할 때, pre-rendering을 한다. 이는 페이지를 사전에 렌더링하여 사용자가 요청할 때마다 동적으로 생성하는 대신, 요청 시간 이전에 미리 렌더링하여 성능을 향상시키는 방법이다. 그리고 여기서, 동적으로 생성하느냐, 정적으로 생성하느냐에 따라 SSR과 SSG의 개념이 나온다.

먼저 동적으로 생성하는 것은, SSR이다. 위에서 말한 것 처럼 요청마다 페이지를 렌더링하는 방법으로, 이는 동적 콘텐츠를 제공하거나 사용자에게 맞춤형 콘텐츠를 제공해야 할 때 유용하다. 왜냐하면, 실시간으로 변경되는 데이터를 새로 렌더링을 통해 그대로 반영하기 좋기 때문이다.

반면, 정적으로 생성하는 것은, SSG이다. 개별 페이지가 미리 렌더링되고 각 요청에 대해 재사용되기 때문에, 정적 생성은 변경되지 않는 페이지, 예를 들어 블로그 포스트나 제품 목록 등에 적합하다. 이는, 이미 생성된 페이지를 반환하는 작업이기 때문에, 빠른 응답속도를 자랑한다. 그리고 Next.js에서는 SSG방식을 권장하는 편이다.

SSR사용을 위한, getServerSideProps

//page가 요청받을때마다호출되어 pre-rendering한다.

export async function getServerSideProps(context) {
  // 데이터를 가져오는 비동기 로직 수행
  const data = await fetchData();

  return {
    props: {
      data // 컴포넌트에 전달할 데이터
    }
  };
}

SSG사용을 위한, getStaticProps

// 빌드 시에 딱 한 번만 호출되고, 바로 static file로 빌드된다. 따라서, 이후 수정이 불가능하다.

export async function getStaticProps() {
  // 데이터를 가져오는 비동기 로직 수행
  const data = await fetchData();

  return {
    props: {
      data // 컴포넌트에 전달할 데이터
    }
  };
}

추가로, 방금전, 구현예시로 말한 현황판(한 번의 표시에, 전체적인 계약서 진행 상황을 보기 위한 페이지이다. 초기 로딩이 빠르면 좋지만, 서버와의 상호작용이 많았던 페이지라, 서버 부담을 줄이기 위해, CSR을 사용)의 경우 더 효율적으로 구현을 할 수 있다. 한 번에 받아야하는 api 요청에 대해서, csr을 적용하여, 사용자에게 view를 상대적으로 늦게 표시하는 것 보다, 렌더링 되기 전에 API요청으로 원하는 데이터를 불러오게 하여, 사전에 미리 pre-rendering하도록 하는 방식을 위해 SSG를 사용하는 것이다.

조금 더 응용해서, 만약 현황판이, 사전 렌더링하고, 이후에도 해당 페이지를 주기적으로 재생성하여 최신 데이터로 업데이트할 상황이 온다면, Next.js의 ISR 렌더링 기법을 사용하는 것이다.

ISR
Next.js에서는 SSG의 기법의 한계가 있었다. 이는, 생성된 이후의 데이터를 업데이트하는 것. 즉, 동적인 데이터를 다루는것에 이점이 없었다는 것이다. 이를 위해, getStaticProps 함수 내에서 revalidate 키를 사용하여 갱신 주기를 설정하여, 갱신 주기가 경과하면 Next.js는 새로운 데이터로 페이지를 재생성하는 방식으로, 사전 렌더링 및 이후 데이터 업데이트 또한 가능토록하는 기술이다.

export async function getStaticProps() {
  const posts = await getPosts(); // 게시글데이터 가져오기
  return {
    props: {
      posts,
    },
    revalidate: 60, // 60초마다 페이지를 다시 생성하여 최신 데이터로 업데이트
  };
}

마무리

예시를 들어가며, 상황에 따른 다양한 렌더링 기법을 적용하여 최적화를 고민할 수 있었떤 좋은 시간이었습니다.

profile
크게 보는 습관

0개의 댓글