Babel
은 자바스크립트 컴파일러입니다
Babel은 JSX 문법을 사용할 수 있게끔 만들어주는 툴로써 사용됩니다
그런데 왜 인터프리터 언어에 별도의 컴파일러가 필요한 걸까요?
우선 그 이유에 대해 알아보겠습니다
자바스크립트 문법은 꾸준히 진화해왔습니다
// ES5 -> ES6
var -> let, const...
그런데 신문법으로 작성된 코드의 경우 브라우저(크롬 버전)에 따라서 실행이 되지 않을 수 있습니다
그렇다면 let, const나 화살표 함수 등을 읽을 수 없는 환경에서 신문법으로 작성한 코드를 실행하려면 어떻게 해야 할까요
단순히 var, function 등으로 코드를 수정하는 것이 아닌, 호이스팅과 같은 순서상의 이슈까지 모두 고려해야 합니다
이 때 babel을 이용하면 손쉽게 신문법을 과거 문법으로 변환할 수 있습니다
확인을 위해 babel을 설치 & 사용해보겠습니다
1. Babel 설치
npm init -y
npm install @babel/core @babel/cli @babel/preset-env
npx babel
// = node ./node_../..../index.js
npm
을 통해 설치를 마치면 npx
명령어로 실행할 수 있습니다
2. Babel 환경 설정
.babelrc
파일을 생성합니다
rc
(Run Commands / Run Controller)는 환경파일을 의미합니다
{
"presets": ["@babel/preset-env"]
}
(위 프리셋은 ES6 → ES5 변환을 가능하게 해주는 설정입니다)
3. ES6 문법으로 코드 작성
ES6 문법인 스프레드 연산자를 사용해서 예제 코드를 작성합니다
const fn = (message) => {
const arr = [1,2,3,4,5]
const arr2 = [6,7,8,9,0]
const arr3 = [...arr, ...arr2]
console.log(...arr3, message)
}
fn('hello world!')
4. Babel 실행 (컴파일)
npx babel [바꿀 파일] --out-file [보낼 파일]
npx babel example.js --out-file dist/example.js
npx babel...
의 실행결과는 다음과 같습니다
"use strict";
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
var fn = function fn(message) {
var _console;
var arr = [1, 2, 3, 4, 5];
var arr2 = [6, 7, 8, 9, 0];
var arr3 = [].concat(arr, arr2);
(_console = console).log.apply(_console, _toConsumableArray(arr3).concat([message]));
};
fn('hello world!');
이제 구 버전의 브라우저에서도 사용할 수 있는 코드가 되었습니다
그리고 위 코드는 document
나 module
과 같은 각 런타임 전용 코드를 사용하지 않았기 때문에
브라우저에서도, NodeJs에서도 사용할 수 있습니다
자바스크립트는 런타입 환경에 따라 사용하는 코드가 일부분 달라집니다
예를 들어, 브라우저의 자바스크립트의 전역객체는 window
입니다
이 전역객체에 내장된 document
객체 등은 브라우저 환경에서만 사용할 수 있습니다
반면 NodeJS의 자바스크립트의 전역객체는 global
이며,
그 안에 내장된 module
객체나 require
와 같은 함수들은 브라우저에서 사용할 수 없습니다
이렇게 모듈을 작성하고 로드하는데 사용되는 자바스크립트의 표준을 CommonJS라고 합니다
CommonJS의 모듈 정의 규칙
- 모듈은
module.exports
객체를 통해 공개됩니다- 다른 모듈을 가져오려면
require()
함수를 사용합니다- 모듈을 정의하려면
exports
객체를 사용합니다
그런데 과거(ES6 이전) window 객체 안에는 module 객체가 없었지만
지금의 자바스크립트에는 모듈을 내보내고 불러오는 데 사용할 수 있는 import
구문이 생겼습니다
- Broswer(ES6) :
exports
,require
- NodeJS(CommonJS) :
export
,import
하지만 여전히 런타임 환경에 따라 사용하는 모듈이 다르기 때문에 우리는 babel과 같은 컴파일러를 사용합니다
예를 들어 노드 환경에서 import
문으로 코드를 작성 후 babel을 통해 컴파일 과정을 거치면,
해당 코드가 NodeJS가 이해할 수 있는 코드로 변환됩니다
(다만 이것은 코드를 각각의 환경이 이해할 수 있는 언어로 변환해준다는 뜻이지, 코드를 실행시켜준다는 뜻은 아닙니다)
1. 관련 패키지 설치
npm install @ babel/core @babel/cli
npm install @babel/plugin-transform-modules-commonjs
2. Babel 환경 설정
아까와 같이 .babelrc
파일을 생성합니다
{
"plugins" : ["@babel/plugin-transform-modules-commonjs"]
}
3. import문으로 코드 작성
[server.js]
import express from 'express'
// const express = require('express')
const app = express()
app.listen(3000, () => {
console.log('listening on port 3000')
})
4. Babel 실행
npx babel server.js --out-file dist/server.js
컴파일 결과 코드가 다음과 같이 변환되는 것을 확인할 수 있습니다
"use strict";
var _express = _interopRequireDefault(require("express"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const app = (0, _express.default)();
app.listen(3000, () => {
console.log('listening on port 3000');
});
터미널에서 server.js 파일을 실행시키니 console.log가 잘 찍히네요
node server
// listening on port 3000
간단한 리액트 컴포넌트 코드입니다
const React = require('react')
const ReactDOM = require('react-dom')
class App extends React.Component {
render() {
return (
<div>
<input value="hello world" />
<ul>
<li>
<a hef="#">menu</a>
</li>
</ul>
</div>
)
}
}
const root = ReactDOM.createRoot(document.getElementById("root"))
root.render(<App />)
↑ 브라우저든 NodeJS든, 자바스크립트는 꺽쇠(<
)를 읽는 순간 구문 에러를 발생시킵니다
이제 Babel을 써서 위 코드를 변환해보겠습니다
1. 관련 패키지 설치
리액트에 관한 프리셋을 설치합니다
(*프리셋은 Babel 플러그인과 설정을 미리 정의된 패키지로 묶어놓은 것입니다)
npm init -y
npm install react
npm install react-dom
npm install @babel/core @babel/cli @babel/preset-react
2. babel 환경 설정
{
"presets" : ["@babel/preset-react"]
}
3. babel 실행
npx babel app.js --out-file dist/app.js
컴파일 결과는 아래와 같습니다
const React = require('react');
const ReactDOM = require('react-dom');
class App extends React.Component {
render() {
return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("input", {
value: "hello world"
}), /*#__PURE__*/React.createElement("ul", null, /*#__PURE__*/React.createElement("li", null, /*#__PURE__*/React.createElement("a", {
hef: "#"
}, "menu"))));
}
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render( /*#__PURE__*/React.createElement(App, null));
하지만 여전히 이 코드는 실행할 수 없습니다
브라우저에는 require
함수가 존재하지 않기 때문입니다
또한 document
객체를 사용하고 있으니 NodeJS에서 실행하는 것도 불가능합니다
이에 대해서는 몇 가지 해결책이 있을 수 있겠지만,
빠른 문제 해결을 위해 Webpack
의 개념에 대해 알아보겠습니다
브라우저에서는 require
함수를 사용할 수 없고,
NodeJS에서 document
객체를 사용할 수 없는 문제를 해결하려면 별도의 모듈 시스템이 필요합니다
Webpack은 자바스크립트 애플리케이션에서 필요한 모듈들을 합쳐서 하나의 자바스크립트 파일로 만들어주는 도구입니다
(*Webpack 외에도 다양한 모듈 번들러가 존재합니다)
Webpack을 사용하면 require
함수 대신 import
문으로 모듈을 불러올 수 있고,
document
객체와 같은 브라우저 전용 기능을 사용하더라도,
target
속성을 이용하여 브라우저 환경에서 실행할 수 있는 코드로 번들링할 수 있습니다
공식문서에서는 Webpack에 대해 이렇게 설명하고 있습니다
웹 애플리케이션을 구성하는 다양한 자원들을 하나의 파일로 번들링하고,
그 결과로 웹페이지를 로딩하는데 필요한 최소한의 파일을 제공합니다
이를 통해서 로딩속도를 개선할 수 있습니다
<App />
<Comment />
<CommentList />
<CommentForm />
<CommentItem />
↓
<script src='./bundle.js'></script>
이와 같이 Webpack을 이용해서 파일을 하나로 묶으면 여러 파일의 연결로 인한 리소스 손실을 최소화할 수 있습니다
Webpack의 속성
entry
: 엔트리포인트를 지정합니다 (어느 파일을 기점으로 파일을 불러올 것인지 ...server.js)output
: 어떤 파일로 내보낼 것인지 지정합니다loaders
: 모듈을 번들링할 때, 특정한 파일들을 불러와서 처리하는 역할을 합니다plugins
: Webpack이 번들링하는 동안, 추가적인 작업을 수행할 수 있도록 도와주는 도구입니다
(예를 들어HTMLWebpackPlugin
은 HTML 파일을 생성하고, CSS 파일을 추출하는 등의 작업을 수행)
Webpack의 실행 흐름은 entry
→ loaders
→ plugins
→ output
순으로 이어집니다
entry를 통해 모듈들을 로딩하고, 로딩된 모듈들에 대해 설정된 loaders를 통해 변환 및 처리하고,
이후 plugins를 통해 추가적인 처리를 수행한 뒤, output으로 결과물을 생성합니다
1. 관련 패키지 설치
npm init -y
npm install webpack webpack-cli
2. 프로젝트 구성
3. webpack 설정 파일
webpack.config.js
라는 이름으로 파일을 생성한 뒤 해당 파일을 root 디렉토리 안에 넣습니다
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
// 절대경로로 써야 합니다
path: path.join(__dirname, 'dist')
},
}
4. 예제코드 작성
[index.js]
const React = require('react');
const ReactDOM = require('react-dom');
const home = require("./pages/home.js")
console.log(home.name)
console.log(`react :`, React)
console.log(ReactDOM)
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(React.createElement("div", null, "hello world"))
pages/home.js
module.exports = {
name: "web7722",
}
5. Webpack 실행
npx webpack
cd dist
node bundle.js
// web7722
// react : { Children : {...}}
Webpack 설정 파일에 있는 Loaders 설정 내용은 다양한 유형의 파일을 모듈화할 수 있습니다
CSS 파일도 모듈화하여 번들링할 수 있습니다
1. 관련 패키지 설치
npm init -y
npm install webpack-cli css-loader style-loader
2. 프로젝트 구성
[src/index.js]
import './index.css'
console.log("hello world")
[src/index.css]
* {
margin: 0;
padding: 0;
background: red;
}
3. webpack.config.js 설정
const path = require("path");
module.exports = {
entry: "./src/index.js",
module: {
rules: [
{
// 정규식을 사용합니다
test : /\.css$/,
use: ["style-loader", "css-loader"]
}
]
},
output: {
filename: "bundle.js",
path: path.join(__dirname, "dist"),
},
};
4. Webpack 실행
html 파일을 작성해서 bundle.js를 불러오면 css까지 함께 실행되는 것을 확인할 수 있습니다
<body>
<script src="bundle.js"></script>
</body>
1. 관련 패키지 설지
npm init -y
npm install react react-dom
npm install webpack webpack-cli html-webpack-plugin
2. 디렉토리 구성
[src/index.html]
<body>
<div id="root"></div>
</body>
[src/index.js]
import React from "react";
import ReactDOM from "react-dom";
import App from "./app.js";
const root = ReactDOM.createRoot(document.querySelector("#root"));
root.render(React.createElement(App));
[src/app.js]
import React from 'react'
class App extends React.Component {
render() {
return React.createElement("div", null, "hello world")
}
}
export default App
3. webpack.config.js 설정
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode: "development",
entry: './src/index.js',
plugins: [
new HtmlWebpackPlugin({
// = entry
template: 'src/index.html',
// = output
filename: "index.html",
})
],
output: {
filename: 'bundle.js',
path: path.join(__dirname, 'dist')
},
}
4. Webpack 실행
npx webpack
실행 결과 dist 디렉토리 안에 index.html과 bundle.js 파일이 생성됩니다
+) ↓ 사진과 같은 오류는 환경설정에서 모드 설정을 따로 하지 않았을 경우에 발생합니다. 번들링에는 문제 X
배운 내용을 정리하면서 Babel과 Webpack을 함께 사용해보겠습니다
1. 관련 패키지를 설치합니다
npm install @babel/preset-env @babel/preset-react
npm install @babel/core babel-loader
npm install react react-dom
npm install webpack webpack-cli html-webpack-plugin
npm install webpack-cli css-loader style-loader
2. 루트 디렉토리에 .babelrc
파일을 생성합니다
{
"presets" : ["@babel/preset-env", "@babel/preset-react"]
}
3. webpack.config.js를 설정합니다
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode: "development",
entry: './src/index.js',
module: {
rules: [
{
test: /\.(js|jsx|css)$/,
// 번들에서 제외할 내용을 지정합니다
exclude: /node_modules/,
// 어떤 loader를 사용할 것인지를 지정합니다
use: ["babel-loader","style-loader", "css-loader"]
}
]
},
plugins: [
new HtmlWebpackPlugin({
// = entry
template: 'src/index.html',
// = output
filename: "index.html",
})
],
output: {
filename: 'bundle.js',
path: path.join(__dirname, 'dist')
},
}
npx create-react-app project
사실 오늘 배운 내용은 개념에 대한 이해를 위한 것이지, 실제로 위 코드를 직접 타이핑해서 작성할 일은 없을지도 모릅니다
하지만 웹팩의 개념과 코드의 흐름만큼은 꼭 이해해둡시다!
개발은 노드JS 환경에서
프론트에 관한 모든 코드의 실행은 브라우저가