Vue Study

Vue2 ClientOnly ์ปดํฌ๋„ŒํŠธ ์ œ์ž‘๊ธฐ

eddie0329 2021. 7. 29. 22:35
๋ฐ˜์‘ํ˜•

๐Ÿ“Œ ๋ชฉ์ฐจ

  • ์„œ๋ก 
  • Vue SSR์˜ ๊ธฐ๋ณธ์ ์ธ ์„ค๋ช…
  • ์ฝ”๋“œ ์„ค๋ช…
  • ๊ฒฐ๋ก 
  • ์ฐธ๊ณ  ์ž๋ฃŒ

๐Ÿ“Œ ์„œ๋ก 

 

์•ˆ๋…•ํ•˜์„ธ์š”.

์ด๋ฒˆ์— ๋ผ์ด์—‡ ํ”„๋กœ์ ํŠธ๊ฐ€ vue๋ฅผ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•ํ•˜์—ฌ ssr์„ ๊ตฌํ˜„ํ•˜์—ฌ client only์˜ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์—†์—ˆ์Šต๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ํ•„์š”์— ๋”ฐ๋ผ ์ปดํฌ๋„ŒํŠธ ์ „์ฒด์˜ ๋žœ๋”๋ง ์‹œ์ ์„ ๊ฒฐ์ •ํ•˜๋Š” Nuxt์—์„œ์˜ <client-only /> ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

๊ทธ์— ๋”ฐ๋ผ ์ด๊ธ€์—์„œ๋Š” ์‚ฌ์šฉ๋ฐฉ๋ฒ• ๋ฐ ๊ตฌํ˜„ ๋ฐฉ์‹์„ ์„ค๋ช…ํ•˜๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ’ก 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๋กœ ๋™์ž‘

 

๊ทธ๋ž˜์„œ ์–ด๋–ค 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)์‹œ์ ์— ์žฌ๋žœ๋”๋ง์„ ํ•ด์ค๋‹ˆ๋‹ค.
    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์„ ๋ง‰์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์‚ฌ์šฉ๋ฐฉ๋ฒ•์€ ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค.

<!-- Comp1์€ csr -->
<client-only>
  <Comp1 />
</client-only>
<!-- Comp2๋Š” ssr -->
<Comp2 />

๐Ÿ“Œ ๊ฒฐ๋ก 

 

์ด๋ ‡๊ฒŒ ํ•œ๋ฒˆ nuxt์—์„œ์˜ client-only ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ตฌํ˜„ํ•ด๋ณด์•˜์Šต๋‹ˆ๋‹ค. ๋ฌผ๋ก  nuxt์—์„œ๋Š” placeholder์˜ ๊ธฐ๋Šฅ๊นŒ์ง€ ์žˆ์ง€๋งŒ ๋ ˆ๋ฒจ์—… ํ”„๋กœ๋•ํŠธ ๋‚ด์—์„œ๋Š” ํ•ด๋‹น ๊ธฐ๋Šฅ์ด ํ•„์š”ํ•˜์ง€ ์•Š์„๊ฒƒ ๊ฐ™์•„ ๋”ฐ๋กœ ๊ตฌํ˜„์„ ํ•ด๋†“์ง€๋Š” ์•Š์•˜์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿผ ์žฌ๋ฐŒ๊ณ  ์ฆ๊ฒ๊ฒŒ client-only๋ฅผ ์‚ฌ์šฉํ•ด์ฃผ์‹œ๊ธฐ๋ฅผ ๋ฐ”๋ผ๋ฉด์„œ ๊ธ€์„ ๋งˆ์นฉ๋‹ˆ๋‹ค.

 

๊ทธ๋Ÿผ ๊ธด๊ธ€ ์ฝ์–ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค ! ๐Ÿ™‡๐Ÿป‍โ™‚๏ธ

 

๐Ÿ“Œ ์ฐธ๊ณ  ์ž๋ฃŒ

๋ฐ˜์‘ํ˜•