우리가 개발한 서비스는 다양한 브라우저에서 제공되어야한다. 즉, 크로스 브라우징이 이루어져야 한다는 뜻이며 단지 서비스의 제공뿐만 아니라 호환성, 효율성 등의 최적화 문제도 해결되어야한다.
따라서, 다양한 환경을 고려해야 하는데 이때 바벨을 사용하게 된다. 새로운 문법이나 타입스크립트 혹은 JSX 같이 다른 언어로 분류되는 언어들에 대해서도 모든 브라우저에서 동작할 수 있도록 호환성을 지켜주는 것이다.
👍 쉽게 말해, Babel을 통해 이러한 언어 기술들을 모든 브라우저에서 작동되는 하위버전(ES5)의 자바스크립트 언어로 변환해주는 것이다. 바벨이란 자바스크립트 컴파일러이다.
💡 중요) 크로스 브라우징
최대한 많은 종류의 웹 브라우저에서 정상적으로 작동하는 웹페이지를 만드는 방법론 중 하나.
HTML, CSS, Javascript 작성 시 W3C의 웹 규격에 맞는 코딩을 함으로써 어느 브라우저, 기기에서 사이트가 의도된 대로 보여지고 작동되는 기법.
크로스 브라우징 작업 필요 이유
: 브라우저 마다 랜더링 엔진이 다르기 때문
- 작동되지 않는 HTML5, Javascript 코드가 존재
- 해석하지 못하는 CSS 코드 존재
- 브라우저 버그들이 존재
- 브라우저마다 자체적인 CSS 스타일
💡 잠깐) 컴파일러
컴파일러란 인간 수준의 고언어로 작성된 프로그램을 기계어로 된 프로그램으로 출력하는 번역기이다. 컴파일러는ㄴ 전체를 한번 훑고 컴퓨터가 이해할 수 있는 기계어로 번역한다. 일반적으로 컴파일러가 인터프리터보다 실행이 빠르다.
자세한 설명: 컴파일러 vs 인터프리터
바벨은 3단계로 빌드를 진행한다.
코드를 읽고 추상 구문 트리로 변환하는 단계
추상 구문 트리를 변경하는 단계
💡 잠깐) 추상 구문 트리(Abstract Systax Tree)
프로그램 내에서 발생하는 기능을 나타내기 위해 만들어진 구문 구조이다. 이는 고수준의 언어를 기계어로 변환하는 과정에서 꼭 필요하고 코드를 트리 구조의 데이터 스트럭쳐로 만들어낸다. 이렇게 구조화시키면 프로그래머도, 컴퓨터도, 훨씬 파악하기 쉬워진다.
변경된 결과물을 출력하는 단계
babel은 단순히 문법만 변환해주는 역할만할 뿐이다. 바벨을 사용한다고 최신 함수를 사용할 수 있는 것이 아니고 최신함수를 사용하기위해서는 object의 prototype에 붙여주는 역할이 필요한데 이를 polyfill이 해주는 것이다.
polyfill은 스크립트에 사용자가 원하는 최신 함수를 추가한다. 자바스크립트는 동적인 언어이기 때문에 원하면 어떤 함수도 스크립트에 추가가 가능하다. 바로 이 역할을 polyfill이 해주는 것이다.
polyfill은 개발자가 특정 기능이 지원되지 않는 브라우저를 위해 사용할 수 있는 코드조각이나 플러그인을 의미한다.(브라우저에서 지원하지 않는 기능들에 대한 호환성 작업을 채워넣는다고해서 polyfill)
💡 중요) babel은 컴파일-타임에 실행되고, bablel-polyfill 은 런-타임에 실행.
$ mkdir babel-practice
$ cd babel-practice
$ yarn init -y
[1, 2, 3].map(n => n + 1);
프로젝트에서 @babel/core와 @babel/cli 패키지를 개발 의존성(devDependencies)으로 설치.
yarn add -D @babel/core @babel/cli
💡 잠깐) 개발 의존성으로 설치하는 이유
바벨이 애플리케이션이 실행될 때 필요한 것이 아니라 빌드할 때만 필요하기 때문.
- @babel/core: 바벨을 사용하는데 필요한 패키지
- @babel/cli: 터미널에서 커맨드를 입력해서 바벨을 사용하기 위해 필요한 패키지
기존의 코드에서 변화가 없다. 왜냐하면 바벨에게 코드를 어떻게 변환해야 하는지 규칙을 알려주지 않았기 때문이다.
$ npx babel test.js
[1, 2, 3].map(n => {
n + 1;
});
바벨에서 플러그인(plugin)이나 프리셋(preset)을 통해 문법 변환 규칙을 알려주기 가능
넓게 이용되는 프리셋인 env를 사용
yarn add -D @babel/preset-env
바벨에게 코드 변환의 규칙으로 env를 알려주기
$ npx babel test.js --presets=@babel/env
"use strict";
[1, 2, 3].map(function (n) {
n + 1;
});
매번 바벨에게 변환될 규칙을 알려주는 것은 매우 비효율적이다. 따라서 바벨 설정파일인 .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;
});
웹사이트를 구성할때 .js .css .images 파일등 수 많은 들이 모여 웹사이트를 구성하게 된다. 따라서 웹사이트에 접속했을때 굉장히 많은 파일이 다운로드될 수 있는데 이것에 비례하여 서버의 자원을 소모하고 웹사이트가 느리게 로딩이 된다.
👍 이러한 현상을 해결하기 위해 나온 '묶는다'는 개념의 번들러가 등장하였고 Weppack, Broserify, Parcel 과 같은 도구들이 번들러에 속한다. 그 중에서도 가장 인기 있는 번들러가 웹팩이다.
번들링을 함으로써 파일은 하나로 합쳐지고 네트워킹 요청횟수는 줄어들고 중복된 소스코드도 최소화
💡 잠깐) Bundle?
- 소프트웨어 및 일부 하드웨어와 함께 작동하는 데 필요한 모든 것을 포함하는 Package
- 각각의 모듈들에 대해 의존성 관계를 파악하여 하나 또는 여러개의 그룹
모듈 번들링에서는 빌드 = 번들링 = 변환
모던 JavaScript 애플리케이션을 위한 정적 모듈 번들러
모듈 번들러란 웹 애플리케이션을 구성하는 자원(HTML, CSS, Javscript, Images 등)을 모두 각각의 모듈로 보고 이를 조합해서 병합된 하나의 결과물을 만드는 도구를 의미
💡 중요) 즉, Webpack은 여러 파일을 하나 이상의 파일로 합쳐주는 자바스크립트 번들러
💡 중요) 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 기능을 이용하여 원하는 모듈을 원하는 타이밍에 로딩할 수 있다
💡 잠깐) Webpack vs Babel
- Webpack : JavaScript의 static module bundler
- Babel : JavaScript Compiler (Transpiler)
Webpack은 의존성을 분석해서 모듈을 번들시켜주는 역할이다. 프로젝트를 개발하고 빌드라는 과정을 통해 하나의 파일로 만들어준다.
Babel은 코드 변환기이다. 다양한 브라우저들에서 서비스를 실행하기 위하여 최신버전의 자바스크립트 문법을 구 버전 문법으로 변환해준다.
✏️ 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라는 객체로 속성을 지정.
module.exports = {
module: {
rules: [
{ test: /\.css$/, use: 'css-loader' },
{ test: /\.ts$/, use: 'ts-loader' },
// ...
]
}
}
✏️ Plugin
웹팩의 기본적인 동작에 추가적인 기능을 제공하는 속성이다.
로더랑 역할을 비교해보자면 로더는 파일을 해석하고 변환하는 과정에 관여하며, 플러그인은 해당 결과물의 형태를 바꾸는 역할을 한다고 볼 수 있다.
// webpack.config.js
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
plugins: [
new HtmlWebpackPlugin(),
new webpack.ProgressPlugin()
]
}
✏️ 성능 최적화 & 자동화
✏️ 번들러가 제공하는 편의성
✏️ Code splitting을 통해 “Load what you need, when you need (필요할 때만 필요한 스크립트를 로드)” 옵션
React 앱을 개발할 때 개발환경을 쉽게 구축하기 위해 Creat React App을 사용한다.
✏️ 1. Less to Learn 자동 최적화
✏️ 2. Only One Dependency
✏️ 3. No Lock-In
💡 잠깐) CRA 자동 세팅 사항
- webpack(모듈+라이브러리 번들링)
- babel(jsx 컴파일)
- jest(테스트)
- eslint(코드형상관리)
- polyfill(구형 브라우저 미지원 문법 등 대응)
- HMR(Hot Module Replacement: 재시작 없이 변경사항 반영)
- CSS 후처리(SASS 컴파일, 벤더접두사prefix 등)
✏️ node.modules
✏️ package.json
✏️ gitignore
✏️ Public
✏️ Src
✏️ npm start
HTTPS 구동 방법
- mac : HTTPS=true npm start
- window : set HTTPS=true && npm start
✏️ npm build
✏️ npm test
✏️ npm eject
지금까지는 나는 React 앱을 개발할 때 항상 쉬운 개발환경 구축을 위하여 CRA를 이용하여 프로젝트를 생성하였다. 이는 앞서 공부한 Babel과 Webpack의 기능을 포함하기 때문에 프로젝트에 Babel, Webpack을 사용해볼 기회가 없었다.
👉 따라서, CRA 없이 개발환경을 구축해보자.
가장 기본적인 라이브러리, 모듈을 설치한다.
// package.json 생성
yarn init -y
yarn add react react-dom
앞서 작성한 Babel 작성 방법을 참고하자.
yarn add -D @babel/core @babel/preset-react @babel/preset-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으로 번들링 한 후의 파일에 적용할 플러그인을 설치
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 설정
// 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로 프로젝트 실행하면 서버가 실행
참조 및 참고하기 좋은 사이트
- https://tecoble.techcourse.co.kr/post/2021-07-07-babel/
- https://okayoon.tistory.com/entry/%ED%81%AC%EB%A1%9C%EC%8A%A4-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A7%95cross-browsing
- https://nyagm.tistory.com/52
- https://curryyou.tistory.com/485
- https://velog.io/@gkj8963/cra%EB%9E%80-Create-React-App
- https://codermun-log.tistory.com/m/436
- https://nesoy.github.io/articles/2019-02/Webpack
- https://www.hanl.tech/blog/webpack-%ec%82%ac%ec%9a%a9%eb%b0%a9%eb%b2%95%ea%b3%bc-%ea%b8%b0%eb%8a%a5-%ec%9a%94%ec%a0%90-%ec%a0%95%eb%a6%ac/
- https://nesoy.github.io/articles/2019-02/Webpack
- https://devlog.jwgo.kr/2018/12/03/webpack-babel-react/
- https://hianna.tistory.com/665
- https://phrygia.github.io/react/2022-02-18-react/