์ฝ”๋“œ ์ตœ์ ํ™”

2022. 1. 26. 22:38ใ†์นดํ…Œ๊ณ ๋ฆฌ ์—†์Œ

๐Ÿ”ฎ ์ฝ”๋“œ ์„ฑ๋Šฅ ์˜ํ–ฅ ์š”์†Œ ์ ๊ฒ€ ์ˆ˜์น™

  1. ํŒจํ‚ค์ง€ ์ตœ์ ํ™”
    • ๋ฒˆ๋“ค ์‚ฌ์ด์ฆˆ ์ธก์ • ๊ฒฐ๊ณผ ๋ถ„์„
    • ๋นŒ๋“œ ์‹œ๊ฐ„๊ณผ ๋ฒˆ๋“ค๋ง ์‚ฌ์ด์ฆˆ์— ์˜ํ–ฅ
    • ์›น ํŽ˜์ด์ง€ ๋ฆฌ์†Œ์Šค ๋กœ๋”ฉ ์†๋„ ๊ฐœ์„  ๊ธฐ๋Œ€
  2. ๋ Œ๋”๋ง ์ตœ์ ํ™” 
    • Page ๋‹จ์œ„ Server-side Data Fetching ํ™œ์šฉ
    • ์œ ์ €์˜ ๋Œ€๊ธฐ์‹œ๊ฐ„ ์ตœ์ ํ™”
    • ๋ Œ๋”๋ง ์†๋„(์›น ํ™”๋ฉด ๊ทธ๋ ค์ง€๋Š” ๋กœ์ง) ๊ฐœ์„  ๊ธฐ๋Œ€
  3. ๋กœ์ง ์ตœ์ ํ™”
    • Memory Leak ๋“ฑ ์„ฑ๋Šฅ ์˜ํ–ฅ ๋กœ์ง ์ˆ˜์ •
    • ๋ธŒ๋ผ์šฐ์ € ์ƒํ˜ธ์ž‘์šฉ ์†๋„ ๊ฐœ์„  ๊ธฐ๋Œ€

 

์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„  ๊ฐœ๋ฐœ ๊ณผ์ •์—์„œ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ฒˆ๋“ค ์‚ฌ์ด์ฆˆ ์ตœ์ ํ™”๋ฅผ ๋‹ค๋ฃน๋‹ˆ๋‹ค.


๋ฒˆ๋“ค ์ตœ์ ํ™”

01. ์›์ธ ๋ถ„์„

๋ฒˆ๋“ค ์‚ฌ์ด์ฆˆ๋ฅผ ๋ถ„์„ํ•  ์ˆ˜ ์žˆ๋Š” ๋‹ค์–‘ํ•œ ๋„๊ตฌ๋“ค์ด ์กด์žฌํ•œ๋‹ค.

ํ”„๋กœ์ ํŠธ ๋‚ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ณ„ ๋น„์ค‘์„ ํŒŒ์•…ํ•ด๋ณด๊ณ  ๊ทธ์— ๋งž๋Š” ์ตœ์ ํ™” ์ง„๋‹จ์„ ์‹œ์ž‘ํ•ด๋ณด์ž.

  • Webpack Analyse ( ๊ฐ€์žฅ ๋‹ค์–‘ํ•œ ์ •๋ณด ์ œ๊ณต but ์‚ฌ์šฉ๋ฒ• ๋ณต์žก & ๋Š๋ฆผ )
  • Visualizer ( ๊น”๋”ํ•œ ์‹œ๊ฐํ™” but ๊ธฐ๋Šฅ ๋ถ€์กฑ )
  • Bundler Analyzer ( ์›์ธ ๋น ๋ฅด๊ฒŒ ์ฐพ๊ธฐ ์œ„ํ•ด ์ถ”์ฒœ )
    • ์šฉ๋Ÿ‰๋ณ„๋กœ ์‹œ๊ฐํ™” ์ œ๊ณต ์–ด๋–ค ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋งŽ์ด ์‚ฌ์šฉ๋˜๊ณ  ์žˆ๋Š”์ง€ ํŒŒ์•… ๊ฐ€๋Šฅ

 

- ๋‚ด๊ฐ€ ์ƒ๊ฐํ•˜๋Š” ๋ฒˆ๋“ค ์ตœ์ ํ™” : More lighter, Minimize duplication, code Optimization, write only you use

 

 

02. ๋ฒˆ๋“ค ์‚ฌ์ด์ฆˆ๋ฅผ ๊ณ ๋ คํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ ์ •

์›์ธ ๋ถ„์„์— ๋„๋‹ฌํ•˜๊ธฐ ์ „, ๊ฐœ๋ฐœ ๊ณผ์ •์—์„œ ์‹ ๊ทœ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜ ์‹œ ๋ฒˆ๋“ค ํ”ผ๋””์•„(https://bundlephobia.com/) ์‚ฌ์ดํŠธ์— ์ ‘์†ํ•˜์—ฌ ๋™์ผํ•œ ๊ธฐ๋Šฅ์„ ์ง€์›ํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ์—†๋Š”์ง€ ์šฉ๋Ÿ‰์„ ๋น„๊ตํ•˜๊ณ , ์˜์กด์„ฑ์ด๋‚˜ ํŠธ๋ฆฌ์‰์ดํ‚น ์ง€์› ์—ฌ๋ถ€, ๊ฐ ๋ฒˆ๋“ค์‚ฌ์ด์ฆˆ ํ™•์ธํ•ด ๊ฐ€๋ฒผ์šด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ ์ •ํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ณ ๋ คํ•ด์•ผํ•œ๋‹ค. (์ด๋Ÿฐ๊ฑธ ๊ณ„์† ๊ณ ๋ คํ•˜๋‹ค๋ณด๋ฉด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ณ‘์ด ์ƒ๊ฒจ๋ฒ„๋ฆฐ๋‹ค. ์‚ฌ์‹ค ๋ชจ๋“ ๊ฑด ์ˆœ์ˆ˜ ์ฝ”๋“œ๋กœ ํ•ด๊ฒฐํ•  ์ˆœ ์žˆ์œผ๋‹ˆ..ใ…Ž ๊ทธ๋Ÿผ์—๋„ ๋„์ž…์ „ ์ž…์ฃผ ์˜ˆ์ • ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์— ๋Œ€ํ•ด '๊ณผ์—ฐ ํ•„์š”ํ•œ๊ฒƒ์ธ๊ฐ€์—' ๋Œ€ํ•œ ๋‹ค๋ฐฉ๋ฉด์ ์ธ ๊ณ ๋ฏผ์ด ํ•„์š”ํ•˜๋‹ค.)

๋ฒˆ๋“ค ์‚ฌ์ด์ฆˆ ๋ฐ ๋ฒ„์ „๋ณ„ ์šฉ๋Ÿ‰ ๋น„๊ต, ๋„คํŠธ์›Œํฌ์— ๋”ฐ๋ฅธ ๋‹ค์šด๋กœ๋“œ ์†๋„ ์ •๋ณด ์ œ๊ณต
tree-shaking์ด ๋˜์—ˆ์„ ๋•Œ ํ•จ์ˆ˜๋ณ„ ์šฉ๋Ÿ‰ ๋น„๊ต ๋ฐ ๋น„์Šทํ•œ ๊ธฐ๋Šฅ ์ง€์›ํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์˜ ์‚ฌ์ด์ฆˆ ๋น„๊ต

 

03. ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ค‘๋ณต ํ”ผํ•˜๊ธฐ

๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ฒ„์ „ ๋งž์ถ”๊ฑฐ๋‚˜ ๋™์ผ ๊ธฐ๋Šฅํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ๋ณ€ํ™˜ํ•ด ์ค‘๋ณต๋œ ๊ตฌํ˜„ ํ”ผํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ๋ฒ„์ „์ด ๋‹ฌ๋ผ ์ค‘๋ณต๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ํŒจํ‚ค์ง€๋งค๋‹ˆ์ € npm ์ด๋‚˜ yarn๋ฅผ ํ™œ์šฉํ•ด ๊ฐœ์„  ๊ฐ€๋Šฅ
    • npm์€ ํŠธ๋ฆฌ ๊ตฌ์กฐ๋กœ ํ•„์š”ํ•œ ๋ชจ๋“  ์˜์กด์„ฑ์„ ๊ด€๋ฆฌํ•˜๋Š” ํ˜•์‹์ด์ง€๋งŒ ์ค‘๋ณต๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์Œ“์ด๊ฒŒ ๋˜๋ฉด ๋ฒˆ๋“ค์‚ฌ์ด์ฆˆ๋Š” ๋ฌผ๋ก  node_modules๋„ ๊ณผ๋„ํ•œ ์šฉ๋Ÿ‰์„ ์ฐจ์ง€ํ•˜๊ฒŒ ๋œ๋‹ค.
    • yarn์˜ ๊ฒฝ์šฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜ ๊ณผ์ •์—์„œ ์•Œ๋งž๊ฒŒ ์ฒ˜๋ฆฌํ•ด์ฃผ๋Š”๋ฐ ๋ณด๋‹ค ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ์„ ์œ„ํ•ด yarn dedupelicate ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ด์šฉํ•ด ๋‹ค์–‘ํ•œ ์ „๋žต์„ ํ†ตํ•ด ์ค‘๋ณต๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ œ๊ฑฐํ•ด์ค€๋‹ค.

 

04. ๋ฌด๊ฑฐ์šด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์˜ํ–ฅ ์ตœ์†Œํ™”

Single Common Chunk๋Š” ์ƒˆ๋กœ์šด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์ถ”๊ฐ€๋˜๋ฉด ๋ชจ๋“  ํŽ˜์ด์ง€ ์šฉ๋Ÿ‰์ด ํ•œ๋ฒˆ์— ์ฆ๊ฐ€ํ•˜๋Š” ๋ฌธ์ œ์ ์ด ์กด์žฌํ•œ๋‹ค.

์ด ๋ฌธ์ œ์ ์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ํŽ˜์ด์ง€๋ณ„ ์ฒญํฌ ํŒŒ์ผ์˜ ์˜ํ–ฅ ์ตœ์†Œํ™”ํ•˜์—ฌ ์‚ฌ์šฉ์ž ์ด๋™์— ๋”ฐ๋ผ ํ•„์š”ํ•œ ์ฒญํฌ๋งŒ ๋ฐ›์•„์˜ฌ ์ˆ˜ ์žˆ๋„๋ก ๋ถ„๋ฆฌํ•œ๋‹ค.

  • ๊ฐœ์„ ๋œ ์ฒญํฌ ํŒŒ์ผ ์ข…๋ฅ˜ 
    • Framework Chunk (ํ”„๋ ˆ์ž„์›Œํฌ) : react, react-dom, next ๋“ฑ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋“ฑ
    • Commons Chunk (๊ณตํ†ต) : ๋ชจ๋“  ํŽ˜์ด์ง€์—์„œ ์‚ฌ์šฉํ•˜๋Š” ์ฝ”๋“œ ๋ชจ์•„๋‘” ์ฒญํฌ
    • Shared Chunk : 2ํŽ˜์ด์ง€ ์ด์ƒ ๊ณต์œ ํ•˜๋Š” ์ฝ”๋“œ ๋ชจ์•„๋‘” ์ฒญํฌ
    • Page๋ณ„ Chunk : ํŠน์ • ํŽ˜์ด์ง€์—์„œ๋งŒ ์‚ฌ์šฉํ•˜๋Š” ์ฒญํฌ

    โžจ ํ”„๋ ˆ์ž„์›Œํฌ์™€ ๊ณตํ†ต ์ฒญํฌ ํŒŒ์ผ์€ ๋ชจ๋“  ํŽ˜์ด์ง€์—์„œ ๋ฐ›์•„์ง€๋ฉฐ ๋จผ์ € ์ง„์ž…ํ•œ ํŽ˜์ด์ง€์—์„œ ์บ์‹ฑ๋˜์–ด ํšจ์œจ์ ์œผ๋กœ ๋ฆฌ์†Œ์Šค ๊ด€๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.

 

 

05. ์ฝ”๋“œ ์Šคํ”Œ๋ฆฌํŒ…

๋Œ€๋ถ€๋ถ„ ์›นํŽ˜์ด์ง€๋Š” ๊ฐ๊ฐ์˜ ํŽ˜์ด์ง€๋ณ„๋กœ ๋‚˜๋ˆ ์ ธ์žˆ์–ด ๋‹ค๋ฅธ ํŽ˜์ด์ง€์˜ ์ฝ”๋“œ๊ฐ€ ์กด์žฌํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค. ํ•„์š”ํ•œ ๊ฒƒ๋งŒ ๋‚จ๊ธฐ๋˜ ๋ชจ๋“  ํŒŒ์ผ์„ ํ•˜๋‚˜๋กœ ํ•ฉ์น˜์ง€ ์•Š๊ณ  ๋”ฐ๋กœ ๋ถ„๋ฆฌํ•˜๋Š” ๊ฒƒ, ์ด๋ฅผ ํ†ตํ•ด ์‚ฌ์šฉ์ž๊ฐ€ ์ ‘์†ํ•œ ํŽ˜์ด์ง€์˜ ์ฝ”๋“œ๋งŒ ๋กœ๋“œํ•˜์—ฌ ๋กœ๋”ฉ์‹œ๊ฐ„๊ณผ ํŠธ๋ž˜ํ”ฝ ์ค„์–ด๋“ค๊ฒŒ ํ•ด์ฃผ๋Š” ํšจ๊ณผ๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋Š” ์‹œ์ ์— useState ๊ฐ’์ด true๊ฐ€ ๋˜๊ณ , <SplitComponent>๊ฐ€ ๋ Œ๋”๋ง ๋˜๋ฏ€๋กœ ๋กœ๋”ฉ์„ ์‹œ์ž‘ํ•˜๋Š” ๊ตฌ์กฐ


  1. import( ) ์‚ฌ์šฉ
    • ํ•„์š”ํ•œ ์‹œ์ ์— ์ปดํฌ๋„ŒํŠธ ๋ถˆ๋Ÿฌ์˜ค๋„๋ก import๋ฅผ ํ•จ์ˆ˜๋กœ ์‚ฌ์šฉํ•˜๋ฉด, Promise ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
    • ์ด ํ•จ์ˆ˜๋Š” ๋ชจ๋“ˆ์„ ๋น„๋™๊ธฐ์ ์œผ๋กœ CommonJS ํ˜•ํƒœ๋กœ ๋ถˆ๋Ÿฌ์˜ค๋‹ˆ, ๋”ฐ๋กœ default ๋ฅผ ๋ช…์‹œํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค
    • ๊ฐœ๋ฐœ์ž๋„๊ตฌ์—์„œ Network ํƒญ์—์„œ ํ™•์ธํ•˜๋ฉด ๋ถ„๋ฆฌ๋œ ์ฒญํฌ ํŒŒ์ผ(chunk.js)์— splitting ์ฝ”๋“œ๊ฐ€ ๋ถˆ๋Ÿฌ์˜ค๊ฒŒ ๋œ๋‹ค
    • import ๋ฅผ ํ•จ์ˆ˜๋กœ ์‚ฌ์šฉํ•ด์ฃผ๋ฉด, ์›นํŒฉ์ด ์•Œ์•„์„œ ์ฝ”๋“œ๋ฅผ ๋ถ„๋ฆฌ๋ฅผ ํ•˜์—ฌ ๋”ฐ๋กœ ์ €์žฅํ•˜๊ณ , import ๊ฐ€ ํ˜ธ์ถœ ๋  ๋•Œ ๋ถˆ๋Ÿฌ์™€์„œ ์‚ฌ์šฉ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค€๋‹ค
    - ํ•จ์ˆ˜ & ์ปดํฌ๋„ŒํŠธ ์Šคํ”Œ๋ฆฌํŒ…
    import { useState } from "react";
    
    export const App = () => {
        const [component, setComponent] = useState(null);
        const onClick = () => {
            import('์ปดํฌ๋„ŒํŠธ๊ฒฝ๋กœ').then(result => setComponent(result.default)); // 1. ์ปดํฌ๋„ŒํŠธ ๋ฐฉ์‹ 1
            import('./์ปดํฌ๋„ŒํŠธ๊ฒฝ๋กœ').then(({ default: SplitMe }) => {setSplitMe({SplitMe})}); 1. ์ปดํฌ๋„ŒํŠธ ๋ฐฉ์‹ 2
            import('./notify').then(({ default: notify }) => {notify()}); // 2. ํ•จ์ˆ˜
        }
    
        return (
            <div>
                <button type="button">click me</button>
                {component && <component />}
            </div>
        );
    };
     
    - HoC ์ž‘์„ฑ์„ ํ†ตํ•ด splitting ์ปดํฌ๋„ŒํŠธ ์žฌํ™œ์šฉ
    • ์ฝ”๋“œ ์Šคํ”Œ๋ฆฌํŒ… ํ•  ๋•Œ ๋ฐ˜๋ณต๋˜๋Š” ๋กœ์ง์„ ํ•จ์ˆ˜ํ™”
    • ๋” ๊ฐ„ํŽธํ•œ ์ฝ”๋“œ ์Šคํ”Œ๋ฆฌํŒ… ๋ฐ ๋กœ์ง ์ตœ์ ํ™”๋ฅผ ๋•๋Š”๋‹ค
    import React, { Component } from 'react';
    
    const withSplitting = getComponent  => {
    	const [Splitted, setSplitted] = (null);
    	const preload = () => {
          // Splitted ๊ธฐ๋ณธ๊ฐ’์€ null์ด์ง€๋งŒ preload ํ˜ธ์ถœ๋˜๋ฉด Splitted ๊ฐ€ ์„ค์ •๋˜๊ณ 
          getComponent().then(({ default: Splitted }) => {
            setSplitted(Splitted);
          });
      }
    	// ์—ฌ๊ธฐ์„œ getComponent๋Š” () => import('./SplitMe')์˜ ํ˜•ํƒœ๋กœ ํ•จ์ˆ˜๊ฐ€ ์ „๋‹ฌ๋˜์•ผ ํ•œ๋‹ค.
    	const handle = (getComponent) => {
    		getComponent().then(({ default: Splitted }) => {
            setSplitted(Splitted);
          });
    	}
    
    	// ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ƒ์„ฑ๋˜๋Š” ์‹œ์ ์—์„œ Splitted ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฏ€๋กœ null ์ด๋‚˜ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ๋จ
       if (!Splitted) return null;
       return <Splitted />;
    };
    
    export default withSplitting;

    import React, { Component } from 'react';
    import withSplitting from './withSplitting'; // HoC import
    
    const SplitMe = withSplitting(() => import('./SplitMe')); // ์Šคํ”Œ๋ฆฌํŒ… ์ฝ”๋“œ ์ƒ๋‹จ์— ์ž‘์„ฑํ•ด์ฃผ๋ฉด ๋!
    
    export const App = () => {
      const [visible, setVisible] = (false);
      const handleClick = () => setVisible(true);
    
      return (
          <div>
            <button onClick={handleClick}>Click Me</button>
            {visible && <SplitMe />}
          </div>
      );
    }

    - ๋ผ์šฐํ„ฐ ์ž‘์„ฑ
    import withSplitting from '../withSplitting';
    
    export const Home = withSplitting(() => import('./Home'));
    export const About = withSplitting(() => import('./About'));

    import React, { Component } from 'react';
    import { Route, Link } from 'react-router-dom';
    import { About, Home } from './pages';
    
    export const App = () => {
        return (
          <div>
          	<Link to="/">Home</Link>
            <Link to="/about" onMouseOver={About.preload()}>
            <hr />
            <Route exact path="/" component={Home} />
            <Route path="/about" component={About} />
          </div>
        );
    }


  2. Dymanic Import ์‚ฌ์šฉ
    webpack ์—์„œ ์ง€์›ํ•ด์ฃผ๊ณ  ์žˆ๋Š” ํ•จ์ˆ˜๋กœ ๋ณ„๋„์˜ ์„ค์ • ์—†์ด ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค.
    ๋™์  ์ฝ”๋“œ ๋ถ„ํ• , lazy-load = ๊ฒŒ์œผ๋ฅธ ๋กœ๋”ฉ
    ๋Ÿฐํƒ€์ž„์‹œ์— ํ•„์š”ํ•œ module ์„ import ๊ฐ€๋Šฅ

    ํŽ˜์ด์ง€ ์ง„์ž…์‹œ์— ํ•„์š”ํ•œ ์ตœ์†Œํ•œ์˜ ์ฝ”๋“œ๋งŒ ๋‹ค์šด๋ฐ›์•„ ์ฒซ ํŽ˜์ด์ง€์˜ ์ดˆ๊ธฐ ์„ฑ๋Šฅ์˜ฌ๋ฆด ์ˆ˜ ์žˆ๋‹ค

    ์‚ฌ์šฉ์ž๊ฐ€ ํด๋ฆญํ•  ๋•Œ๋งˆ๋‹ค Import ํ•จ์œผ๋กœ์„œ, ๋ถ„ํ• ํ•˜์—ฌ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.

    Webpack ์—์„œ Dynamic Import ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” babel-plugin-syntax-dynamic-import ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ํ•„์š”
    // babel.config.json
    {
      "plugins": ["@babel/plugin-syntax-dynamic-import"]
    }โ€‹

    chunk ํŒŒ์ผ ๋ช…์€ webpack ์˜ output ์— ์„ธํŒ… ํ•œ ํŒŒ์ผ๋ช…์„ ๋”ฐ๋ผ๊ฐ„๋‹ค
    module.exports = { 
    	output: { 
        	filename: '[name].js', 
            chunkFilename: '[name].chunk.js', 
    	}, 
    }

    import dynamic form 'next/dynamic';
    
    const PageA = dynamic(() => import('./PageA'));
    const PageB = dynamic(() => import('./PageB')); // webpackPrefetch : trueโ€‹
    • ์›นํŒฉ magic comment ์ด์šฉ์‹œ prefetch ๊ธฐ๋Šฅ ์ด์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ์—ฌ์œ ๋กœ์šด ์‹œ๊ฐ„์— ๋ฏธ๋ฆฌ ๋ฐ›์•„๋‘์–ด ์‚ฌ์šฉ์ž์—๊ฒŒ ์ตœ์†Œํ•œ์˜ ์ง€์—ฐ ์‹œ๊ฐ„ ์ค„ ์ˆ˜ ์žˆ๋‹ค.
    • ์ ์ ˆํ•œ DI์™€ Prefetch ์ด์šฉํ•ด ์ดˆ๊ธฐ ๋กœ๋”ฉ ์†๋„๋ฅผ ์ ˆ๋ฐ˜ ๊ฐ€๊นŒ์ด ์ค„์ผ ์ˆ˜ ์žˆ์—ˆ๋‹ค.


  3. React.lazy() +  <Suspense> ์‚ฌ์šฉ
    • React.lazy() : ์ปดํฌ๋„ŒํŠธ ๋žœ๋”๋ง์‹œ์ ์— ๋น„๋™๊ธฐ์ ์œผ๋กœ ๋กœ๋”ฉํ•˜๊ฒŒ ํ•ด์ฃผ๋Š” ํ•จ์ˆ˜
    • <Suspense> : ์ฝ”๋“œ ์Šคํ”Œ๋ฆฌํŒ…๋œ ์ปดํฌ๋„ŒํŠธ ๋กœ๋”ฉ & ๋กœ๋”ฉ์ „ ๋ณด์—ฌ์ค„ ์ปดํฌ๋„ŒํŠธ ์ง€์ •๊ฐ€๋Šฅ
    import React, { useState, Suspense, lazy } from "react";
    
    const SplitComponent = lazy(() => import('์ปดํฌ๋„ŒํŠธ๊ฒฝ๋กœ'));
    
    export const App = () => {
        const [visible, setVisible] = useState(false);
        const onClick = () => {setVisible(prev => !prev)};
        
        return(
        	<div>
               <button type='button' onClick={onClick}></button>
               <Suspense fallback={<div>...loading</div>}>
                 {visible && <SplitComponent />}
               <Suspense/>
            </div>
        );
    }


  4. loadable ์‚ฌ์šฉ
    npm i @loadable/component
    • ์‚ฌ์šฉ๋ฒ• React.lazy()์™€ ๋น„์Šท & <Suspence> ์‚ฌ์šฉ์—†์ด ๋กœ๋”ฉ ํ™”๋ฉด ๋ฐ ํŽธ๋ฆฌํ•œ preload ๊ธฐ๋Šฅ ์ง€์›
    • ์‚ฌ์šฉ์ž๊ฐ€ ๋ฒ„ํŠผ์— ๋งˆ์šฐ์Šค๋ฅผ ์˜ฌ๋ฆฌ๋ฉด ์ปดํฌ๋„ŒํŠธ ๋กœ๋“œ
    import { useState } from "react";
    import loadable from "@loadable/component";
    
    export const App = () => {
        const SplitComponent = loadable(() => import('์ปดํฌ๋„ŒํŠธ ๊ฒฝ๋กœ'), {fallback: <div> loading </div>})
        const [visible, setVisible] = useState(false);
        const onClick = () => {setVisible(prev => !prev)};
        const onMouseOver = () => {SplitComponent.preload()};
        
        return(
        	<div>
               <button type='button' onClick={onClick} onMouseOver={onMouseOver}>Click!</button>
               <Suspense fallback={<div>...loading</div>}>
                 {visible && <SplitComponent />}
               <Suspense/>
            </div>
        );
    }

 

5. react-loadable (๋„์ž…์ „ ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์ง„์งœ ํ•„์š”ํ•œ๊ฐ€!! ๊ณ ๋ฏผํ•˜๊ธฐ)

import React from 'react';
import Loadable from 'react-loadable';

const Loading = () => {
  return <div>๋กœ๋”ฉ์ค‘...</div>;
};

// ๋กœ๋”ฉ์ค‘์ผ๋•Œ ๋ Œ๋”๋ง ํ•  ์ปดํฌ๋„ŒํŠธ๋”ฐ๋กœ ์ง€์ • ๊ฐ€๋Šฅ
export const Home = Loadable({
  loader: () => import('./Home'),
  loading: Loading
});

export const About = Loadable({
  loader: () => import('./About'),
  loading: Loading
});

 

import React, { Component } from 'react';
import { Route, Link } from 'react-router-dom';
import { About, Home } from './pages';

export const App = () => {
    const handleMouseOver = () => {About.preload()};
    return (
        <div>
            <Link to="/">Home</Link>
            <Link to="/about" onMouseOver={handleMouseOver}>About</Link>
            <hr />
            <Route exact path="/" component={Home} />
            <Route path="/about" component={About} />
        </div>
    );
}

 

6. SSR (Next.js ๊ฐ™์€ ํ”„๋ ˆ์ž„์›Œํฌ ํ™œ์šฉ)

ssr ์‹œ์ ์—์„œ ํŽ˜์ด์ง€ ๋‹จ์œ„๋กœ ์‚ฌ์šฉ์ž์— ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”ํ›„์— ๋ฐ›์•„์˜ค๋ฉด์„œ ๋ฒˆ๋“ค ์‚ฌ์ด์ฆˆ๋ฅผ ์ค„์ผ ์ˆ˜๋„ ์žˆ๋‹ค.

react-query์˜ useQuery์™€ ์œ ์‚ฌํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ฐ–๋Š” `useResource`๋ฅผ ๋„์ž…ํ•ด ๊ธฐ์กด ์ฝ”๋“œ๋ฅผ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ํ•ด๋ณด๋Š”๊ฒƒ๋„ ์ถ”์ฒœํ•œ๋‹ค!

์ˆ˜์ฒœ์ค„์ด ๋„˜๋Š” ์„œ๋น„์Šค๋ฅผ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•ด์•ผ๋  ๊ฒฝ์šฐ ๋น„์šฉ์„ ์ค„์ด๋Š” ๊ฒƒ์€ ์•„์ฃผ ์ค‘์š”ํ•œ ์ผ์ด๋ฏ€๋กœ, ํ•ญ์ƒ ์ตœ์†Œํ•œ์˜ ๋ฆฌํŒฉํ† ๋ง ๋ฆฌ์†Œ์Šค ๋Œ€ํ•ด ๊ณ ๋ฏผํ•˜๋Š” ๊ฒƒ๋„ ๊ฐœ๋ฐœ์ž๋กœ์„œ ์ค‘์š”ํ•œ ์ž์งˆ์ด๋ผ๊ณ  ๋Š๊ปด์ง€๋Š” ํ•œ ์ฃผ์˜€๋‹ค.

 


 

์ฝ”๋“œ ์ตœ์ ํ™”์— ๋Œ€ํ•œ ๋‚˜์˜ ์ƒ๊ฐ

ํ”„๋ก ํŠธ ํŠธ๋ Œ๋“œ๋Š” ์ •๋ง ๋งŽ์ด ๋ฐ”๋€Œ๊ณ  ์ด์™ธ์—๋„ ๋ฒˆ๋“ค ์‚ฌ์ด์ฆˆ๋ฅผ ์ค„์ผ ์ˆ˜ ์žˆ๋Š” ๋‹ค์–‘ํ•œ ๋ฐฉ์•ˆ์ด ์กด์žฌํ•œ๋‹ค. ๋ณธ์งˆ์€ ๋ฒˆ๋“ค ์‚ฌ์ด์ฆˆ๋ฅผ ์ค„์—ฌ - ์œ ์ €์—๊ฒŒ ํ™”๋ฉด์ด ๊ทธ๋ ค์ง€๋Š” ์†๋„, ์›น์‚ฌ์ดํŠธ์˜ ์‹ค์งˆ์ ์ธ ํผํฌ๋จผ์Šค ํ–ฅ์ƒ์„ ์œ„ํ•œ ์ž‘์—…์ด๋‹ค. ์ด์ „๋ถ€ํ„ฐ ์–ด๋–ค ๋ฐฉ๋ฒ•๋“ค์ด ์“ฐ์—ฌ์ ธ ์™”๋Š”์ง€ ๊ทธ๊ธธ์„ ๊ณต๋ถ€ํ•ด๋ณด๋ฉด ๋‚ด ๊ธฐ์ค€์—์„œ์˜ ์ข‹์€ ๋ฐฉ์•ˆ๋ฅผ ์ฐพ๊ฒŒ ๋˜๋Š” ๊ฒƒ ๊ฐ™๋‹ค. ๐Ÿ™†‍โ™€๏ธ ์ฝ”๋“œ๋ฅผ ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ๋Š” ๋‹ค์–‘ํ•œ ๋ฐฉ๋ฒ•์„ ์•ž์œผ๋กœ๋„ ์•Œ์•„๊ฐ€๊ณ , ์œ ์ €๊ฐ€ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๊ทธ๋ฆฌ๊ณ  ๊ฐœ๋ฐœ์ž์˜ ์‹œ๊ฐ„์„ ์•„๊ปด์ฃผ๋Š” ๋™๋ฃŒ๊ฐ€ ๋˜๋ฉด ์ข‹๊ฒ ๋‹ค :)

 

 


๋„์›€์„ ์ค€ ํฌ์ŠคํŠธ

  • ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํŒจํ‚ค์ง€ ์ตœ์ ํ™”
 

์‰ฝ๊ฒŒ ๋”ฐ๋ผํ•˜๋Š” ํ”„๋ก ํŠธ์—”๋“œ ์›น ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ํŒจํ‚ค์ง€ ์ตœ์ ํ™”

๋ณธ ํฌ์ŠคํŠธ๋Š” Next.js ๊ธฐ๋ฐ˜ ํ”„๋กœ์ ํŠธ์˜ ํŠธ๋ฆฌ์…ฐ์ดํ‚น ์ง„ํ–‰ ๊ณผ์ •์„ ๋”ฐ๋ผํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค๋ช…๊ณผ ํ•จ๊ป˜ ์ •๋ฆฌํ•œ ๊ธ€์ž…๋‹ˆ๋‹ค. ๋น„๋ก ๊ฐ€๋Šฅํ•œ ๋ชจ๋“  ์ตœ์ ํ™”๋ฅผ ์ง„ํ–‰ํ•˜์ง€๋Š” ๋ชปํ–ˆ์ง€๋งŒ, ๋ฐฉ๋ฒ•์˜ ๋ฌธ์ œ๋Š” ์•„๋‹ˆ์—ˆ์œผ๋ฉฐ ๊ฒฐ๊ณผ

velog.io

  • React.lazy() & <Suspense>
 

react.lazy()์™€ Suspense

์ฝ”๋“œ์Šคํ”Œ๋ฆฟ์ด ๋ญ”๊ฐ€? ๊ทธ๋ƒฅ ์ง€๋‚˜์ณค๋˜ ๋‚ด์šฉ์ธ๋ฐ ํ•œ๋ฒˆ ์•Œ์•„๋ณด์ž์ผ๋ฐ˜์ ์œผ๋กœ lazy๋ฅผ ์‚ฌ์šฉํ•˜์ง€์•Š๊ณ  home/ktw๋กœ ๋ผ์šฐํŒ…์„ ํ•˜๊ฒŒ๋˜๋ฉด ์•„๋ž˜ ๊ทธ๋ฆผ๊ณผ๊ฐ™์ด ๋ชจ๋“  ํŒŒ์ผ์„ ๋ฆฌ๋กœ๋”ฉํ•œ๋‹ค.ํ•˜์ง€๋งŒ, React.lazy()๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด

velog.io

 

 

 

Posted by Ang