본문 바로가기
> 레벨업의 테크노트

[프론트엔드] ClientOnly 컴포넌드 개발 여행

by 알 수 없는 사용자 2022. 1. 12.

📌 목차

  • 서론
  • Vue SSR의 기본적인 설명
  • 코드 설명
  • 결론
  • 참고자료

📌 서론

안녕하세요. 프론트엔드 개발자로 일하는 최종선이라고 합니다.

저희 회사는 Nuxt를 사용하는 대신 Vue를 자체 커스터마이징을 하여 SSR을 구현하여 사용하고 있습니다.
따라서 필요에 따라 컴포넌트 전체의 랜더링 시점을 결정하는 Nuxt에서의 컴포넌트를 따로 제작하게 되었습니다.

그에 따라 이 글에서는 사용방법 및 구현 방식을 설명하고자 합니다. 그럼 재밌게 읽어주세요~! 🙇🏻‍♂️

💡 Nuxt에서 client-only 컴포넌트란?
ClientOnly 컴포넌트는 자식요소들을 client-side에서 랜더링 시킵니다.

📌 Vue SSR의 기본적인 설명

일단 해당 컴포넌트에 대해서 조금 더 깊게 이해를 하자면 먼저 customized vue ssr 부분을 먼저 이해를 해야 합니다.

보통 SSR일 때는 server에서 데이터를 조합하여 만든 static 한 HTML 파일이 하나가 떨어지게 됩니다. 그리고 Client side에 왔을 땐 Client Bundle로 이제 javscript code 삽입 (hydrate)을 하게 됩니다.

Server Side → Static HTML → Client Side → HTML에 script 코드 삽입 (hydrate) → 이후 모든 동작은 CSR(SPA)로 동작

그래서 어떤 side이냐에 따라 컴포넌트의 생명주기는 이렇게 됩니다.

  Server Side Client Side
beforeCreate V  
create V  
beforeMount   V
mount   V

📌 코드 설명

이제 한번 전체 코드를 살펴볼까요? 일단 전체 코드는 이렇게 짜여있습니다.

export default {
  name: 'ClientOnly',
  functional: true,
  render(h, { parent, slots }) {
    const { default: defaultSlots = [] } = slots();

    if (parent._isMounted) {
      return defaultSlots;
    }

    parent.$once('hook:mounted', () => {
      parent.$forceUpdate();
    });
    return defaultSlots.length > 0 ? defaultSlots.map(() => h(false)) : h(false);
  },
};

그럼 한 줄 한 줄씩 살펴보겠습니다.

render(h, { parent, slots }) {
    // ⭐️ 먼저 slots에 해당하는 것들을 배열로 선언해 줍니다.
    // ex>
    // <client-only>
    //   <Comp1/>
    //   <Comp2/>   
    // </client-only>
    // defaultSlots = [Comp1, Comp2]
    const { default: defaultSlots = [] } = slots();
  },
};

 

 

render(h, { parent, slots }) {
    // ⭐️ 위에서 본 것처럼 mounted가 호출이 된다면,
    // 이미 client side이기 때문에 그냥 children을 render 합니다.
    if (parent._isMounted) {
      return defaultSlots;
    }
  },
};

 

 

render(h, { parent, slots }) {
    // ⭐️ 만약 위 if (parent._isMounted)에서 걸러지지 않았다면 지금은 server side입니다.
    // 그래서 parent가 mounted된 시점(client-side)에 re-render을 해줍니다.
    parent.$once('hook:mounted', () => {
      parent.$forceUpdate();
    });
  },

 

 

render(h, { parent, slots }) {
    // ⭐️ 위 if (parent._isMounted)에서 걸러지지 않았으니 
    // slot으로 들어온 아이들의 render를 막아줍니다.
    return defaultSlots.length > 0 ? defaultSlots.map(() => h(false)) : h(false);
  },
};

이렇게 해주어서 server-side의 rendering을 막을 수 있습니다. 그리고 사용방법은 간단합니다.(Nuxt와 사용 방법은 동일)

<!-- Comp1은 csr -->
<client-only>
  <Comp1 />
</client-only>
<!-- Comp2는 ssr -->
<Comp2 />

그럼 저희 프로젝트에 Header에 ClientOnly를 한번 걸어볼까요?

📌 결론

이렇게 한번 nuxt에서의 client-only 컴포넌트를 구현해 보았습니다. 물론 nuxt에서는 placeholder의 기능까지 있지만 레벨 업 프로덕트 내에서는 해당 기능이 필요하지 않을 것 같아 따로 구현을 해놓지는 않았습니다. 그럼 재밌고 즐겁게 client-only를 사용해 주시기를 바라면서 글을 마칩니다.

 

그럼 긴글 읽어주셔서 감사합니다 ! 🙇🏻‍♂️

📌 참고자료

댓글