클래스의 개념이 조금 잡히기 시작하니 클래스를 쓰지 말란다. 아무튼 클래스 사용을 지양하기 위해 나온 것이 React Hooks. 함수 형태로 사용 가능하다. 코드가 훨씬 짧고 간결하다. 클래스 대신 컴포넌트로, state를 객체로 쓰는 대신 하나씩 사용하기. 클래스를 사용하든 Hooks를 사용하든 개인의 취향에 달려있지만 React는 Hooks를 권장하고 있다.
/* Class */
this.state = {
first: Math.ceil(Math.random() * 9),
second: Math.ceil(Math.random() * 9),
value: '',
result: ''
};
/* Hooks */
const [first, setFirst] = React.useState(Math.ceil(Math.random() * 9));
const [second, setSecond] = React.useState(Math.ceil(Math.random() * 9));
const [value, setValue] = React.useState('');
const [result, setResult] = React.useState('');
클래스와 Hooks의 state 선언 비교. Hooks는 useState
처럼 use
로 시작하는 함수를 사용한다.
페이지 하나를 살펴봐도 컴포넌트가 수십, 수백개가 들어간다. 페이스북 개발자가 밝힌 바로는 페이스북에 사용된 컴포넌트가 약 2만 개. 끝도 없이 증식하는 컴포넌트를 유지보수하기란 은하수에서 B612 찾기와 같다. 이런 중복을 없애기 위해 여러 개의 js 파일을 하나의 파일로 만들어주기 위한 기술이 웹팩이다.
웹팩을 사용하기 위해서는 node 설치가 필요하다. macOS에서 설치법은 다음과 같다.
brew install node //node, npm 설치
node -v
npm -v //node, npm 버전 확인
/* 작업 중인 폴더로 이동한 뒤 */
npm init //package.json을 생성한다.
npm i react react-dom //리액트와 리액트돔 설치
npm i -D webpack webpack-cli //웹팩 설치
-D
는 개발용을 의미한다. package.json
의 dependencies
가 아닌 devDependencies
에 저장된다. 즉, 클라이언트에 배포할 때는 웹팩이 필요하지 않기 때문에 굳이 넣지 않는다.
웹팩의 설정은 기본적으로 webpack.config.js
파일에서 모두 이루어진다. 위 파일을 만들어 config 설정을 하면 자동으로 읽어온다.
const path = require('path');
module.exports = {
mode: 'development',
devtool: 'eval', //프로덕션용: hidden-source-map
resolve: {
extensions: ['.jsx', '.js'],
},
entry: {
app: './client',
},
module: {
rules: [{
test: /\.jsx?$/,
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env', '@babel/preset-react'],
},
}],
},
output: {
path: path.join(__dirname, 'dist'),
filename: 'app.js',
},
};
mode
와 devtool
은 개발용인지 배포용인지에 따라 설정값이 다르다. 개발용일 경우에는 각각 development
와 eval
을, 배포용일 때에는 production
과 hidden-source-map
을 사용한다.
resolve
는 합칠 파일의 확장자를 관리한다. extensions
에 확장자를 추가하면 entry
에 파일명을 적을 때 확장자를 생략할 수 있다. 위의 코드에서는 .jsx
와 .js
확장자를 추가했다.
entry
는 쉽게 말하자면 웹팩을 하려는 파일을 입력하는 곳이다.
/* client.jsx */
const React = require('react');
const ReactDOM = require('react-dom');
const GuGuDan = require('./GuGuDan');
ReactDOM.render(<GuGuDan />, document.querySelector('#root'));
client.jsx
를 입력하면 코드를 읽어 module
에 있는 설정들을 적용한 뒤, output
에 설정된 파일로 합쳐져 출력된다.
module
은 entry
에서 입력된 파일들의 여러가지 설정을 할 수 있는 곳이다. options
의 preset
은 여러 plugin의 모음이라고 할 수 있다. 위 코드에서 @babel/preset-env
경우에는 브라우저의 설정에 맞게 자동으로 변환해주는 모듈이다. 기본적으로 preset
안에 모듈 이름만 넣으면 모든 설정을 가져오지만, 특정 설정만 하고 싶을 경우에는 다음과 같이 사용하면 된다.
['@babel/preset-env', {
targets: {
browsers: ['> %5 in KR', 'last 2 chrome versions'],
},
debug: true,
}],
모듈을 배열로 감싸준 뒤, 첫번째 인덱스에 사용하고자 하는 모듈의 이름을 넣고, 두번째 배열에 설정값을 넣는다. @babel/preset-env
경우에는 브라우저의 설정을 버전별로 관리할 수 있으므로 last 2 chrome versions
를 입력한다면 최신 두 버전만 지원한다는 뜻이다. 브라우저 리스트
추가 팁
코드를 수정해야할 때마다 웹팩을 다시 실행시켜야하는데 자동으로 웹팩을 리로딩시키는 플러그인이 있다. npm으로 react-refresh
와 @pmmmwh/react-refresh-webpack-plugin
을 설치하면 된다. webpack-dev-server
는 개발용 서버이므로 같이 설치해주는 게 좋다. 단, 사용하기 위해서는 package.json
의 script에 webpack serve --env development
를 추가해준다.
const path = require('path');
const { webpack } = require('webpack');
// refresh를 위한 모듈 추가
const RefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
module.exports = {
name: 'wordrelay-setting',
mode: 'development',
devtool: 'eval',
resolve: {
extensions: ['.js', '.jsx']
},
entry: {
app: ['./client'],
},
module: {
rules: [{
test: /\.jsx?/,
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env', {
targets: {
browsers: ['> 1% in KR'],
},
debug: true,
}],
'@babel/preset-react',
],
plugins: [
'@babel/plugin-proposal-class-properties',
'react-refresh/babel', //추가
],
},
}],
},
plugins: [
new RefreshWebpackPlugin() //추가
],
output: {
path: path.join(__dirname, 'dist'),
filename: 'app.js',
publicPath: '/dist/',
},
devServer: { //서버 설정 추가
publicPath: '/dist/',
hot: true,
},
}
핫 리로딩의 장점은 기존 데이터가 유지된다는 것이다.