최근 구직활동을 시작하면서 사전과제를 받게 되었다.
나의 포지션은 프론트엔드 개발자로 react 라이브러리를 사용하고 있기때문에 react로 자사 사이트 클론 코딩하고 api 요청에 대한 응답결과를 구현하는 과제였다.
포트폴리오를 만들었을때는 CRA를 사용해서 리액트 프로젝트를 만들었었다. create-react-app 한줄만 입력하면 손쉽게 리액트 프로젝트를 만들 수 있었기 때문이다.
생각해보니 프론트엔드 자격요건이나 우대사항에 webpack & babel에 대한 이해나 지식을 요구하는 경우를 종종 보았는데 직접 설정해서 사용해본적은 인강으로 react를 배웠을때 정도였던 것 같다. 문득 직접 프로젝트를 설정할 수 있지만 CRA를 사용하는 것과 모르고 사용하는 것의 차이는 매우 크다는 생각이 들었다.
그래서 마침 사전과제도 요청받았겠다 과제도 하고 내 공부도 할 겸 (꿩먹고 알먹고😋) CRA없이 react 프로젝트를 만들고 프로젝트 환경을 설정해 보기로 했다.
설정을 시작하기 전에 바벨과 웹팩에 대해 간단하게 알아보자.
웹팩은 자바스크립트 애플리케이션을 위해 여러개의 파일을 하나로 묶어주는 모듈 번들러이다.
웹팩은 여러 파일을 하나의 파일로 합치는 역할을 하며, 각 모듈간의 의존성을 분석하여 그룹화시켜준다.
(모듈을 하나의 파일이라고 생각하고 각 파일들을 하나로 합쳐준다고 생각하면 쉽다.)
프로젝트에 사용되는 많은 라이브러리들을 빌드과정을 통해 html이 실행할 수 있는 하나의 파일로 합쳐준다.
js, jsx뿐만 아니라 css, 폰트, 이미지 등 다양한 파일을 번들링 할 수 있다.
*웹팩이 필요한 이유
바벨은 ES6+ 버전 이상의 자바스크립트 코드를 하위 버전의 자바스크립트 문법으로 변환 시켜준다.
바벨 웹사이트를 방문하면 Babel is a Javascript compiler
이란 문구가 보인다.
문구 그대로 바벨은 자바스크립트 컴파일러다. 자바스크립트의 특정 문법, 타입스크립트, JSX 등의 문법은 웹 브라우저에서 지원하지 않는 경우가 많은데 호환 가능한 ES5이하의 문법으로 변환 해준다.
*바벨이 필요한 이유
// package.json 생성
yarn init -y
yarn add react react-dom
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
웹팩으로 번들링 한 후의 파일에 적용할 플러그인을 설치한다.
yarn add -D html-webpack-plugin clean-webpack-plugin
프로젝트 루트에 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,
},
};
// package.json
"scripts": {
"start": "webpack-dev-server --progress --mode development",
"build": "webpack --progress --mode production"
}
루트에 public 폴더를 만들고 index.html 파일을 만든다.
<!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;
yarn start
로 프로젝트 실행하면 서버가 실행된다.
yarn build
명령어를 실행하면 dist 파일에 번들링 된 결과물인 bundle.js와 index.html이 생성된다.
처음에는 babel-loader와 css-loader style-loader만 설치해서 설정했었다. 근데 코딩하면서 import로 이미지를 불러오는 것도 json을 불러오는 것도 전부 설정이 필요하다는 것을 알게 되었다.
그동안 CRA가 한번에 설치해주는게 얼마나 편한지 알게되었고, webpack과 babel이 어렵다는 막연한 두려움을 해소해주는 계기가 되었다. 😉
(나중에 scss-loader도 웹팩에 추가해서 사용해봐야지...)
⚙ prettier나 es-lint까지 설정하면 개발 효율성이 더욱 높아질 것 같다.