npm init
을 하여 package.json
을 먼저 생성합니다.
npm i react react-dom
npm i typescript
npm i @types/react @types/react-dom
npm i -D eslint
npm i -D prettier eslint-plugin-prettier eslint-config-prettier
파일 최상단에 .eslintrc
.prettierrc
tsconfig.json
파일을 생성합니다.
// .eslintrc
{
"extends": ["plugin:prettier/recommended", "react-app"]
}
// .prettierrc
{
"printWidth": 120,
"tabWidth": 2,
"singleQuote": true,
"trailingComma": "all",
"semi": true
}
// tsconfig.json
{
"compilerOptions": {
"esModuleInterop": true,
"sourceMap": true,
"lib": ["ES2020", "DOM"],
"jsx": "react",
"module": "esnext",
"moduleResolution": "Node",
"target": "es5",
"strict": true,
"resolveJsonModule": true,
"baseUrl": "."
}
}
<html>
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
/>
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>resume</title>
<link
rel="stylesheet"
href="https://a.slack-edge.com/bv1-8/client-boot-styles.57e47b5.css"
crossorigin="anonymous"
/>
<link rel="shortcut icon" href="https://a.slack-edge.com/cebaa/img/ico/favicon.ico" />
<link
href="https://a.slack-edge.com/bv1-8/slack-icons-v2-40cccfd.woff2"
rel="preload"
as="font"
crossorigin="anonymous"
/>
</head>
<body>
<div id="app"></div>
<script src="./dist/app.js"></script>
</body>
</html>
import React from 'react';
import { render } from 'react-dom';
import App from './layouts/App';
render(<App />, document.querySelector('#app'));
import React from 'react';
const App = () => {
return <div>기본 설정</div>;
};
export default App;
npm i -D webpack @babel/core babel-loader @babel/preset-env @babel/preset-react
npm i -D @types/webpack @types/node @babel/preset-typescript
npm i style-loader css-loader
npm i cross-env
npm i -D ts-node
import path from 'path';
import webpack from 'webpack';
const isDevelopment = process.env.NODE_ENV !== 'production';
const config: webpack.Configuration = {
name: 'resume', // file name
mode: isDevelopment ? 'development' : 'production',
devtool: !isDevelopment ? 'hidden-source-map' : 'eval',
resolve: {
extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'], // babel이 처리할 확장자 목록
},
entry: {
app: './client',
},
module: {
rules: [
{
test: /\.tsx?$/,
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{
targets: { browsers: ['last 2 chrome versions'] },
debug: isDevelopment,
},
],
'@babel/preset-react',
'@babel/preset-typescript',
],
},
exclude: path.join(__dirname, 'node_modules'),
},
{
test: /\.css?$/,
use: ['style-loader', 'css-loader'], // js로 변환
},
],
},
plugins: [new webpack.EnvironmentPlugin({ NODE_ENV: isDevelopment ? 'development' : 'production' })],
output: {
path: path.join(__dirname, 'dist'),
filename: '[name].js',
publicPath: '/dist/',
},
};
export default config;
{
"compilerOptions": {
"module": "commonjs",
"moduleResolution": "Node",
"target": "es5",
"esModuleInterop": true
}
}
scripts
에 build
를 추가해줍니다.
{
"name": "resume",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "cross-env NODE_ENV=production TS_NODE_PROJECT=\"tsconfig-for-webpack-config.json\" webpack",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"@types/react": "^17.0.43",
"@types/react-dom": "^17.0.14",
"cross-env": "^7.0.3",
"css-loader": "^6.7.1",
"path": "^0.12.7",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"style-loader": "^3.3.1",
"typescript": "^4.6.3",
"webpack-bundle-analyzer": "^4.5.0"
},
"devDependencies": {
"@babel/core": "^7.17.8",
"@babel/preset-env": "^7.16.11",
"@babel/preset-react": "^7.16.7",
"@babel/preset-typescript": "^7.16.7",
"@types/node": "^17.0.23",
"@types/webpack": "^5.28.0",
"babel-loader": "^8.2.4",
"eslint": "^8.12.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.0.0",
"fork-ts-checker-webpack-plugin": "^7.2.1",
"prettier": "^2.6.1",
"ts-node": "^10.7.0",
"webpack": "^5.70.0",
"webpack-cli": "^4.9.2"
}
}
npm run build
build 를 하면 dist 폴더 안에 app.js 파일이 생성됩니다.
여기까지 했을 때, 단점은 핫리로딩 적용이 안 되어있어서, 매번 수정할 사항들이 바로 적용이 안되는 단점이 있습니다. 이제 핫 리로딩을 적용시켜 볼 것입니다.
npm i webpack-dev-server -D
npm i webpack-cli
npm i -D @types/webpack-dev-server
npm i @pmmmwh/react-refresh-webpack-plugin
npm i react-refresh
webpack-dev-server를 설정하여 핫리로딩을 추가해주었습니다.
npm run dev
로 실행합니다.
import path from 'path';
import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin';
import webpack, { Configuration as WebpackConfiguration } from 'webpack';
import { Configuration as WebpackDevServerConfiguration } from 'webpack-dev-server';
interface Configuration extends WebpackConfiguration {
devServer?: WebpackDevServerConfiguration;
}
import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin';
const isDevelopment = process.env.NODE_ENV !== 'production';
const config: Configuration = {
name: 'resume',
mode: isDevelopment ? 'development' : 'production',
devtool: isDevelopment ? 'hidden-source-map' : 'inline-source-map',
resolve: {
extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],
},
entry: {
app: './client',
},
module: {
rules: [
{
test: /\.tsx?$/,
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{
targets: { browsers: ['IE 10'] },
debug: isDevelopment,
},
],
'@babel/preset-react',
'@babel/preset-typescript',
],
env: {
development: {
plugins: [require.resolve('react-refresh/babel')],
},
},
},
exclude: path.join(__dirname, 'node_modules'),
},
{
test: /\.css?$/,
use: ['style-loader', 'css-loader'],
},
],
},
plugins: [
new ForkTsCheckerWebpackPlugin({
async: false,
// eslint: {
// files: "./src/**/*",
// },
}),
new webpack.EnvironmentPlugin({ NODE_ENV: isDevelopment ? 'development' : 'production' }),
],
output: {
path: path.join(__dirname, 'dist'),
filename: '[name].js',
publicPath: '/dist/',
},
devServer: {
historyApiFallback: true,
port: 3090,
devMiddleware: { publicPath: '/dist/' },
static: { directory: path.resolve(__dirname) },
},
};
if (isDevelopment && config.plugins) {
config.plugins.push(new webpack.HotModuleReplacementPlugin());
config.plugins.push(
new ReactRefreshWebpackPlugin({
overlay: {
useURLPolyfill: true,
},
}),
);
}
if (!isDevelopment && config.plugins) {
}
export default config;
{
"name": "resume",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "cross-env TS_NODE_PROJECT=\\\"tsconfig-for-webpack-config.json\\\" webpack serve --env development",
"build": "cross-env TS_NODE_PROJECT=\\\"tsconfig-for-webpack-config.json\\\" NODE_ENV=production webpack"
},
"author": "ZeroCho",
"license": "MIT",
"dependencies": {
"@types/react": "^17.0.2",
"@types/react-dom": "^17.0.1",
"axios": "^0.21.1",
"core-js": "^3.15.1",
"cross-env": "^7.0.3",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-router": "^5.2.0",
"react-router-dom": "^5.2.0",
"typescript": "^4.4.2"
},
"devDependencies": {
"@babel/core": "^7.13.8",
"@babel/preset-env": "^7.13.8",
"@babel/preset-react": "^7.12.13",
"@babel/preset-typescript": "^7.13.0",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.0-rc.0",
"@types/fork-ts-checker-webpack-plugin": "^0.4.5",
"@types/node": "^14.14.31",
"@types/react-router-dom": "^5.1.7",
"@types/webpack": "^5.28.0",
"@types/webpack-dev-server": "^4.0.3",
"babel-loader": "^8.2.2",
"css-loader": "^6.2.0",
"eslint": "^7.20.0",
"eslint-config-prettier": "^8.1.0",
"eslint-plugin-prettier": "^3.3.1",
"fork-ts-checker-webpack-plugin": "^6.1.0",
"prettier": "^2.2.1",
"react-refresh": "^0.10.0",
"style-loader": "^3.2.1",
"ts-node": "^10.0.0",
"webpack": "^5.24.2",
"webpack-cli": "^4.5.0",
"webpack-dev-server": "^4.0.0"
}
}