ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 2022-08-01 : webpack & React
    TIL ( Today I Learned ) 2022. 8. 1. 13:48

    1. Webpack

    Bundler : 웹사이트를 구성할 때 .js .css .images 파일 등 수많은 파일들이 모여서 웹사이트를 구성하게 되는데 이런 파일들이 모여서 웹사이트가 느리게 로딩된다. 또한 많은 자바스크립트 등을 사용하다보면 각각의 패키지들이 같은 이름이나 함수를 사용하게 되면서 애플리케이션이 깨지는데, 이런 현상을 해결하기 위해 나온 개념이다.

     

    - Webpack = 모듈 번들링

    - 웹 어플리케이션을 구성하는 자원 ( HTML, CSS, Javascript, Images 등) 을 각각의 모듈로 보고 이를 조합해서 하나의 결과물을 만드는 과정

    - 웹에서 사용하는 모든 자원(assets)를 번들링 해주는 도구

     

    등장 배경

    1. 파일 단위의 자바스크립트 모듈 관리의 필요성

    2. 웹 개발 작업 자동화 도구

    3. 웹 애플리케이션의 빠른 로딩 속도와 높은 성능

     

    사용이유

    - 예전에는 페이지마다 새로운 html을 요청해서 뿌려주는 방식이라면, 요새는 SPA( 하나의 html페이지에 여러개의 자바스크립트 파일들이 포함된다. ) 연관되어 있는 자바스크립트 종속성 있는 파일들을 하나의 파일로 묶어줘서 관리하기 편함

    - 파일을 컴파일 할 때, 여러 모듈들의 파일을 읽어오는데 시간이 오래 걸린다. 그 부분을 해결하기 위해 여러 파일을 하나의 파일로 번들링 해준다.

    - 하나의 자바스크립트 파일로 만들어서 웹페이지 성능 최적화

    - 중복된 소스코드를 최소화하고 모듈 개념을 사용해서 글로벌이 오염되지 않는다.

     

    파일 단위의 자바 스크립트 모듈 관리의 필요성

    - 자바스크립트의 변수 유효 범위는 기본적으로 전역 범위를 갖는다. 이 때문에 서로 다른 모듈에서 같은 이름의 변수를 사용한다면, 서로가 서로를 의도치 않게 변경할 수도 있다.

    - 이전에는 AMD, CommonJS와 같은 라이브러리를 사용

     

    AMD VS CommonJS VS Webpack

    - AMD 방식은 Requirejs, CommonJS 방식은 Browserify 가 인기가 많았다.

    - Requirejs는 콜백함수를 통해 모듈들을 전달받는 구조였기 때문에 모듈이 많아질수록 관리하기 힘들고

    - Browerify는 node.js로 코드를 작성해야 했기 때문에 모듈이 많아지면 node.js를 다루는 상황이 많아져 모듈 관리를 하나로 유지하고 싶었습니다

    - 반면, 웹팩은 AMD와 CommonJS를 동시에 지원하고, 기본적으로 부분을 캐싱하여 변경점만 번들링하는 방식이기 때문에 속도가 빠르고 테스터 런너와 연동이 좋다

     

    웹 개발 작업 자동화

    - 배포 시 HTML, CSS, JS 압축 / 이미지 압축 / CSS 전처리기 변환

    - Grunt와 Gulp 같은 도구들이 등장

     

    Grunt VS Gulp VS Webpack

    - Grunt와 Gulp는 Task Runner이고, 웹팩은 Package Bundler이다

        ㆍTask Runner : 반복 가능한 특정 작업을 자동화

        ㆍPackage Bundler : 종속성을 가진 애플리케이션 모듈을 정적인 소스로 재생산

    - Gulp는 종속성 관리를 할 수 없지만 웹팩은 종속성 관리가 가능하고 이는 규모가 큰 프로젝트에 좋음

     

    2. Babel

    : Ecmascript6를 Ecmascript5로 변환해준다

     

    사용이유

    - 인터넷 익스플로러처럼 ES6를 지원하지 않는 브라우저는 ES5로 변경 ( RIP )

    - 그래서 개발 환경 설정할 때, webpack이랑 babel로 기초 환경 설정을 잡고 개발해야한다.

     

    3. 웹팩 핵심 요소

    1. Entry

    2. Output

    3. Loader

    4. Plugin

     

    Entry

    : 엔트리 속성은 웹 패에서 웹 자원을 변환하기 위해 필요한 최초 진입점, 즉 Entry로 묶고자하는 파일의 첫번째 진입점을 설정

    //webpack.config.js
    //SPA(Single Page Application)
    module.exports = {
      entry:'./src/index.js'
    }

    - 이때 최초 진입점이 되는 파일 /index.js 파일은 웹애플리케이션의 전반적인 구조와 내용이 담겨있어야한다.

    - 그래야 웹패이 해당 파일을 토대로 애플리케이션의 모듈들의 연관 관계에 대해 이해하고 분석하고 합침

     

    Output

    : 웹팩을 실행하여 빌드하고 난 후 결과물의 파일 경로를 의미, filename 속성은 웹팩으로 빌드한 파일의 이름을 의미하며 여러가지 옵션을 넣을 수 있고, path 속성은 해당 파일의 경로를 의미한다.

     

    - 여기서 path 속성에서 사용된 메서드는 인자로 받은 경로를 조합하여 유효한 파일 경로를 만드는 Node.jsAPI라고 한다

    var path = require('path');
    
    module.exports = {
      output: {
        filename: "bundle.js",
        path: path.resolve(__dirname, './dist')
      }
    }
    /* Node.js API가 하는 역할은 아래 코드와 동일하다. */
    output: './dist/bundle.js'

     

    Loader

    : 웹팩이 애플리케이션을 해석하는 자바스크립트 파일이 아닌 HTML, CSS, Images, Font 등을 변환할 수 있게 도와주는 속성이다.

    - 웹팩은 모든 파일을 모듈로 취급하여 관리하는데 사실상 자바스크립트 파일만 알고 있어 로더를 이용해 다른 파일들이 웹팩이 이해하게끔 변경해줘야 한다.

    //app.js
    import './common.css';
    
    console.log('css loaded');
    /* common.css */
    p {
    	color:blue;
    }
    //webpack.config.js
    module.exports = {
     entry:'./app.js',
     output : {
       filename: 'bundle.js'
    }
    }

    - 위 파일을 빌드 시 오류 발생

    Module parse failed: unexpected token
    You may need an appropriate loader to andle this type, currently no loaders are 
    configured to process this file

    - 사용되는 로더 종류

        ㆍCSS Loader

        ㆍBabel Loader

        ㆍSass Loader

        ㆍFile Loader

        ㆍVue Loader

        ㆍTS Loader

        ㆍStyle Loader

    module.exports = {
      modules: {
        rules : [
          {test:/\.css$/, use:'css-loader'},
          { test:/\.ts$/, use:'ts-loader'},
    	]
      }
    }

    - 위와 같이 rules라는 객체로 속성을 지정

    - test : 로더를 적용할 파일 유형 ( 일반적으로 정규 표현식 )

    - use : 해당 파일에 적용할 로더 이름

     

    Plugin

    : 웹팩의 기본적인 동작에 추가적인 기능을 제공하는 속성

    - Loader랑 역할을 비교해보자면 로더는 파일을 해석하고 변환하는 과정에 관여하며, Plugin은 해당 결과물의 형태를 바꾸는 역할

        ㆍHtmlWebpackPlugin : 웹팩으로 빌드한 결과물로 Html 파일을 생성해주는 플러그인

        ㆍProgressPlugin : 웹팩의 빌드 진행율을 표시해주는 플러그인

    var webpack = require('webpack');
    var HtmlWebpackPlugin = require('html-webpack-plugin');
    
    module.exports = {
      plugins: [
        new HtmlWebpackPlugin(),
        new webpack.ProgressPlugin()
        ]
    }

     

    정리 - 웹팩으로 해결하려는 문제

    1. 자바스크립트 변수 유효 범위 문제

        ㆍES6의 모듈 문법과 번들링으로 해결

    2. 브라우저별 HTTP 요청 숫자의 제약

        ㆍTCP 스펙에 따라 브라우저에서 한 번에 서버로 보낼 수 있는 HTTP 요청 숫자가 제약되어 있다

        ㆍHttp/2 에서는 하나의 커넥션에 동시에 여러 파일들을 요청할 수 있지만,

            Http/1.1에서는 하나의 커넥션에서 하나의 요청을 보내야 한다. 

            그러므로 js파일이 10개인 경우 10번의 요청을 보내야하기 때문에 성능저하 문제

    3. 사용하지 않는 코드의 관리

    4. Dynamic Loading 및 Lazy Loading 미지원 문제

    - 이전에는 Require.js 같은 라이브러리를 사용하지 않는 이상 동적으로 원하는 순간에 모듈을 로딩하는 것이 불가능했다. 웹펙에서는 Code Splitting 기능을 이용하여 원하는 모듈을 원하는 타이밍에 로딩

     

    참고 자료

    1. https://velog.io/@kallroo/FEWebPack

    2. https://velog.io/@kim-jaemin420/%EC%9B%B9%ED%8C%A9%EC%9B%B9%ED%8C%A9%EC%9D%B4%EB%9E%80-%EC%9B%B9%ED%8C%A9%EC%9D%B4-%ED%95%98%EB%8A%94-%EC%9D%BC%EA%B3%BC-%ED%95%84%EC%9A%94%ED%95%9C-%EC%9D%B4%EC%9C%A0

     

     

    4. moment

    - Moment.js 는 시간이 포함된 데이터를 조작해야 할 경우 가장 많이 사용되는 라이브러리

    import moment from 'moment';
    // 안써도 자동으로 한국 시간을 불러온다. 명확하게 하기 위해 import
    import 'moment/locale/ko';
    
    // format에 맞게 출력된다.
    const nowTime = moment().format('YYYY-MM-DD HH:mm:ss');
    console.log(nowTime);
    // 출력 결과: 2020-08-23 12:54:30

     

    참고 자료 : https://haranglog.tistory.com/10

     

    5. setTimeOut과 setInterval을 이용한 호출 스케줄링

    : 일정 시간이 지난 후에 원하는 함수를 예약 실행(호출)할 수 있게 하는 것을 '호출 스케줄링(scheduling a call)'이라 한다.

     

    - 호출 스케줄링 구현하는 방법에는 두가지가 있다

        ㆍsetTimeOut을 이용해 일정 시간이 지난 후에 함수를 실행하는 방법

        ㆍsetInterval을 이용해 일정 시간 간격을 두고 함수를 실행하는 방법

     

    setTimeout

    let timerId = setTimeout(func|code, [delay], [arg1], [arg2], ...)

    - delay : 실행 전 대기시간, 단위는 밀리초(millisecond, 1000밀리초 = 1초)이며 기본값은 0이다

    - arg1, arg2 ... : 함수로 전달할 인수들로, IE9 이하에선 지원하지 않는다.

     

    clearTimeout으로 스케줄링 취소하기

    - setTimeout을 호출하면 '타이머 식별자(timer identifier)'가 반환된다. 스케줄링을 취소하고 싶다면 clearTimeout 사용

    let timerId = setTimeout(...);
    clearTimeout(timerId);

     

    setInterval

    let timerId = setTimeout(func|code, [delay], [arg1], [arg2], ...)

    - 문법, 인수 동일

    - setTimeout이 함수를 단 한 번만 실행하는 것과 달리 setInterval은 함수를 주기적으로 실행하게 만든다.

    - 함수 호출을 중단하려면 clearInterval(timerId)을 사용

    // 2초 간격으로 메시지를 보여줌
    let timerId = setInterval(() => alert('째깍'), 2000);
    
    // 5초 후에 정지
    setTimeout(() => { clearInterval(timerId); alert('정지'); }, 5000);

     

    참고자료 : https://ko.javascript.info/settimeout-setinterval

     

    6. useEffect return ( 언마운트 )

    - useEffect는 컴포넌트가 마운트 됐을 때 ( 처음 나타났을 때 ), 언마운트 됐을 때( 사라질 때 ), 그리고 업데이트될 때 ( 특정 props가 바뀔 때 ) 특정 작업을 처리한다.

    useEffect(() => {
      // mount
      console.log('컴포넌트가 화면에 나타남');
      
      return() => {
        // unmount
        console.log('컴포넌트가 화면에서 사라짐');
      }
    }, [])

    - mount

        ㆍ받아온 props를 객체의 state로 설정해줄때

        ㆍREST API로 작업을 처리할 때

        ㆍd3 video같은 라이브러리를 사용할 때

        ㆍsetInterval, setTimeout 을 사용할 때

    - Unmount

        ㆍ뒷정리 함수라고 생각하면 된다

        ㆍclearInterval, clearTimeout

        ㆍ라이브러리 인스턴스 제거

     

    - useEffect 함수를 이용해 컴포넌트가 생기고, 사라질 때를 제어할 수 있다

    useEffect(() => {
      (컴포넌트가 생길 때 수행 작업);
      return {
          (컴포넌트가 사라질 때 수행 작업);
      }
    }, [의존성])

     

    참고 자료 : https://velog.io/@uoayop/useEffect-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EB%A7%88%EC%9A%B4%ED%8A%B8-%EC%96%B8%EB%A7%88%EC%9A%B4%ED%8A%B8-%EC%A0%9C%EC%96%B4

     

    7. useEffect의 4가지 팁

    1. 단일 목적의 useEffect

    - 클린 코드의 관점에서는 함수는 한 가지 목적을 가지고 있어야 한다.

    - useEffect를 작고 단순한 단일 목적의 함수로 분리한다면 의도치 않은 이펙트 함수의 실행을 방지

     

    2. 가능하다면 커스텀 훅을 사용한다.

    - 커스텀 훅으로 만든다면 두 상태 변수를 완전히 독립적으로 만들 수 있다.

    - 그리고 각 함수들이 어떤 변수를 사용하는지도 쉽게 파악할 수있다.

     

    3. 조건부 useEffect의 옳은 방법

    - 잘못된 방법

    function App() {
      const [varA, setVarA] = useState(0);
    
      // 이렇게 하면 안된다.
      useEffect(() => {
        let timeout;
        if (varA < 5) {
          timeout = setTimeout(() => setVarA(varA + 1), 1000);
        }
    
        return () => clearTimeout(timeout);
      }, [varA]);
    
      return <span>Var A: {varA}</span>;
    }

        ㆍclearTimeout이 매번 실행

    - 옳은 방법

    function App() {
      const [varA, setVarA] = useState(0);
    
      useEffect(() => {
        if (varA >= 5) return;
    
        const timeout = setTimeout(() => setVarA(varA + 1), 1000);
    
        return () => clearTimeout(timeout);
      }, [varA]);
    
      return <span>Var A: {varA}</span>;
    }

        ㆍ의도치 않은 useEffect 실행을 막을 수 있다.

     

    4. useEffect 안에서 사용되는 모든 변수들을 dependency 배열에 추가한다.

    - 매우 중요한 규칙이다

    - 앱이 점점 커질수록 useEffect에는 더 많은 dependency가 추가될 수 있다. 모든 디펜던시의 변화를 감지해 깨진 클로저가 만들어지는 것을 피하려면 모든 디펜던시를 디펜던시 배열에 추가해야한다.

     

    참고자료 : https://ui.toast.com/weekly-pick/ko_20200916

     

     

    8. useMemo

    - useMemo는 메모라이즈된 값을 return하는 hook이다

    - useMemo는 이전 값을 기억해두었다가 조건에 따라 재활용하여 성능을 최적화 하는 용도

    const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

    - 유의 사항

        ㆍ모든 함수를 useMemo로 감싸게 되면 이 또한 리소스 낭비가 될 수 있으므로, 퍼포먼스 최적화가 필요한 연산량이 많은 곳에 사용하는 것이 좋다.

     

    - useRef와의 차이

    : useMemo는 deps가 변경되기 전까지 값을 기억하고, 실행 후 값을 보관하는 역할로도 사용한다. 

      복잡한 함수의 return 값을 기억한다는 점에서 useRef와는 다르다

      useRef는 특정 값을 기억하는 경우, useMemo는 복잡한 함수의 return값을 기억하는 경우 사용한다.

     

    React.memo, useMemo, useCallback 정리

    - 공통점

        ㆍReact.memo, useMemo, useCallback은 모두 불필요한 렌더링 또는 연산을 제어하는 용도로

            성능 최적화에 목적이 있다.

    - 차이점

        ㆍReact.memo는 HOC(Higher-Order Components)

           ( HOC란 컴포넌트를 인자로 받아서 새로운 컴포넌트를 return 해주는 구조의 함수 )

           useMemo와 useCallback은 hook이다.

        ㆍReact.memo는 HOC이기 때문에 클래스형 컴포넌트, 함수형 컴포넌트 모두 사용 가능하지만,

            useMemo는 hook이기 때문에 함수형 컴포넌트 안에서만 사용 가능하다.

        ㆍuseMemo는 함수의 연산량이 많을 때 이전 결과값을 재사용하는 목적이고,

            useCallback은 함수가 재생성되는 것을 방지하기 위한 목적이다.

     

    9. React.memo()를 언제 써야할까?

    - 같은 props로 렌더링이 자주 일어나는 컴포넌트

    - React.memo() 는 함수형 컴포넌트에 적용되어 같은 props에 같은 렌더링 결과를 제공한다

    - 사용하기 가장 좋은 케이스는 함수형 컴포넌트가 같은 props로 자주 렌더링 될거라 예상될 때이다.

    'TIL ( Today I Learned )' 카테고리의 다른 글

    2022-08-02 : Spring  (0) 2022.08.02
    2022-08-02 : JAVA  (0) 2022.08.02
    2022-07-29 : JAVA & Spring  (0) 2022.07.29
    2022-07-28 : Spring  (0) 2022.07.28
    2022-07-27 : 파일업로드 & buffer, 기타 등등  (0) 2022.07.27

    댓글

Designed by Tistory.