들어가기전
React와 Typescript를 사용하여 프로젝트를 하기전, 제로초님의 sleact 강의를 듣고 내가 생각보다 기초가 많이 부족하다는 것을 알게 되었다. 그래서 CRA를 사용하지 않고 프로젝트를 생성하는 방법과 그 과정 속에서 내가 알게 된 것들을 정리하고자 한다.
npm init
npm
이란 Node Package Manager의 약자로, 개발자들이 자바스크립트 패키지를 쉽게 설치, 업데이트, 삭제할 수 있도록 도와주는 관리 프로그램이다.npm init
은 npm을 쓸수 있는 초기 환경을 설정한다. 즉 package.json파일을 생성한다.{
"name": "",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "webpack serve --env development",
"build": "cross-env NODE_ENV=production webpack"
},
"author": "",
"license": "MIT",
"dependencies": {
"@types/react": "^17.0.2",
"@types/react-dom": "^17.0.1",
"axios": "^0.26.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": "^16.11.26",
"@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": "^8.13.0",
"eslint-config-prettier": "^8.1.0",
"eslint-plugin-prettier": "^4.0.0",
"fork-ts-checker-webpack-plugin": "^7.2.3",
"prettier": "^2.2.1",
"react-refresh": "^0.12.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"
}
}
npm install
로 라이브러리들을 다운받는다.eslint
: 코드 검사도구로 안쓰는 변수나 오타를 잡아준다.prettier
: 코드를 정렬하는 도구이다.eslint-config-pretteir
로 eslint의 원래 포매팅 기능을 없애버리고 eslint-plugin-prettier
로 prettier의 포매팅 기능을 사용한다.// .eslintrc
{
"extends": ["plugin:prettier/recommended"]
// prettier가 추천하는 대로 코드 모양을 바꾸겠다라는 뜻
}
// .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": {
// import할 때 편하게 import하기 위함 ../../../ 같은것을 @hooks/...
"@hooks/*": ["hooks/*"],
"@components/*": ["components/*"],
"@layouts/*": ["layouts/*"],
"@pages/*": ["pages/*"],
"@utils/*": ["utils/*"],
"@typings/*": ["typings/*"]
}
},
"ts-node": {
"compilerOptions": {
"module": "commonjs",
"moduleResolution": "Node",
"target": "es5",
"esModuleInterop": true
}
}
}
tsconfig.json
파일은 TS파일들을 JS파일로 변환할 때 어떻게 변환할 것인지 설정하는 파일이다.esModuleInterop
: import * as React from 'react' 대신 import React from 'react'을 가능하게 한다."sourceMap": true
: 에러났을 때 에러난 곳으로 이동하는 옵션이다."lib": ["ES2020", "DOM"]
: lib은 library약자로 프론트개발할때는 ES2020과 DOM을 적어야한다."jsx": "react"
: jsx는 다른 프로그래밍에서도 쓰이기 때문에, 다르게 인식될 수 있다. 이 jsx는 react것이라고 명확히 지정하는 것을 의미한다."module": "esnext"
: 최신모듈(import,export)을 쓰겠다는 것, 이 옵션을 안쓰면 node의 모듈 시스템인 commonjs 같은 것을 쓰겠다는 것"target": "es5"
: ES2020으로 작성하더라도 ES5로 바꾸겠다는 의미"strict": true
: type checking을 엄격하게 하겠다는 의미"resolveJsonModule": true
: json 파일을 import하는 것을 허락하겠다는 의미.TS를 JS로 바꾸는 방법 크게 2가지
- TS가 처음부터 끝까지 다 바꾸는 것,
- TS가 한 번 바꿔주고 그것을 Babel이 이어받아서 바꾸는 것
// Babel을 쓰는 이유: HTML, CSS, JS, 이미지 파일 등이 있을 때는 바벨을 쓴다, Babel은 모든 것을 JS로 만들 수 있다.
// Babel은 단독으로 사용하기보단 Webpack Plugin으로 사용한다.
Babel
은 대표적인 트랜스파일러다. 모든 브라우저가 ES6의 기능(최신기능)을 제공하지 않기 때문에 ES5코드(구기능)으로 변환시키는 과정이 필요하다. 이 과정을 트랜스파일링이라고 한다. Webpack
은 여러개 파일을 하나로 합쳐주는 모듈 번들러이다. 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';
import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin'; // ts 검사와 webpack 실행은 원래 순서대로 되는데 동시에 될 수 있도록 하는 라이브러리(성능이 좋아진다)
interface Configuration extends WebpackConfiguration {
devServer?: WebpackDevServerConfiguration;
}
const isDevelopment = process.env.NODE_ENV !== 'production';
const config: Configuration = {
name: 'sleact',
mode: isDevelopment ? 'development' : 'production',
devtool: !isDevelopment ? 'hidden-source-map' : 'eval',
resolve: {
extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'], // babel이 처리할 확장자 목록
alias: {
// ../../../ 없애는 것 -> tsconfig.json에서도 설정해줘야하고, webpack.config.ts에서도 설정해줘야함. 소스코드를 ts로 치는 동안 ts검사기(소스코드 제대로 치고 있나 검사해주는 애)는 tsconfig.json을 보고 검사를 하고, 실제로 웹팩은 webpack.config.ts를 보고 바꾸기 때문에
'@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: {
// tsx나 ts파일을 babel-loader가 js로 바꿔주는데 바꿔줄때 babel에 대한 설정
presets: [
[
'@babel/preset-env',
{
targets: { browsers: ['last 2 chrome versions'] }, // js로 바꿀 때 targets에 적은 브라우저를 지원하게끔 알아서 바꿔주겠다.(최신 크롬버전 2개) 따라서 env라는 preset이 유용하다
debug: isDevelopment,
},
],
'@babel/preset-react',
'@babel/preset-typescript',
],
env: {
development: {
plugins: [require.resolve('react-refresh/babel')],
},
}, // 핫리로딩 설정
},
exclude: path.join(__dirname, 'node_modules'),
},
{
// babel이 css파일도 js로 바꿔주는데 그것을 바로 css-loader와 style-loader가 해줌
test: /\.css?$/,
use: ['style-loader', 'css-loader'],
},
],
},
plugins: [
new ForkTsCheckerWebpackPlugin({
async: false,
// eslint: {
// files: "./src/**/*",
// },
}),
new webpack.EnvironmentPlugin({ NODE_ENV: isDevelopment ? 'development' : 'production' }),
// EnvironmentPlugin은 react에서 NODE_ENV라는 변수를 사용할 수 있게 해준다
],
output: {
path: path.join(__dirname, 'dist'),
filename: '[name].js', // name은 app이 된다
publicPath: '/dist/',
},
devServer: {
historyApiFallback: true, // react router할때 필요한 설정. spa는 원래라면 어떤 주소를 치든 다 메인페이지로 간다.(index.html) 이 옵션을 true로 하면 devserver가 가짜주소를 있는 것마냥 해준다.
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());
}
if (!isDevelopment && config.plugins) {
}
export default config;
<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>react ts project</title>
<style>
html,
body {
margin: 0;
padding: 0;
overflow: initial !important;
}
* {
box-sizing: border-box;
}
</style>
</head>
<body>
<div id="app"></div>
<script src="/dist/app.js"></script>
</body>
</html>
import React from 'react';
const App = () => {
return <div>초기 세팅입니다.</div>;
};
export default App;
import React from 'react';
import { render } from 'react-dom';
import App from './layouts/App';
render(<App />, document.querySelector('#app'));
npx webpack
npx webpack
명령어를 터미널에 입력하면 root 경로에 dist폴더와 그 안에 app.js파일이 생긴다.cross-env NODE_ENV=production webpack
이라는 명령어로 실행하면 생성되는 JavaScript파일의 용량이 줄어든다.NODE_ENV=production webpack
로 돌아가지만, 윈도에서는 cross-env NODE_ENV=production webpack
로 돌려야한다. cross-env
와 ts-node
를 설치하면 좋은데 위의 1-2)에서 install 했기 때문에 해당 과정은 생략해도 된다. (webpack이 ts를 돌릴때 ts-node라는 라이브러리를 통해 돌린다)"build": "cross-env NODE_ENV=production webpack"
라는 코드가 있기 때문에 npm run build
명령어를 통해서 webpack을 실행할 수 있다.npm run build
명령어를 한 번 더 실행해야한다. 핫리로딩
즉, 고치면 바로 수정되어서 브라우저 화면에 반영되는 것을 하려면 추가적인 셋팅이 필요하다. webpack serve --env development
라는 코드가 있기 때문에 npm run dev
명령어를 통해서 webpack dev server를 실행할 수 있다.npm i @loadable/component
npm i --save-dev @types/loadable__component
import React from 'react';
import loadable from '@loadable/component';
import { Switch, Redirect, Route } from 'react-router-dom';
const Login = loadable(() => import('@pages/Login'));
const Signup = loadable(() => import('@pages/Signup'));
const App = () => {
return (
<Switch>
<Redirect exact path="/" to="/login" />
<Route path="/login" component={Login} />
<Route path="/signup" component={Signup} />
</Switch>
);
};
export default App;
CRA를 쓰기전에 직접 수동으로 셋팅하는 방법을 배우는 게 좋다고 한다. 알고쓰는 것과 모르고 쓰는 것은 다르다. 작동 원리를 안 다음 CRA를 쓰도록 하자
출처: Slack 클론 코딩[실시간 채팅 with React]