node 프로젝트를 생성(npm init)하고 기본적인 라이브러리들을 설치한다.
npm i react @types/react
npm i react-dom @types/react-dom
npm i typescript
npm i -D prettier eslint-plugin-prettier eslint-config-prettier
ESLint와 prettier도 설치해주면 좋다. 사실 VSCode를 쓴다면 extensions만 설치해도 에디터가 다 잡아주기 때문에 node 모듈까지 굳이 설치할 필요는 없는 것 같다. 착각하기 쉬운데, 저장을 눌렀을 때 코드가 자동으로 정렬되고, 잘못된 코드에 밑줄이 그어지는 건 npm으로 설치하는 eslint와 prettier가 하는 게 아니다. 그건 vscode가 지원하는 기능이다.
그럼 eslint와 prettier를 npm으로 설치하는 이유는 뭘까? vscode의 prettier extension 공식 문서에 이렇게 써있다.
Should prettier not be installed locally with your project's dependencies or globally on the machine, the version of prettier that is bundled with the extension will be used.
즉 extenstion 역시 설치한 prettier를 사용하기도 한다는 것. 그게 아니더라도 코드 파일이 많은 경우 에디터로 하나하나 확인하기에는 번거롭기 때문도 있다.
이제 tsconfig와 eslint, prettier 설정 파일을 작성해주자.
- .eslintrc
{
"extends": ["plugin:prettier/recommended"]
}
- .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": ".",
"paths": {
"@hooks/*": ["hooks/*"],
"@components/*": ["components/*"],
"@layouts/*": ["layouts/*"],
"@pages/*": ["pages/*"],
"@utils/*": ["utils/*"],
"@typings/*": ["typings/*"]
}
}
}
각 설정 파일들은 vscode와 node 라이브러리가 "본다." 설정 파일을 명시하지 않으면 vscode에서 생각하는 설정과 node에서 생각하는 설정이 달라질 수 있으니 작성해주는 게 좋다.
이제 webpack과 babel을 설정해주자. 필요한 라이브러리를 설치해주고 설정 파일을 작성한다.
npm i -D webpack
npm i -D @babel/core babel-loader @babel/preset-env @babel/preset-react
npm i -D @types/webpack @types/node @babel/preset-typescript
npm i -D style-loader css-loader
npm i cross-env ts-node webpack-cli
npm i -D react-refresh
npm i @emotion/babel-plugin
- webpack.config.ts
import path from 'path';
import webpack from 'webpack';
const isDevelopment = process.env.NODE_ENV !== 'production';
const config: webpack.Configuration = {
name: 'sosuling',
mode: isDevelopment ? 'development' : 'production',
devtool: !isDevelopment ? 'hidden-source-map' : 'eval',
resolve: {
extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],
alias: {
'@hooks': path.resolve(__dirname, 'hooks'),
'@components': path.resolve(__dirname, 'components'),
'@layouts': path.resolve(__dirname, 'layouts'),
'@pages': path.resolve(__dirname, 'pages'),
'@utils': path.resolve(__dirname, 'utils'),
'@typings': path.resolve(__dirname, 'typings'),
},
},
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',
],
env: {
development: {
plugins: [require.resolve('react-refresh/babel')],
},
},
},
exclude: path.join(__dirname, 'node_modules'),
},
{
test: /\.css?$/,
use: ['style-loader', 'css-loader'],
},
],
},
plugins: [
new webpack.EnvironmentPlugin({ NODE_ENV: isDevelopment ? 'development' : 'production' }),
],
output: {
path: path.join(__dirname, 'dist'),
filename: '[name].js',
publicPath: '/dist/',
},
};
export default config;
- tsconfig-for-webpack-config.json
{
"compilerOptions": {
"module": "commonjs",
"moduleResolution": "Node",
"target": "es5",
"esModuleInterop": true
}
}
- package.json에 스크립트 추가
"build": "cross-env TS_NODE_PROJECT=\"tsconfig-for-webpack-config.json\" NODE_ENV=production webpack"
이제 npm run build로 작성한 코드가 제대로 변환이 되는지 확인해보자.
다음으로 webpack dev server를 설정하자. 필요한 라이브러리를 설치한다.
npm i -D webpack-dev-server @types/webpack-dev-server
npm i @pmmmwh/react-refresh-webpack-plugin
성능을 위한 플러그인 추가
npm i -D fork-ts-checker-webpack-plugin
- webpack.config.ts 수정
import path from 'path';
import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin';
import webpack, { Configuration as WebpackConfiguration } from 'webpack';
import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin';
// import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
import { Configuration as WebpackDevServerConfiguration } from 'webpack-dev-server';
interface Configuration extends WebpackConfiguration {
devServer?: WebpackDevServerConfiguration;
}
const isDevelopment = process.env.NODE_ENV !== 'production';
const config: Configuration = {
name: 'sosuling',
mode: isDevelopment ? 'development' : 'production',
devtool: !isDevelopment ? 'hidden-source-map' : 'eval',
resolve: {
extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],
alias: {
'@hooks': path.resolve(__dirname, 'hooks'),
'@components': path.resolve(__dirname, 'components'),
'@layouts': path.resolve(__dirname, 'layouts'),
'@pages': path.resolve(__dirname, 'pages'),
'@utils': path.resolve(__dirname, 'utils'),
'@typings': path.resolve(__dirname, 'typings'),
},
},
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',
],
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, // react router
port: 3090,
devMiddleware: { publicPath: '/dist/' },
static: { directory: path.resolve(__dirname) },
// proxy: {
// '/api/': {
// target: 'http://localhost:3095',
// changeOrigin: true,
// },
// },
},
};
if (isDevelopment && config.plugins) {
config.plugins.push(new webpack.HotModuleReplacementPlugin());
config.plugins.push(new ReactRefreshWebpackPlugin());
// config.plugins.push(new BundleAnalyzerPlugin({ analyzerMode: 'server', openAnalyzer: true }));
}
if (!isDevelopment && config.plugins) {
// config.plugins.push(new webpack.LoaderOptionsPlugin({ minimize: true }));
// config.plugins.push(new BundleAnalyzerPlugin({ analyzerMode: 'static' }));
}
export default config;
- package.json에 스크립트 추가
"dev": "cross-env TS_NODE_PROJECT=\"tsconfig-for-webpack-config.json\" webpack serve --env development"
이후 npm run dev로 서버가 실행되는지 확인하자.
'React' 카테고리의 다른 글
[React] 왜 key는 list에서만 필요할까? 왜 index를 key로 사용하면 안되는 걸까? (0) | 2022.05.01 |
---|---|
[React] React.FC, React.VFC를 쓰면 안되는 이유 (0) | 2022.04.04 |
[React] 함수형 컴포넌트에서 defaultProps와 default parameters 중 무엇을 사용해야 할까? (0) | 2022.04.03 |
[React] props에 객체를 전달하면: pass by reference (0) | 2022.02.24 |
[React] 리액트 디자인 패턴에 대한 고찰 (0) | 2022.02.22 |