$ mkdir webpack-dev-server-exam && cd webpack-dev-server-exam
$ npm init -y
- file structure
webpack-dev-server-exam/
├── public/
│ └── index.html
├── src/
│ └── js/
│ ├── counter.js
│ ├── redux-counter.js
│ └── request.js
├── package.json
└── webpack.config.js
- src/counter.js
const $count = document.querySelector('.count');
document.querySelector('.increase').onclick = () => {
$count.textContent = +$count.textContent + 1;
};
document.querySelector('.decrease').onclick = () => {
$count.textContent = +$count.textContent - 1;
};
- public/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Webpack Dev Server Exam</title>
</head>
<body>
<!-- webpack-dev-server가 정상 가동하면 카운터가 동작한다.-->
<div class="count">0</div>
<button class="increase">+</button>
<button class="decrease">-</button>
<script src="./bundle.js"></script>
</body>
</html>
$ npm install --save-dev webpack webpack-cli webpack-dev-server
package.json
...
"scripts": {
"start": "webpack-dev-server --progress --open"
},
...
$ npm install --save-dev @babel/core @babel/cli @babel/preset-env @babel/plugin-proposal-class-properties babel-loader
- webpack.config.js
const path = require('path');
module.exports = {
entry: './src/js/counter.js',
// entry: './src/js/redux-counter.js',
output: {
path: path.resolve(__dirname, 'public/js'),
filename: 'bundle.js',
},
module: {
rules: [
{
test: /\.js$/,
include: [path.resolve(__dirname, 'src/js')],
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
plugins: ['@babel/plugin-proposal-class-properties'],
},
},
},
],
},
devServer: {
contentBase: path.join(__dirname, 'public'),
compress: true,
port: 9000,
},
};
$ npm start
$ npm install redux
- src/redux-counter.js
console.log('[redux-counter]');
import { createStore } from 'redux';
const reducer = (state = { count: 0 }, action) => {
switch (action.type) {
case 'INCREASE':
return { ...state, count: state.count + 1 };
case 'DECREASE':
return { ...state, count: state.count - 1 };
default:
return state;
}
};
const store = createStore(reducer);
store.subscribe(() => {
const { count } = store.getState();
document.querySelector('.count').textContent = count;
});
document.querySelector('.increase').onclick = () => {
store.dispatch({ type: 'INCREASE' });
};
document.querySelector('.decrease').onclick = () => {
store.dispatch({ type: 'DECREASE' });
};
- webpack.config.js
const path = require('path');
module.exports = {
// entry: './src/js/counter.js',
entry: './src/js/redux-counter.js',
...
$ npm start
- src/request.js
const getTodos = async () => {
const res = await fetch('/todos');
const todos = await res.json();
console.log(todos);
};
getTodos();
- async/await를 위해 폴리필 적용
$ npm install @babel/polyfill
- webpack.config.js
const path = require('path');
// for devServer.before
let todos = [
{ id: 3, content: 'Javascript', completed: false },
{ id: 2, content: 'CSS', completed: true },
{ id: 1, content: 'HTML', completed: false },
];
module.exports = {
// entry: './src/js/counter.js',
// entry: './src/js/redux-counter.js',
entry: ['@babel/polyfill', './src/js/request.js'],
output: {
path: path.resolve(__dirname, 'public/js'),
filename: 'bundle.js',
},
module: {
rules: [
{
test: /\.js$/,
include: [path.resolve(__dirname, 'src/js')],
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
plugins: ['@babel/plugin-proposal-class-properties'],
},
},
},
],
},
// https://webpack.js.org/configuration/dev-server
devServer: {
contentBase: path.join(__dirname, 'public'),
publicPath: '/',
stats: 'errors-only',
overlay: true,
compress: true,
port: 9000,
before: (app, server, compiler) => {
const bodyParser = require('body-parser');
app.use(bodyParser.json());
// GET /todos
app.get('/todos', (req, res) => {
res.json(todos);
});
// GET /todos/:id
app.get('/todos/:id', (req, res) => {
const { id } = req.params;
todos = todos.filter(todo => todo.id === +id);
res.json(todos);
});
// POST /todos { id, content, compelted }
app.post('/todos', (req, res) => {
const newTodo = req.body;
todos = [newTodo, ...todos];
res.json(todos);
});
// PATCH /todos/:id { compelted }
app.patch('/todos/:id', (req, res) => {
const { id } = req.params;
const { completed } = req.body;
todos = todos.map(todo =>
todo.id === +id ? { ...todo, completed } : todo
);
res.json(todos);
});
// PATCH /todos { compelted }
app.patch('/todos', (req, res) => {
const { completed } = req.body;
todos = todos.map(todo => ({ ...todo, completed }));
res.json(todos);
});
// DELETE /todos/:id
app.delete('/todos/:id', (req, res) => {
const { id } = req.params;
todos = todos.filter(todo => todo.id !== +id);
res.json(todos);
});
},
},
};
$ npm start
$ cd ~/Desktop
$ git clone https://github.com/ungmo2/webpack-dev-server-exam.git
$ cd webpack-dev-server-exam
$ npm install
$ npm start