SSR과 CSR을 적절히 잘 조화해서 사용해보려고 노력한 기록

이승훈·2024년 9월 1일
0

시행착오

목록 보기
22/23
post-thumbnail

들어가기 전에

RN으로 앱 개발 위주의 삶을 살아온지 어언 1년하고도 8개월...
웹 개발은 간헐적으로 어드민페이지 혹은 회사 홈페이지 위주의 개발만을 하고 있던 어느날 웹개발 프로젝트가 하나 똑 떨어졌다.
두근거리는 맘으로 시작한 오랜만의 웹개발, 그 과정에서 내가 겪는 갈등과 고민 그리고 선택에 대한 기록을 적을 계획이다.

프로젝트 설명 및 프레임워크 결정

프로젝트 설명

병원에서 종합 검진을 받은 고객은 결과를 책자형태로 받게된다.
허나 우리가 제공해주는 서비스를 이용한다면 종합 검진 결과를 카카오톡을 통해 링크를 전달받은 후,
웹페이지를 통해 자세한 결과를 더욱 보기 쉬운 형태로 확인할 수 있다.
또한 종합검진 결과에 대해 기존 책자형태에서는 제공하지 않던 AI 해석이 추가적으로 첨부된다.

프레임워크 결정

기술적으로 현재 프로젝트의 특징은 아래와 같다.

  • 첫 로그인 시 POST 요청을 제외하고 서버와는 GET 요청 이외의 통신방식은 없다.
  • 고객의 건강검진 데이터는 완성상태에서만 전달되며, 카카오톡 링크 발송시점 이후 변경되지 않는다.

뭔가 SSR을 사용하면 좋은 퍼포먼스를 낼 수 있을것같다는 느낌이 강하게 온다.
각각의 종합 검진 결과에 해당하는 설명 페이지로 들어갈 때 SSR을 통해 더욱 빠르게 렌더링해줄 수 있을것같은 그런 강한 유혹이 생긴다.

어차피 모든 검진 데이터는 고정되어있으니 각각의 모든 검진 결과 페이지를 들어갈 때 서버에서 렌더링해준다면 훨씬 빠른속도로 모든 페이지를 쾌적하게 제공해줄 수 있을것만 같은 그런 핑크빛 미래가 그려진다.

Framework는 NEXT.js로 결정

기본 구조 설계

네트워크 요청 설계

해당 프로젝트의 페이지 구성의 큰 줄기는 아래와 같다.

  1. 종합검진, 일반검진 중 하나의 항목을 선택함

  2. 선택한 검진에 따른 검진 항목 목록을 볼 수 있는 페이지

  3. 목록 중 선택한 항목의 상세정보를 볼 수 있는 페이지

종합검진 항목들의 목록을 볼 수 있는 페이지에서 각 항목을 클릭하여 상세 페이지로 들어갈 시
각 상세 항목의 정보를 네트워크 요청을 통해 받아와 서버사이드 렌더링해주는것이 첫 의도였다.

허나 이후 지속적으로 고민해본 결과 위와같은 설계는 불필요한 네트워크 요청을 여러번 발생시킨다고 판단하였다.

즉, 네트워크 요청 방법 설계를 두고 아래의 두가지 방법을 고민하였고 2번째 방법을 채택하였다.

  1. 각각의 상세 항목을 클릭하여 상세페이지로 들어갈 때 상세항목에 대해 네트워크 요청을 보내고 SSR 을 사용하여 페이지 렌더링
  2. 처음 로그인할 때 모든 건강검진정보를 가져와 zustand 상태로 저장, 이후 네트워크 요청없이 각 항목을 클릭 시 클라이언트사이드에서 저장된 정보를 가지고 CSR을 사용하여 페이지 렌더링.

선택 이유

  1. 렌더링 속도: 모든 데이터를 처음에 한 번에 가져오고 zustand에 저장하면 이후 페이지 전환 시 네트워크 요청 없이 빠르게 클라이언트 측 렌더링을 할 수 있다. 따라서 항목 간 전환이 빈번한 경우에는 이 방식이 훨씬 더 빠르고 부드럽게 느껴질 것이라 판단하였다.

  2. 네트워크 요청의 최소화: 페이지 전환 시마다 네트워크 요청을 보내는 대신 한 번에 가져오기 때문에 네트워크 요청 수가 적다. 이는 네트워크 요청이 적을수록 성능이 더 좋다는 관점에서 확실한 장점이다.

현재 데이터 규모와 사용 패턴을 고려할 때, 두 번째 방법은 충분히 합리적인 선택이라고 판단된다. 데이터가 작고 변화가 적다면 한 번에 데이터를 가져와서 클라이언트 상태로 관리하는 방식이 빠르고 효율적이기 때문이다.

만일 데이터가 증가하거나 실시간으로 변경되는 경우가 많다면 첫 번째 방법을 채택하였을 것이다.

개선 필요 사항

초기 로딩 시간 : 모든 데이터를 한 번에 가져오기 때문에 초기 로딩 시간이 상대적으로 길어질 수 있다. 특히 데이터 양이 늘어날 경우, 사용자가 첫 화면을 볼 때까지의 대기 시간이 길어질 수 있다. 현재 검진 정보의 데이터가 작다고 판단 헀지만 이후 데이터 양이 커지거나 확장될 가능성을 항상 고려해야 한다.

개선 방법

서버사이드에서 네트워크 요청을 통해 검진결과데이터를 받아온 후 클라이언트 컴포넌트로 검진결과 데이터를 전달.

import { cookies } from "next/headers";
import ComprehensiveCheckup from "./comprehensiveCheckup";

export default async function ServerSideComponent({
  searchParams,
}: {
  searchParams: { [key: string]: string | string[] | undefined };
}) {
  const checkupId = searchParams.checkupId;
  const cookieStore = cookies();
  const accessToken = cookieStore.get("accessToken")?.value;

  if (typeof checkupId === "string" && accessToken) {
     const [comprehensiveCheckupResponse, generalCheckupResponse] =
      await Promise.all([
        fetch(`${API_URL}/v1/checkup/comp/${checkupId}`, {
          credentials: "include",
          headers: {
            Cookie: `accessToken=${accessToken}`,
          },
        }),
        fetch(`${API_URL}/v1/checkup/general/${checkupId}`, {
          credentials: "include",
          headers: {
            Cookie: `accessToken=${accessToken}`,
          },
        }),
      ]);

    const comprehensiveData = await comprehensiveCheckupResponse.json();
    const generalData = await generalCheckupResponse.json();

    if (comprehensiveData?.statusCode === 404) {
      return <ClientSideRootComponent />;
    }

    return (
      <ClientSideRootComponent
        comprehensiveData={comprehensiveData.data}
        generalData={generalData.data}
      />
    );
  }

  return <ClientSideRootComponent />;
}
  1. URL에 checkupId가 있다면 서버사이드에서 네트워크 요청을 통해 검진결과를 서버사이드에서 요청 후 클라이언트 컴포넌트로 전달한다.
  2. URL에 checkupId가 없다면 클라이언트 컴포넌트에 별도의 검진결과 Props를 전달하지 않고 클라리언트 컴포넌트에서 별도로 네트워크 요청을 통해 검진결과를 받아온다.
  3. 클라이언트 컴포넌트로 전달된 검진결과는 또한 Zustand에서 관리하는 상태에 저장된다.
  4. Zustand에 검진결과 데이터가 존재한다면 클라이언트 사이드에서는 네트워크 요청을 진행하지 않는다.

기대 효과

  • 네트워크 요청 시점의 차이:

    • 클라이언트 사이드에서의 데이터 요청은 모든 HTML, CSS, JS가 다운로드되고 브라우저에서 JavaScript가 실행된 후 비로소 네트워크 요청이 시작되므로, 상대적으로 데이터 로딩이 늦어질 수 있습니다.
    • 반면, 서버사이드에서 네트워크 요청을 처리하면 Next.js의 SSR 덕분에 서버에서 데이터를 가져와 페이지를 완전히 준비한 상태로 클라이언트에게 전달하기 때문에 클라이언트에서는 이미 데이터를 받은 상태에서 렌더링이 진행됩니다. 이는 사용자의 초기 렌더링 경험을 상당히 개선하는 효과가 있습니다.
  • 빠른 첫 번째 렌더링 (FCP: First Contentful Paint): SSR을 통해 페이지가 로드되면 초기 렌더링 속도가 빨라지고, 사용자가 페이지에 도달했을 때 이미 데이터가 채워진 상태이므로 콘텐츠의 빈틈이나 지연이 거의 없을것으로 기대됨.

  • 네트워크 환경에 대한 이점: 만약 사용자가 느린 네트워크 환경에서 페이지에 접근할 경우, 클라이언트 측 네트워크 요청보다 서버사이드에서 데이터를 미리 가져와 렌더링하는 것이 훨씬 더 유리할 수 있다고 기대됨.

profile
Beyond the wall

0개의 댓글