안녕하세요. IT 엘도라도 에 오신 것을 환영합니다.
글을 쓰는 것은 귀찮지만 다시 찾아보는 것은 더 귀찮습니다.
완전한 나만의 것으로 만들기 위해 지식을 차곡차곡 저장해 보아요.   포스팅 둘러보기 ▼

리액트 (React)

[React] Babel, Webpack 동작 원리 간단히 알아보기

피그브라더 2020. 11. 11. 20:08

이번 포스팅은 React 애플리케이션 개발의 핵심 중 하나인 BabelWebpack에 대해 다뤄볼 것이다. 다만 엄청 자세히는 다루지 않고, React 애플리케이션을 개발하는 사람이라면 알아야 하는 상식 선의 개념 및 동작 원리에 대해서만 다룰 것이다. 실제로 Create React App을 통해 React 애플리케이션을 개발하고 있는 사람은 Babel과 Webpack이 어떻게 설정되어 있는지 확인해보지 못한 경우도 있을 것이다. 그것은 Create React App이 Babel 및 Webpack의 설치와 각종 설정을 내부적으로 이미 다 해주고 이를 숨겨두었기 때문이다(물론 확인하는 방법이 있긴 하다). 그래서 막연하게 개발만 하던 사람은 내부적으로 Babel 및 Webpack이 어떻게 돌아가는지 자세히는 모르고 있을 수도 있다. 필자의 경우가 그랬다. 그래서 이번 기회에 그 둘의 개념 및 동작 원리에 대해 어느 정도는 이해하고 넘어가야겠다고 생각하게 되었다. 필자의 경우와 비슷한 경험을 하고 있는 누군가에게 도움이 되는 포스팅이기를 바란다.

 

※ 본 포스팅을 이해하는 데 앞서, JavaScript의 모듈 시스템에 대한 이해가 부족한 분은 다음 포스팅을 먼저 읽고 오기 바란다.

 

[JavaScript] 모듈 내보내기/불러오기 (CommonJS vs ES6)

1. JavaScript 모듈 시스템 파일 단위의 모듈화는 코드의 재활용성을 극대화 시킴으로써 애플리케이션 개발의 생산성을 엄청나게 향상시킨다. 그런데 초창기 JavaScript는 모듈 시스템이라는 것을 갖

it-eldorado.tistory.com

 

설명은 '개념'에 중점을 둘 것이다. 실제 설치 방법이나 각종 설정 코드들은 맨 아래에 걸어둔 참조 링크들에서 확인하기 바란다.

 

1. Babel (JavaScript 컴파일러)

 

Babel은 Node.js 환경에서 실행되는 JavaScript 애플리케이션의 일종으로, ES6 이상의 JavaScript 코드를 모든 브라우저들에서 호환이 가능한 ES5 이하의 JavaScript 코드로 변환해주는 JavaScript 컴파일러(혹은 트랜스파일러)이다. Node.js 패키지이기 때문에 설치를 위해서는 NPM(Node Package Manager)을 이용하면 된다(npm install @babel/cli @babel/core). Babel의 세부적인 컴파일 옵션을 설정해주는 것도 가능한데, 이를 위해서는 Babel 플러그인(Plugin) 혹은 그것들의 집합인 Babel 프리셋(Preset)을 NPM으로 추가 설치해줘야 한다. 그러고 나서 프로젝트 루트 폴더에 .babelrc 파일을 만들어서 그 안에 컴파일 옵션으로 설정할 원하는 플러그인 혹은 프리셋의 목록을 작성해주면 된다.

 

@babel/cli를 설치하였다면 명령어(EX. babel src/js -w -d dist/js)를 입력함으로써 직접 Babel을 실행시킬 수 있다. 그러면 소스 폴더에 존재하는 JavaScript 파일들을 컴파일하여 목적 폴더에 컴파일된 새로운 JavaScript 파일들을 만들어낼 것이다. 그러나 일반적으로 Babel은 그 자체로 사용되기보다 Webpack(아래 섹션에서 설명할 예정)과 같은 모듈 번들러에 의해 로드되어 사용이 된다. 즉, Webpack이 번들링을 본격적으로 수행하기 전에 ES6 이상의 JavaScript 코드를 먼저 ES5 이하의 JavaScript 코드로 변환해주기 위한 수단으로 Babel을 로드하여 실행하도록 설정하는 것이 일반적으로 통용되는 방식이라는 것이다. 왜 그럴까?

 

React 애플리케이션을 개발하다 보면 여러 JavaScript 파일들은 서로 의존성 관계를 가질 수밖에 없는데, Babel은 JavaScript 파일들을 하위 버전으로 컴파일해줄 뿐 그러한 의존성들을 해결해주기 위한 번들링을 전혀 수행하지 않기 때문이다. ES6 방식의 모듈 내보내기 및 불러오기 코드를 CommonJS 방식의 모듈 내보내기 및 불러오기 코드로 변환해봤자 브라우저는 이 코드들을 이해할 수 없다. CommonJS 방식의 모듈 내보내기 및 불러오기 코드는 Node.js 환경에서 구동되는 JavaScript 애플리케이션들만 이해할 수 있기 때문이다. 물론 ES6 방식의 모듈 내보내기 및 불러오기 코드를 유지하도록 Babel 설정을 해줄 수도 있겠지만, 모든 브라우저가 ES6를 지원하지는 않기 때문에 절대 좋은 방식이 아니다. 또 적절한 모듈 로더 라이브러리를 활용하는 것도 방법이겠다만 이것도 그리 편리한 방식은 아니다. 결국 Babel만 사용해서는 힘들다는 결론에 도달한다.

 

결국, Webpack 등의 모듈 번들러에 Babel을 연결해서 정적 파일들의 복잡한 의존성 관계를 미리 해결하여 번들링 해둔 소수의 정적 파일들을 클라이언트에게 제공해주는 것이 가장 일반적으로 통용되는 방식이 되었다. 모듈 간 의존성 관계의 해결을 클라이언트의 브라우저에게 맡기는 것이 아니라 미리 의존성들을 모두 해결한 상태로(번들링 된 파일의 형태로) 클라이언트에게 서비스한다는 것이다. 그런데 어쨌든 클라이언트에게 서비스하려면 브라우저의 호환성을 고려할 수밖에 없기 때문에, Webpack의 번들링 과정에 Babel을 이용한 컴파일도 사실상 필수가 된다. 결국, Babel은 그 자체적으로 사용되기보다 Webpack과 같은 모듈 번들러에 연결되어 일종의 부품처럼 사용이 되는 방식이 일반적인 관습으로 자리 잡게 되었다.

 

2. Webpack (모듈 번들러)

Webpack (이미지 출처 : https://webpack.js.org/)

 

Webpack은 Node.js 환경에서 실행되는 JavaScript 애플리케이션의 일종으로, 복잡한 의존성 관계들을 가지는 여러 정적 파일(JavaScript, CSS, Sass 등)들을 번들링 하여 적은 정적 파일들을 만들어내는 모듈 번들러이다. Node.js 패키지이기 때문에 설치를 위해서는 NPM(Node Package Manager)을 이용하면 된다(npm install webpack webpack-cli). Webpack을 사용하면 모듈들 간의 의존성이 해결된 상태의 번들링 파일들을 클라이언트에게 제공하는 것이 되기 때문에 별도의 모듈 로더 라이브러리가 필요 없어지고, 일일이 <script> 태그로 여러 개의 JavaScript 파일들을 로드하거나 <link> 태그로 여러 개의 스타일 시트들을 로드하는 번거로움을 겪지 않아도 된다는 장점이 있다.

 

webpack-cli를 설치하였다면 명령어(EX. webpack -w)를 입력함으로써 직접 Webpack을 실행시킬 수 있다. Webpack은 프로젝트 루트 폴더에 존재하는 webpack.config.js 파일을 읽어 번들링 옵션들을 참조한다. 이 파일에는 번들링을 시작할 엔트리 파일들의 목록, 번들링 된 파일들이 저장될 경로, 어떤 이름을 가진 파일들이 어떠한 로더(Loader)와 어떠한 옵션으로 컴파일 되어야 하는지 등과 관련된 각종 번들링 옵션 정보들을 작성해줘야 한다.

 

여기서 말하는 로더(Loader)란 무엇일까? 기본적으로 Webpack은 JavaScript 파일 및 JSON 파일에 대한 번들링밖에 지원하지 않는다. 따라서 JavaScript가 아닌 타입의 정적 파일(CSS 파일, Sass 파일 등)들을 번들링 하려면 누군가의 도움이 필요하다. 그 누군가가 바로 로더이다. 로더가 곧 Webpack의 플러그인(Plugin)인 것이다.

 

예를 들어, css-loader라는 로더는 CSS 파일을 읽어서 스타일 정보들을 문자열 형태로 변환한다. 그리고 style-loader라는 로더는 그 문자열을 읽어와서 index.html 파일의 <head> 태그 내부에 <style> 태그의 형태로 해당 스타일 정보를 삽입하게 하는 JavaScript 파일을 만들어 낸다.

참고로 이와 같이 한 타입의 정적 파일을 읽는 데에 여러 로더가 사용되는 경우, 마지막에 작성된 로더부터 역순으로 실행이 된다. 즉, 처음 실행되는 로더의 아웃풋이 그다음 실행되는 로더의 인풋이 된다.

 

이미 몇 개는 언급하긴 했지만, 대표적인 몇 개의 로더들을 소개하자면 다음과 같다.

 

  • babel-loader : Babel을 불러와서 JavaScript 파일을 ES5 이하의 버전으로 컴파일시킨다.
  • sass-loader : node-sass 패키지를 불러와서 Sass 파일을 CSS 파일로 컴파일시킨다.
  • css-loader : CSS 파일의 스타일 정보들을 문자열 형태로 컴파일한다.
  • style-loader : 문자열이 나타내는 스타일 정보들을 <head> 태그 내부에 <style> 태그의 형태로 삽입하게 하는 JavaScript 파일로 컴파일한다.

 

마지막으로, React 애플리케이션을 개발하다가 생길 수 있는 사소한 의문점에 대해 다뤄보려 한다. public 폴더의 index.html 파일에는 index.js 파일을 로드하는 <script> 태그가 없다. 그렇다면 이 둘은 어떻게 어느 시점에 연결되는 것일까?

 

답은 Webpack 플러그인 중 하나인 html-webpack-plugin에 있다. Create React App으로 만들어진 프로젝트의 설정을 기준으로 설명하자면 다음과 같다. html-webpack-plugin은 새로운 HTML 파일을 만들어내는 역할을 수행하는데, 그 과정에서 public 폴더의 index.html 파일을 참조하도록 설정을 하고 inject 옵션도 true로 설정한다. 그러면 해당 플러그인은 최종적으로 만들어지는 새로운 HTML 파일에 <script> 태그를 삽입하게 된다. 이렇게 만들어진 index.html 파일을 배포 시에 사용하는 것이다.

 

 

 

 

 

 

본 글은 아래 링크의 내용을 참고하여 학습한 내용을 나름대로 정리한 글임을 밝힙니다.

https://poiemaweb.com/es6-babel-webpack-1

https://poiemaweb.com/es6-babel-webpack-2

https://medium.com/a-beginners-guide-for-webpack-2/webpack-loaders-css-and-sass-2cc0079b5b3a

https://medium.com/a-beginners-guide-for-webpack-2/using-sass-9f52e447c5ae

https://stackoverflow.com/questions/42438171/wheres-the-connection-between-index-html-and-index-js-in-a-create-react-app-app

https://blog.sessionstack.com/how-javascript-works-a-deep-dive-into-webpack-cbc9c169eed7