WEB PACK 스터디 3주차

12994 단어 webpackwebpack

📌 주제

리액트 환경 세팅, 코드 스플리팅과 캐싱 적용하기

이번에는, 웹팩에서 리액트를 사용할 수 있게 설정을 하고, 코드 스플리팅과 캐싱을 하는 방법에 대해서 알아보도록 하겠다. 이를 위해서, 진행한 프로젝트는, 기존에 CSS,SCSS를 주로 이용했었기 때문에, Styled-Component 사용법이 미숙하다 판단하여, 유튜브에 있는 Styled-Component 튜토리얼 영상을 보면서 진행을 하였다.

- 결과물

유튜브 링크

📌 리액트 웹팩 설정

웹팩에서 리액트를 사용하기 위한 방법은 간단하다.

npm i -D @babel/preset-react
npm i react react-dom

먼저, 필요한 패키지들을 설치한다음, @babel/preset-react를 babel 설정 파일에 추가 해주기만 하면 된다.

참고로, 리액트를 버전 지정없이 설치하게 되면, 글 작성일 기준으로 18버전이 설치되는데, 그 전과 다르게 루트 DOM 노드 설정이 아래처럼, 달라지게 되었다.

📌 코드 스플리팅

📖 코드 스플리팅이 필요한 이유

보통 SPA(Single Page Application)에서는, html 파일 하나에, 자바스크립트를 이용하여, 화면을 구성하게 되는데, 이러한 특성상, 어떠한 처리없이, 번들링을 하게 되면, 자바스크립트 파일 하나에, 모든 자바스크립트 코드나, 이미지, CSS같은 리소스들도 포함되어, 용량이 매우 커지게 된다. 따라서, SPA는 초기 웹페이지 로딩 시간이, 어플리케이션 규모가 커질수록 길어지게 된다. 로딩시간의 지연은, 성능이슈에 속하고, 로딩시간 지연 문제를 해결하지 않는다면, 이용자들을 잃게 되는 결과를 낳는다.
결과적으로는, 이러한 문제를 해결하기 위해, 번들링 되는 파일을 분할해서, 용량을 줄이고, 로딩시간을 줄이는것이 코드스플리팅의 목적이다. 또한, 번들링 된 파일 단위를 청크파일이라고 한다.

참고로, 2주차때, 설명했던, 이미지를 웹팩의 asset 모듈을 이용해서, 따로 빼내거나, mini-css-exract-plugin을 이용해서, CSS파일을 따로 빼내는 것 또한, 코드 스플리팅을 한 것이다.

📖 코드 스플리팅을 하는 방법

웹팩 공식문서에서 소개하는 일반적인 코드 스플리팅의 방식에는 3가지가 있다.

  • Entry Points: entry 속성에서 entry-point를 나눠서 하는 방법
  • Prevent Duplication: dependOn 옵션을 사용하거나, SplitChunksPlugin을 사용해서 중복되는 Dependency들을 분리하는 방법
  • Dynamic Imports: 코드 내에서 함수 실행을 통해, 동적으로 모듈을 불러오는 방법

Entry Points

이 방법은, 공식문서에 있는 내용으로 설명을 대체한다.

  • webpack.config.js

  • Build 결과

위의 웹팩 설정에서는 index와 another라는 이름을 가진 entry-point를 두개 설정을 하였다. 이렇게 빌드를 하면, 각 entry-point마다 번들링 파일이 생겨서 코드 스플리팅이 된다.

하지만, 이 방법에는 문제가 있다. 각 자바스크립트 파일에는 lodash 모듈이 import 되어 있는 상태이다. 위와 같이 설정을 하였을 때, 만일, 두개의 entry-point 모두에 lodash 모듈이 import 되어 있다면, 중복이 되어 번들링이 되기 때문에, 이는 비효율적으로 모듈이 중복되어 문제가 된다.

Prevent Duplication

dependOn

중복문제를 해결하기위해, entry 속성의 각 entry-point마다 dependOn 옵션을 설정하여, 모듈을 공유하게 설정을 하고, 만일 SPA 기반의 어플리케이션이라면 optimization 속성의 runtimeChunk: 'single' 설정을 추가해주면서, 중복을 해결 할 수 있다.

만일 runtimeChunk: 'single' 설정을 해주지 않는다면, 특정 데이터를 조작할 때, 그 데이터를 공유하지 않아 일관성이 유지가 되지 않는 문제점이 있다고 한다.
해당 문제점을 설명하는 링크

  • webpack.config.js

  • Build 결과

결과를 살펴보면, shared 번들링 파일로 lodash 모듈이 분리가 되고, 각 entry-point 용량이 전과 다르게, 중복이 해결되서 용량이 감소한 것을 알 수 있다.

splitChunks

optimization 속성에, spitChunks옵션을 이용해서 설정하게 되면, 자동으로 entry-point마다 중복되는 dependncy들을 따로 빼내서 번들링 하게 된다.

  • webpack.config.js
  • Build 결과

빌드 결과 로그를 살펴보면 vendors-node_modules_lodash 라는 이름의 번들링 파일이 생성 되었으며, 각 entry-point마다 해당 번들링 파일을 가리키고 있는 것을 알 수 있다.

Dynamic Imports

이 방법은 동적으로 필요할 때, 해당 모듈을 import 하는 방식이다. 이번에는, 직접 진행한 프로젝트에 적용해보면서 해당 방법을 알아보도록 하겠다.

메인 페이지에서 우측 상단에 (Try It Free) 버튼을 클릭하면, "code splitting용 텍스트"라는 문자열을 리턴하는 모듈을 동적으로 import하고 alert로 해당 문장을 표시하도록 해보겠다.

//codeSplitting.js
export const codeSplitting = () => {
  return 'code splitting 용 텍스트';
};

이 모듈을 동적으로 import 하기 위해서는, 특정 이벤트가 발생 했을 때, 모듈을 실행 시킬 수 있도록 함수를 정의 해줘야 한다.

function dynamicImport() {
  return import(/* webpackChunkName: "test"*/ "../utils/codeSplitting.js").then(
    ({ codeSplitting }) => codeSplitting()
  );
}

해당 함수가 실행이 되면, 해당 경로의 모듈을 import 하고 모듈이 실행된 결과물을 배출 하는 함수이다. catch를 통해, 모듈을 import하지 못했을 때, 예외 처리를 할 수도 있다.

import 문 안에 /* webpackChunkName: "번들링파일이름"*/ 을 통해 해당 모듈의 번들링 파일 이름을 설정할 수 있다. 본인은 test라는 이름으로 설정을 하였다.

const onClickHandler = () => {
    dynamicImport().then((res) => alert(res));
  };

버튼을 클릭 했을 때, 이벤트를 핸들링 하는 함수를 만들어주고, 위에서 정의한 dynamicImport 함수를 실행시키도록 하였다.

빌드를 하게 되면, test라는 이름의 번들링 파일이 생긴 것을 알 수 있다.

그럼 이제, 직접 버튼을 클릭하면, 어떻게 동적으로 import되는지 눈으로 확인해보자.

버튼을 클릭하면, test 번들링 파일이 불러와 지는 것을 알 수 있다.

React lazy

앞에서, 언급했던 방법대로 Dynamic imports를 통해 코드 스플리팅 하는 방법이 있지만, 리액트에서는 따로 lazy loading을 통해 코드 스플리팅을 할 수 있도록, 지원해준다.

리액트 공식문서에서, 소개하듯이, Suspense로 감싸주고, 그 안에 React.lazy를 통해 불러온 컴포넌트를 넣어주면 필요할 때, 동적으로 import할 수 있다.

이 방식을 이용해서, 본인이 진행한 프로젝트에 진행을 해본 결과를 눈으로 확인해보자. 본인은 컴포넌트 단위로 lazy loading 한것이 아니라 페이지 단위로 진행을 하였다.

const Main = lazy(() => import('./pages/Main' /* webpackPrefetch: true */));
const Second = lazy(() => import('./pages/Second' /* webpackPrefetch: true */));
<Suspense fallback={<div>suspense fallback</div>}>
            <BrowserRouter>
              <Routes>
                <Route path='/' element={<Main />}></Route>
                <Route path='/second' element={<Second />}></Route>
              </Routes>
            </BrowserRouter>
 </Suspense>
  • Build 결과

    각 페이지 마다, lazy loading을 적용한 것이, 빌드 결과물에서는 117 과 469라는 이름을 가진 결과물이 나오게 되었다.

  • 라우팅 주소 : /

  • 라우팅 주소 : /second

    각 페이지에 맞는 번들링 파일이 불러와진 것을 알 수 있다.

📖 코드 스플리팅의 문제점

여기까지, 코드 스플리팅에 대해서 알아보았다. 하지만 코드 스플리팅을 하게 되면, 분할한 파일 만큼 웹페이지 로드 시에, 그 만큼 많은 HTTP요청을 하게 된다. 1주차 때, 소개 했듯이, 브라우저 마다 HTTP요청 횟수는 제한되어 있기 때문에, 코드 스플리팅을 통해 증가한 HTTP 요청 숫자 또한 성능문제를 야기할 수 있다. 따라서, 이를 해결하기 위해 적용되는 개념이 캐싱이다. 이제부터 캐싱적용 방법에 대해서 알아보겠다.

📌 캐싱

캐시는 데이터나 값을 미리 복사해 놓는 임시 장소를 가리킨다. 캐시는 캐시의 접근 시간에 비해 원래 데이터를 접근하는 시간이 오래 걸리는 경우나 값을 다시 계산하는 시간을 절약하고 싶은 경우에 사용한다. 캐시에 데이터를 미리 복사해 놓으면 계산이나 접근 시간 없이 더 빠른 속도로 데이터에 접근할 수 있다.

불필요한 네트워크 트래픽을 방지하기 위해, 기존 내용물에 변경사항이 없다면, 캐시를 적용해서 해결할 수 있다. 이제부터 웹팩에서 캐시를 설정하는 방법에 대해서 알아보자.

  • webpack.config.js

설정자체는 매우 간단하다. output 속성의 번들링 파일의 이름에 [contenthash] 를 추가해서 뒤에 해시값을 덧붙이는 것이다. 이렇게 설정을 하면, 웹팩에서는 변경사항이 생긴 번들링 파일만 뒤의 해시값이 변경이 되도록 하고, 브라우저에서 이를 통해 해시값이 변경이 되지않은 파일들은 캐시를 적용 할 수 있는 것이다.
이 설정과 더불어, 캐시 적용을 할 때, SplitChunksPlugin도 이용한다면, 더 좋은 효과를 얻을 수 있다.

  • webpack.config.js

    위 웹팩 설정을 살펴보면, SplitChunksPlugin안에 cacheGroups라는 옵션에서 vendors라는 이름의 번들링 파일을 따로 빼내는 설정을 해주고 있다. 번들링의 대상이 되는 모듈들은 node_modules안에 있는 모든 패키지들이다. 이렇게 처리하는 이유는, 설치한 패키지들은 사실상 수정할일이 거의 없기 때문에, 따로 빼내서 관리한다면, 매번 빌드할 때마다, 해시값이 유지되어 캐싱을 적용할 수 있기 때문이다.

그럼 이제부터 직접 진행한 프로젝트의 특정 파일을 변경해서, 해시값이 어떻게 변하는지 알아보자.

App.js 파일을 변경해서 확인해보자.

- Before

- After

결과를 살펴보면 main 청크 파일의 해시값이 바뀐 것을 알 수 있다..

브라우저에서는 캐싱이 되고있다는 사실을 304(Not Modified) 상태 코드를 통해 할 수 있다.
새로고침을 하면 변경이 되지않은 캐싱이 되는 파일들의 상태코드는 304로 표기된다.

직접 App.js파일을 변경하여 캐싱이 어떻게 적용되는지 확인하려 했으나, 빌드를 진행할 때마다, 브라우저에서 캐싱이 풀리게 된다. 그 이유를 짐작해 봤을 때, CleanWebpackPlugin으로 인해 파일이 지워졌다 새로 생성되면서, 해시값은 변하지 않았더라도, 파일은 지워졌다가, 새로 생성되기 때문에, 변경사항이 생겼다 판단하여, 캐시가 지워지는 것 같다.

📌 결론

이번 글에서는, 리액트 환경을 설정하고, 코드 스플리팅과, 캐싱에 대해서 알아보았는데, 지금까지 공부한 내용으로, 웹팩을 직접 설정해 볼 수 있는 능력이 생긴 것 같다. 또한, 글에서는 소개하고 있지 않지만, Dotenv 플러그인을 사용해서 환경변수를 관리 할 수 있게도 처리하였고, webpack-merge를 통하여 설정파일들을 따로 관리할 수 있게도 설정하였다. 웹팩에 대해서 알면 알수록, 더욱 깊은 내용이 기다리고 있는 것 같고, 앞으로도 계속 공부를 하게 될 부분이지 않을까 싶다. 그리고 다음 주차는, 요즘 많이 쓰이는 Typescript 세팅을 해보고, eslint와 prettier 설정해보는 기간이다. 특히 eslint와 prettier설정이 협업에 중요하므로, 열심히 공부할 예정이다.

References

https://webpack.js.org/guides/code-splitting/
https://ko.wikipedia.org/wiki/%EC%BA%90%EC%8B%9C
https://ko.reactjs.org/
https://www.youtube.com/watch?v=02zO0hZmwnw&t=2s
https://www.zerocho.com/category/Webpack/post/58ad4c9d1136440018ba44e7

좋은 웹페이지 즐겨찾기