더 많은 포스팅 보기 자세히보기

프론트엔드/React | Next

SSR 서비스에서 반응형 웹 개발 이야기(SSR이 필요한 이유)

유도혁 2022. 10. 1. 16:21

개요

useWindow라는 커스텀 훅을 만들어 isMobile, isTablet, isDesktop 으로 컴포넌트나 props를 분기시키며 개발하고 있었다.

// useWindow.ts

const useWindow = () => {
  const isMobile = useRef<boolean>(false);
  const isTablet = useRef<boolean>(false);
  const isDesktop = useRef<boolean>(false);
  const isClient = useRef<boolean>(false);

  const [windowSize, setWindowSize] = useState<{ width: number; height: number }>({
    width: 0,
    height: 0,
  });

  useEffect(() => {
    if (typeof window !== 'undefined') {
      isClient.current = true;
      const handleResize = () => {
        setWindowSize({
          width: window.innerWidth,
          height: window.innerHeight,
        });

        isMobile.current = window.innerWidth < deviceWidthNumber.tablet;
        isTablet.current =
          window.innerWidth > deviceWidthNumber.tablet &&
          window.innerWidth < deviceWidthNumber.desktop;
        isDesktop.current = window.innerWidth > deviceWidthNumber.desktop;
      };

      window.addEventListener('resize', handleResize);

      handleResize();

      return () => window.removeEventListener('resize', handleResize);
    }
  }, []);

  return {
    ...
  };
};

export default useWindow;

단순하게 위 사진처럼, PC 화면에서는 aside 화면이 노출되는 상태이고, 모바일 화면에서는 햄버거 메뉴가 추가되어 이를 클릭하였을 때 aside 화면을 확인할 수 있는 구조이다.

useWindow를 사용하였을 경우 브라우저 화면을 늘리고 줄일 때마다 UI가 즉시 반영된다. 하지만 이는 모든 초기 렌더링이 끝났을 경우의 이야기이고, 아래의 각 서비스 환경에 따라 초기 렌더링 시의 보이는 화면에 대한 차이를 이야기해보려고 한다.

각 초기 렌더링

CSR과 useEffect();

기본적인 CSR의 경우 첫 화면을 불러올시, 깡통 index.html파일과 함께 해당 페이지에대한 번들링된 bundle.js 파일을 클라이언트에 던져주게 된다. 이는 JavaScript 파일이 동작하기 이전까지 사용자는 빈 화면을 볼 것이고, 크롤링 봇의 경우는 유용한 페이지가 아니라고 인식하고 지나갈 것이다. 물론 JavaScript가 동작하고나면, 사용자가 어느 액션을 취한다거나 같은 서비스 내의 페이지를 이동시 화면이 빠르게 변경되는 것을 확인할 수 있을 것이다.

CSR useLayoutEffect();

여러 초급 React 개발자들이 자주 하는 실수가 적절한 상황에 적절한 hook을 사용하지 않는 것이다. hook을 많이 알고 있는 것에 따라 서비스의 리소스 최적화에 어느 정도 영향을 끼치는 것 같다. 지금과 같은 상황에서도 useWindow라는 커스텀 훅을 useEffect로 사용 중이다. 이를 useLayoutEffect로만 바꾸어주어도, 초기 렌더링 속도를 꽤 줄일 수 있다.

💡 useEffect - Data측면의 상태관리를 할때 주로 사용한다.

💡 useLayoutEffect - 화면에 영향이 있는 상태관리시 주로 사용한다.

SSR과 useEffect();

SSR(server side rendering) 말 그대로 client에서 요청을 하면, CSR과는 다르게 server에서 JavaScript를 동작시켜, 1차적으로 렌더링을 끝낸 화면과 bundle.js파일을 client로 보내주게된다. 하지만, 이 과정에서 client server에서는 요청한 clientwindow size를 알 수 없기 때문에 SSR이 원하는 대로 이루어지지 않고, client에 렌더링 된 파일을 보내준다. 그 이후 동작은 기존 CSR과 같다.

가끔 SSR이 왜 필요한지, 예전의 SSR과 다른게 뭔지 질문을 받아보았는데 이참에 필요한 이유를 정리해보려고 한다.

예전의 SSR

server에서 html template파일을 갖고 있다가, client에서 요청시 필요한 templateresponse로 보내준다.

이는 페이지를 이동할 때 마다 비슷한 UI가 있어도 다시 모든 파일을 보내준다. 상태관리나 api call의 경우 주로 jQuery나 Ajax로 하게되는데, 객체지향 프로그래밍이 어렵고, 코드가 전혀 암호화되지 않기 때문에 보안적으로 매우 취약한 상태가된다.

CSR

client에서 요청 시 해당 프로젝트를 갖는 저장소에서 index.html과 빌드 및 번들링 된 파일을 보내준다. 초기 렌더링이 조금 느리지만, 페이지 이동 시 같은 화면은 놔두고, 필요한 파일만 받아와서 화면을 업데이트할 수 있게 되었으며, 예전의 SSR과 비교해서 코드를 객체 지향적으로 작성할 수 있게 되었고, 성능 최적화에 많은 이점을 얻게 되었다. 하지만, 기본적으로 index.html에 넣어둔 opengraph정보 말고는 크롤링 봇이 갖고갈 수 있는 정보가 전혀 없기 때문에, 다른 사이트들보다 좋은 콘텐츠를 갖고 있다고 해도 포털사이트들의 검색 결과에 상위노출 시키기가 어렵고, 다양한 소셜서비스에서 바이럴 효과를 얻고 싶어도, 미리보기 화면이 원하는 대로 만들어지지 않기 때문에, 클릭률이 낮아질 수밖에 없다.

CSR Pre-Rendering

CSR의 단점을 보완하기 위해서 만들어진 기술로, 프로젝트 빌드 시점에 어느 정도의 원하는 페이지들을 미리 만들어두고, client 요청 시 미리 만들어둔 파일을 전달해준다. 당연하게도 초기 렌더링 시간이 없어지고, 크롤링 봇또한 의도한 콘텐츠를 읽어 들여 포털사이트의 검색 결과의 순위가 올라갈 수도 있고, 소셜서비스의 바이럴 효과 클릭률도 올라가게 된다.

“그럼 Pre-Rendering 하면 되지, 왜 신규 SSR이 나와서 저장소가 아니라 서버를 써야 하냐?””라고 묻는다면, 만약 우리 서비스에서 잘 보여주고 싶은 페이지가 1억 개를 가지고 있고, 그것을 모두 Pre-Rendering 하게 된다고 한다면, 빌드 시간이 엄청나게 불어나게 되고, 어느 페이지의 콘텐츠가 업데이트되었다고 한다면, 이미 Pre-Rendering 된 파일은 업데이트되지 않는다.

신규 SSR

위 모든 단점이 보완되어 즉, 객체 지향적으로 프로그래밍할 수 있고, 코드를 쉽게 암호화할 수 있으며, 기본적인 콘텐츠를 client server가 서비스의 server에서 불러온 다음에 렌더링하여 크롤링 봇의 인식 및 소셜 바이럴에 문제가 없고, 페이지 이동 시 필요한 파일만 줄 수 있고, Pre-Rendering 기술도 사용할 수 있어서 저장된 페이지를 바로 client에 건네줄 수 있으며, 한 번 요청된 페이지를 캐싱시키고 캐싱 된 페이지를 다음부턴 바로 보내줄 수도 있다.

추가로 더 알아본다면, react의 최신 기술인 hydration이라는 기술이 있는데, 사용자가 보고 있는 컴포넌트만 client에 보내주고, 스크롤 등 화면을 이동 시에 추가로 필요한 컴포넌트를 받아와서 렌더링시키는 방법이다. 이를 SSR에 적용하면, 크롤링 봇이 hydration 된 컴포넌트를 인식하지 못한다고 생각할 수 있는데, 화면 렌더링은 시켜두되 해당 컴포넌트의 번들링 된 JavaScript 파일은 화면상에 보였을 때 받아오는 신기술이 있다.

https://nextjs.org/docs/advanced-features/dynamic-import

 

Advanced Features: Dynamic Import | Next.js

Dynamically import JavaScript modules and React Components and split your code into manageable chunks.

nextjs.org

즉, nextjs는 무적이고 최강이다.

SSR과 useLayoutEffect();

SSR과 css @media screen

결국 SSR에서 반응형 웹 개발을 할 때 hook의 의존성을 줄이고, css@media screen으로 개발하면, 더욱 나은 SSR 개발을 할 수 있다.