Babel & Webpack & CRA

박정호·2022년 8월 30일
1

JS

목록 보기
15/24
post-thumbnail

⭐️ Babel

우리가 개발한 서비스는 다양한 브라우저에서 제공되어야한다. 즉, 크로스 브라우징이 이루어져야 한다는 뜻이며 단지 서비스의 제공뿐만 아니라 호환성, 효율성 등의 최적화 문제도 해결되어야한다.

따라서, 다양한 환경을 고려해야 하는데 이때 바벨을 사용하게 된다. 새로운 문법이나 타입스크립트 혹은 JSX 같이 다른 언어로 분류되는 언어들에 대해서도 모든 브라우저에서 동작할 수 있도록 호환성을 지켜주는 것이다.

👍 쉽게 말해, Babel을 통해 이러한 언어 기술들을 모든 브라우저에서 작동되는 하위버전(ES5)의 자바스크립트 언어로 변환해주는 것이다. 바벨이란 자바스크립트 컴파일러이다.

💡 중요) 크로스 브라우징

최대한 많은 종류의 웹 브라우저에서 정상적으로 작동하는 웹페이지를 만드는 방법론 중 하나.

HTML, CSS, Javascript 작성 시 W3C의 웹 규격에 맞는 코딩을 함으로써 어느 브라우저, 기기에서 사이트가 의도된 대로 보여지고 작동되는 기법.

크로스 브라우징 작업 필요 이유
: 브라우저 마다 랜더링 엔진이 다르기 때문

  • 작동되지 않는 HTML5, Javascript 코드가 존재
  • 해석하지 못하는 CSS 코드 존재
  • 브라우저 버그들이 존재
  • 브라우저마다 자체적인 CSS 스타일

💡 잠깐) 컴파일러

컴파일러란 인간 수준의 고언어로 작성된 프로그램을 기계어로 된 프로그램으로 출력하는 번역기이다. 컴파일러는ㄴ 전체를 한번 훑고 컴퓨터가 이해할 수 있는 기계어로 번역한다. 일반적으로 컴파일러가 인터프리터보다 실행이 빠르다.

자세한 설명: 컴파일러 vs 인터프리터

1️⃣ Babel 동작단계

바벨은 3단계로 빌드를 진행한다.

✏️ Parsing(파싱)

코드를 읽고 추상 구문 트리로 변환하는 단계

✏️ Transforming(변환)

추상 구문 트리를 변경하는 단계

💡 잠깐) 추상 구문 트리(Abstract Systax Tree)

프로그램 내에서 발생하는 기능을 나타내기 위해 만들어진 구문 구조이다. 이는 고수준의 언어를 기계어로 변환하는 과정에서 꼭 필요하고 코드를 트리 구조의 데이터 스트럭쳐로 만들어낸다. 이렇게 구조화시키면 프로그래머도, 컴퓨터도, 훨씬 파악하기 쉬워진다.

✏️ Printing(출력)

변경된 결과물을 출력하는 단계

2️⃣ Babel polyfill

babel은 단순히 문법만 변환해주는 역할만할 뿐이다. 바벨을 사용한다고 최신 함수를 사용할 수 있는 것이 아니고 최신함수를 사용하기위해서는 object의 prototype에 붙여주는 역할이 필요한데 이를 polyfill이 해주는 것이다.

polyfill은 스크립트에 사용자가 원하는 최신 함수를 추가한다. 자바스크립트는 동적인 언어이기 때문에 원하면 어떤 함수도 스크립트에 추가가 가능하다. 바로 이 역할을 polyfill이 해주는 것이다.
polyfill은 개발자가 특정 기능이 지원되지 않는 브라우저를 위해 사용할 수 있는 코드조각이나 플러그인을 의미한다.(브라우저에서 지원하지 않는 기능들에 대한 호환성 작업을 채워넣는다고해서 polyfill)

💡 중요) babel은 컴파일-타임에 실행되고, bablel-polyfill 은 런-타임에 실행.

3️⃣ Babel 사용순서

✏️ 1. 프로젝트 생성

$ mkdir babel-practice
$ cd babel-practice
$ yarn init -y

✏️ 2. ES2015 arrow function 문법을 사용하여 코드 작성

[1, 2, 3].map(n => n + 1);

✏️ 3. Babel 설치

프로젝트에서 @babel/core와 @babel/cli 패키지를 개발 의존성(devDependencies)으로 설치.

yarn add -D @babel/core @babel/cli

💡 잠깐) 개발 의존성으로 설치하는 이유

바벨이 애플리케이션이 실행될 때 필요한 것이 아니라 빌드할 때만 필요하기 때문.

  • @babel/core: 바벨을 사용하는데 필요한 패키지
  • @babel/cli: 터미널에서 커맨드를 입력해서 바벨을 사용하기 위해 필요한 패키지

✏️ 4. Babel 적용

기존의 코드에서 변화가 없다. 왜냐하면 바벨에게 코드를 어떻게 변환해야 하는지 규칙을 알려주지 않았기 때문이다.

$ npx babel test.js
[1, 2, 3].map(n => {
  n + 1;
});

✏️ 5. Plugin/Preset 설정

바벨에서 플러그인(plugin)이나 프리셋(preset)을 통해 문법 변환 규칙을 알려주기 가능

  • 플러그인(plugin): 규칙 하나하나를 세밀하게 적용할 때 사용
  • 프리셋(preset): 이런 규칙들을 모아놓은 세트로, 주로 한 번에 적용할 때 사용

넓게 이용되는 프리셋인 env를 사용

  • env는 ES6 이상(ES2015+)의 문법으로 작성된 코드를 ES5 문법의 코드로 변환해주는 모든 규칙을 정의
yarn add -D @babel/preset-env

바벨에게 코드 변환의 규칙으로 env를 알려주기

$ npx babel test.js --presets=@babel/env
"use strict";

[1, 2, 3].map(function (n) {
  n + 1;
});

✏️ 6. 바벨 설정 파일

매번 바벨에게 변환될 규칙을 알려주는 것은 매우 비효율적이다. 따라서 바벨 설정파일인 .babelrc, babel.config.js, babel.config.json을 이용

  • .babelrc: 주로 하위 디렉토리나 파일에서 특정 플러그인이나 변환(규칙)을 실행할 때 적절

  • babel.config.js: 여러 패키지 디렉토리를 가진 프로젝트에서 규칙을 설정할 때 유용
    (babel.config.js가 보다 보편적으로 사용)

  • babel.config.json: babel.config.js를 사용하면 구성하는 api가 노출. 이러한 방식은 캐싱과 관련해서 복잡성을 증가시키므로, 대부분은 정적인 구성인 babel.config.json을 사용하는 게 더 좋은 선택이다.

프로젝트 최상위 디렉토리에 .babelrc 파일을 생성

// .babelrc
{
  "presets": ["@babel/env"]
}

터미널에서 바벨 옵션을 주지 않고 실행해도 규칙이 적용 가능

$ npx babel test.js
"use strict";

[1, 2, 3].map(function (n) {
  n + 1;
});

⭐️ Webpack

웹사이트를 구성할때 .js .css .images 파일등 수 많은 들이 모여 웹사이트를 구성하게 된다. 따라서 웹사이트에 접속했을때 굉장히 많은 파일이 다운로드될 수 있는데 이것에 비례하여 서버의 자원을 소모하고 웹사이트가 느리게 로딩이 된다.

👍 이러한 현상을 해결하기 위해 나온 '묶는다'는 개념의 번들러가 등장하였고 Weppack, Broserify, Parcel 과 같은 도구들이 번들러에 속한다. 그 중에서도 가장 인기 있는 번들러가 웹팩이다.

번들링을 함으로써 파일은 하나로 합쳐지고 네트워킹 요청횟수는 줄어들고 중복된 소스코드도 최소화

💡 잠깐) Bundle?

  • 소프트웨어 및 일부 하드웨어와 함께 작동하는 데 필요한 모든 것을 포함하는 Package
  • 각각의 모듈들에 대해 의존성 관계를 파악하여 하나 또는 여러개의 그룹

모듈 번들링에서는 빌드 = 번들링 = 변환

1️⃣ Webpack 정의

모던 JavaScript 애플리케이션을 위한 정적 모듈 번들러

모듈 번들러란 웹 애플리케이션을 구성하는 자원(HTML, CSS, Javscript, Images 등)을 모두 각각의 모듈로 보고 이를 조합해서 병합된 하나의 결과물을 만드는 도구를 의미

💡 중요) 즉, Webpack은 여러 파일을 하나 이상의 파일로 합쳐주는 자바스크립트 번들러

2️⃣ Webpack 사용이유

  • 파일 단위의 자바스크립트 모듈 관리의 필요성
  • 웹 개발 작업 자동화 도구 (Web Task Manager)
  • 웹 애플리케이션의 빠른 로딩 속도와 높은 성능

💡 중요) Webpack을 통해 해결하려는 문제

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

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

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

TCP 스펙에 따라 브라우저에서 한 번에 서버로 보낼 수 있는 HTTP 요청 숫자는 제약되어 있다고 한다.
http/2에서는 하나의 커넥션에 동시에 여러 파일들을 요청할 수 있지만,
우리가 주로 사용하는 http/1.1에서는 하나의 커넥션에서 하나씩 요청을 보내야 한다.

예를들어 하나의 웹 사이트에서 사용하는 자바스크립트 파일이 10개라면 로드될때마다 10개를 모두 네트워크 요청을 통해 받아와야 하며 프로젝트 규모가 커질 수록 병목현상을 야기시키는 요소 중 하나이다.

웹팩은 여러 파일을 하나 이상의 파일로 합쳐 맨 처음 언급하였던 서버로 부터 파일을 다운로드 받는 횟수가 줄어들게 되고 이 효과로 인해 브라우저별 HTTP 요청 숫자 제약을 피할 수 있다.

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

✏️ 4. Dynamic Loading 및 Lazy Loading 미지원 문제
이전에는 Require.js 같은 라이브러리를 사용하지 않는 이상 동적으로 원하는 순간에 모듈을 로딩하는 것이 불가능 했다.웹팩에서는 Code Splitting 기능을 이용하여 원하는 모듈을 원하는 타이밍에 로딩할 수 있다

👉 출처: https://codermun-log.tistory.com/m/436

💡 잠깐) Webpack vs Babel

  • Webpack : JavaScript의 static module bundler
  • Babel : JavaScript Compiler (Transpiler)

Webpack은 의존성을 분석해서 모듈을 번들시켜주는 역할이다. 프로젝트를 개발하고 빌드라는 과정을 통해 하나의 파일로 만들어준다.

Babel은 코드 변환기이다. 다양한 브라우저들에서 서비스를 실행하기 위하여 최신버전의 자바스크립트 문법을 구 버전 문법으로 변환해준다.

3️⃣ Webpack 요소

✏️ Entry

엔트리 속성은 웹팩에서 웹 자원을 변환하기 위해 필요한 최초 진입점이다. 즉 entry로 묶고자하는 파일의 첫번째 진입점을 설정해주면 된다. 또한, dependency graph를 만들기 위해 필요한 Input Source이다.

여러개의 entry가 존재 가능하고, Default values는 ./src/index.js이다.

최초 진입점이 되는 대상 파일은 웹 애플리케이션의 전반적인 구조와 내용이 담겨있어야 한다. 그래야 웹팩이 해당 파일을 토대로 모듈들의 연관관계에 대해 이해하고 분석하고 합차기 때문이다.

// webpack.config.js

// Single Page Application(SPA)

module.exports = {
  entry: './src/index.js'
}

// Multi Page Application (MPA)

module.exports = {
  entry: {
  login: './src/LoginView.js',
  main: './src/MainView.js'
}
}

💡 잠깐) dependency graph
모듈간의 의존관계가 생기는 구조

✏️ Output

웹팩을 실행하여 빌드하고 난 후 결과물의 파일 경로를 의미하며 여러가지 옵션을 넣을 수 있으며, path 속성은 해당 파일의 경로를 뜻한다.(path 속성에서 사용된 메서드는 인자로 받은 경로를 조합하여 유효한 파일 경로를 만드는 Nods.jsAPI)

Webpack이 생성한 bundles의 결과물의 위치를 지정 가능하고, Default value는 ./dist/main.js이다.

// webpack.config.js
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 등을 변환할 수 있게 도와주는 속성

Webpack은 오직 Javascript와 Json만 이해할 수 있는 단점이 있다.

웹팩은 모든 파일을 모듈로 취급하여 관리하는데 사실상 Javascript와 Json만 이해 가능하기 때문에 로더를 이용해 다른 파일들을 웹팩이 이해하게끔 변경해줘야 한다.
만약 로더로 설정을 지정해주지 않으면 웹팩이 해당 파일을 읽을 수 없기 때문에 에러가 발생한다.

아래와 같이 rules라는 객체로 속성을 지정.

  • test => 로더를 적용할 파일 유형
  • use => 해당 파일에 적용할 로더의 이름
module.exports = {
  module: {
    rules: [
      { test: /\.css$/, use: 'css-loader' },
      { test: /\.ts$/, use: 'ts-loader' },
      // ...
    ]
  }
}

✏️ Plugin
웹팩의 기본적인 동작에 추가적인 기능을 제공하는 속성이다.

로더랑 역할을 비교해보자면 로더는 파일을 해석하고 변환하는 과정에 관여하며, 플러그인은 해당 결과물의 형태를 바꾸는 역할을 한다고 볼 수 있다.

  • HtmlWebpackPlugin : 웹팩으로 빌드한 결과물로 HTML 파일을 생성해주는 플러그인
  • ProgressPlugin : 웹팩의 빌드 진행율을 표시해주는 플러그인
// webpack.config.js
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  plugins: [
    new HtmlWebpackPlugin(),
    new webpack.ProgressPlugin()
  ]
}

👍 참고하자! https://www.daleseo.com/webpack-config/

4️⃣ Webpack 장단점

👍 장점

✏️ 성능 최적화 & 자동화

  • 코드 축소와 더불어 사용하지 않는 코드를 제거하는 tree shaking과 같은 최적화를 수행 함으로써 HTTP 요청 수를 감소하여 웹사이트 성능을 궁극적으로 향상한다.

✏️ 번들러가 제공하는 편의성

  • CSS가 아닌 Sass나 Stylus 등을 사용할 경우, 또는 TypeScript 사용 시 번들러가 컴파일 과정에서 필요한 플러그인을 추가하고 번들러를 실행해준다.

✏️ Code splitting을 통해 “Load what you need, when you need (필요할 때만 필요한 스크립트를 로드)” 옵션

  • Code splitting을 통해 원하는 때에 시시 적절히 모듈을 로딩할 수 있는 Dynamic Loading & Lazy Loading 이 가능하다.

👎 단점

  • 비교적 복잡한 configuration(배열형태)
  • Webpack의 번들 크기

⭐️ CRA

React 앱을 개발할 때 개발환경을 쉽게 구축하기 위해 Creat React App을 사용한다.

1️⃣ CRA 기능

✏️ 1. Less to Learn 자동 최적화

  • 패키지, 리액트의 지속적인 버전업에 대비해 페이스북에서 리액트의 지속적인 업데이트를 담당한다. 이때 필요한 명령이 CRA인 것이며, 발자의 노력 없이 CRA를 실행시키는 것만으로도 자동 최적화된다.

✏️ 2. Only One Dependency

  • 앱에는 하나의 빌드 dependency가 필요하다. 모든 기본 요소가 복잡한 버전 불일치 없이 원활하게 작동하는지 확인하는 역할을 CRA가 한다.

✏️ 3. No Lock-In

  • CRA는 웹팩, 바벨, ESLint 등 다양한 패키지를 포함하고 있다. 고급 구성이 필요한 경우 CRA에서 언제든지 꺼내어 구성 파일을 직접 편집할 수 있다.

💡 잠깐) CRA 자동 세팅 사항

  • webpack(모듈+라이브러리 번들링)
  • babel(jsx 컴파일)
  • jest(테스트)
  • eslint(코드형상관리)
  • polyfill(구형 브라우저 미지원 문법 등 대응)
  • HMR(Hot Module Replacement: 재시작 없이 변경사항 반영)
  • CSS 후처리(SASS 컴파일, 벤더접두사prefix 등)

2️⃣ CRA 구성

✏️ node.modules

  • CRA를 구성하는 모든 패키지 소스 코드가 존재하는 폴더

✏️ package.json

  • CRA 기본 패키지 외 추가로 설치된 라이브러리/패키지 정보(추가 패키지, 버전)이 기록되는 파일

✏️ gitignore

  • github에 올리고 싶지 않은 폴더와 파일을 생성

✏️ Public

  • 화면에 실제로 출력되는 화면 파일

✏️ Src

  • 작업 시 코드들을 저장하는 공간.

3️⃣ CRA 명령어

✏️ npm start

  • 리액트 개발 서버 구동 (디폴트 http. 최적화 안 된 개발 모드로 실행된다. 배포할 땐 build된 파일로 서비스 필요)

HTTPS 구동 방법

  • mac : HTTPS=true npm start
  • window : set HTTPS=true && npm start

✏️ npm build

  • 실제 서버 배포시, 빌드된 정적 파일들을 응답해주기만 하면 된다.(별도 서버 애플리케이션은 필요 X)

✏️ npm test

  • 파일이름.test.js, 파일이름.spec.js 형식도 테스트 파일로 인식. tests폴더/ 내부의 파일도 모두 테스트 파일로 인식

✏️ npm eject

  • react-scripts 를 사용하지 않고, 모든 설정 파일을 추출

😃 CRA없이 세팅하기

지금까지는 나는 React 앱을 개발할 때 항상 쉬운 개발환경 구축을 위하여 CRA를 이용하여 프로젝트를 생성하였다. 이는 앞서 공부한 Babel과 Webpack의 기능을 포함하기 때문에 프로젝트에 Babel, Webpack을 사용해볼 기회가 없었다.

👉 따라서, CRA 없이 개발환경을 구축해보자.

✏️ 1. package.json 생성 및 react 설치

가장 기본적인 라이브러리, 모듈을 설치한다.

  • react : 리액트 코어 라이브러리
  • react-dom : 리액트와 DOM을 연결
// package.json 생성
yarn init -y
yarn add react react-dom

✏️ 2. Babel 설치

앞서 작성한 Babel 작성 방법을 참고하자.

  • @babel/core : 바벨 코어
  • @babel/preset-react : 리액트의 JSX 코드를 트랜스파일링
  • @babel/cpreset-env : ES6+ 코드를 ES5 코드로 트랜스파일링 (+폴리필 자동화)
yarn add -D @babel/core @babel/preset-react @babel/preset-env

✏️ 3. webpack과 관련 모듈(loader) 설치

앞서 보았던 웹팩의 핵심 요소 중 하나인 로더에는 다양한 로더들이 존재하고 웹팩과 관련한 것들을 설치한다.

  • webpack : 웹팩 코어
  • webpack-cli : 터미널의 커맨드라인에서 웹팩 사용
  • webpack-dev-server : 웹팩을 메모리 상으로만 빌드한 결과물을 개발 서버에 구동
  • babel-loader : ES6+, JSX 문법을 트랜스파일링
  • css-loader : CSS 코드를 JS로 변환
  • style-loader : 변환된 css파일을 index.hmtl의
  • file-loader : 이미지, 폰트 등의 파일 로딩
  • json-loader : json 파일 로딩
  • dotenv : .env 파일을 환경변수에 대신 설정해줌
// webpack 설치
yarn add -D webpack webpack-cli webpack-dev-server

// 관련모듈 설치
yarn add -D babel-loader css-loader style-loader file-loader

// 프로젝트에서 json 사용 시  추가로 설치했다. (선택)
yarn add -D json-loader

// .env 파일을 사용한다면 설치 (선택)
yarn add dotenv

✏️ 4. webpack으로 번들링 한 후의 파일에 적용할 플러그인을 설치

  • html-webpack-plugin : html 파일에 번들링된 JS 코드를 삽입하고 dist 폴더에 번들링된 결과물을 옮겨줌
  • clean-webpack-plugin : 번들링을 할 때 마다 이전의 번들링 결과를 제거
yarn add -D html-webpack-plugin clean-webpack-plugin

✏️ 5. babel, webpack 설정

프로젝트 루트에 babel.config.js 파일을 만들고 프리셋을 설정한다.

// babel.config.js
module.exports = {
  presets: ['@babel/preset-env', '@babel/preset-react'],
};

프로젝트 루트에 webpack.config.js 파일과 .env파일을 만든다.
(웹팩 설정에 따라 프로젝트 결과물이 달라질 수 있으며, 설정 옵션이 많기 때문에 webpack 사이트를 참고하면서 필요한 것 설정)

//.env
MODE = 'development'; // development(개발 모드), production(프로덕션 모드)
PORT = '3000';
// webpack.config.js
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const dotenv = require('dotenv').config();

module.exports = {
  mode: process.env.MODE, // 만약 환경변수를 사용하지 않는다면 직접 'development' 입력
  entry: './src/index.js',
  output: {
    // 번들링 결과 : /dist폴더
    path: __dirname + '/dist',
    // bundle.해쉬.js로 생성
    filename: 'bundle.[hash].js',
    publicPath: '/',
  },
  resolve: {
    // 번들링을 할 파일 설정
    extensions: ['.js', '.jsx'],
  },
  module: {
    // loader 설정 - 등록한 로더의 뒤의 요소부터 번들링에 반영
    // node_modules 제외
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: '/node_modules/',
        loader: 'babel-loader',
      },
      {
        test: /\.css$/,
        use: [{ loader: 'style-loader' }, { loader: 'css-loader' }],
      },
      {
        test: /\.(png|jpe?g|gif|svg)$/i,
        loader: 'file-loader',
        options: {
          name: 'assets/[contenthash].[ext]',
        },
      },
    ],
  },
  plugins: [
    // 빌드 이전 결과물을 제거
    new CleanWebpackPlugin(),
    // 번들한 css파일과 js파일을 html 파일에 link 태그, script태그로 추가
    new HtmlWebpackPlugin({
      template: 'public/index.html',
    }),
    // 환경 정보를 제공
    new webpack.DefinePlugin({
      mode: process.env.MODE,
      port: process.env.PORT,
    }),
  ],
  devServer: {
    host: 'localhost',
    port: process.env.PORT,
    open: true,
    historyApiFallback: true,
    // hot : 모듈의 변화된 부분만 서버에 자동으로 반영
    hot: true,
  },
};

✏️ 6. package.json 설정

  • start : 개발서버에서 리액트 프로젝트 수행
  • build : dist 폴더에 번들링된 파일 생성
// package.json
"scripts": {
  "start": "webpack-dev-server --progress --mode development",
  "build": "webpack --progress --mode production"
}

✏️ 7. 리액트 컴포넌트 생성
루트에 public 폴더를 만들고 index.html 파일을 만든다.(CRA을 사용하지 않았으므로 직접 작성해야 한다)

<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=divice-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>title</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>

src 폴더를 생성하고 index.js와 App.js 파일을 만들고 다음과 같이 입력한다.

// index.js
import React from 'react';
import ReactDom from 'react-dom';
import App from './App';

// public/index.html 파일에서 root아이디를 가진 DOM에 랜더
ReactDom.render(<App />, document.getElementById('root'));
// App.js
import React from 'react';

const App = () => {
  return (
    <div className="App">
      <h1>Hello React World!</h1>
    </div>
  );
};
export default App;

✏️ 8. yarn start로 프로젝트 실행하면 서버가 실행

참조 및 참고하기 좋은 사이트

profile
기록하여 기억하고, 계획하여 실천하자. will be a FE developer (HOME버튼을 클릭하여 Notion으로 놀러오세요!)

0개의 댓글