인프런에 올라간 제로초님의 강의를 보고 정리한 내용입니다.
https://www.inflearn.com/course/web-game-react
const React = require('react');
const { Component } = React;
class WordRelay extends Component {
state = {
word: '제로초',
value: '',
answer: '',
}
onSubmitForm = (e) => { //onSubmitForm : 엔터로 정보가 전송시 실행
e.preventDefault(); // 창이 새로고침 되는 걸 방지
if (this.state.word[this.state.word.length - 1] === this.state.value[0]) {
console.log( this.state.word[this.state.word.length - 1] +" == " + this.state.value[0]);
this.setState({
word : this.state.value,
value : '',
answer : '정답!',
})
this.input.focus();
}
else {
this.setState({
value : '',
answer : '땡!',
})
this.input.focus();
}
}
onRefInput = (e) => {
this.input = e; //html의 input을 이 리액트 컴포넌트에서 input으로 불러올 수 있게 된다.
}
onChangeInput = (e) => {
this.setState({value: e.target.value});
}
render(){
return (
<>
<div>{this.state.word}</div>
<form onSubmit={this.onSubmitForm}>
<input ref={this.onRefInput} value={this.state.value} onChange={this.onChangeInput}/>
<div>{this.state.answer}</div>
</form>
</>
)}
}
module.exports = WordRelay;
const React = require('react');
const { useState, useRef } = React;
const WordRelay = () => {
const [word, setWord] = useState('제로초');
const [value, setValue] = useState('');
const [answer, setAnswer] = useState('');
const inputRef = useRef(null);
const onSubmitForm = (e) => { //onSubmitForm : 엔터로 정보가 전송시 실행
e.preventDefault(); // 창이 새로고침 되는 걸 방지
if (word[word.length - 1] === value[0]) {
console.log( word[word.length - 1] +" == " + value[0]);
setWord(value);
setValue('');
setAnswer('정답!');
inputRef.current.focus();
}
else {
setValue('');
setAnswer('땡!');
inputRef.current.focus();
}
}
const onChangeInput = (e) => {
setValue(e.target.value);
}
return (
<>
<div>{word}</div>
<form onSubmit={onSubmitForm}>
<label htmlFor="word"></label>
<input ref={inputRef} value={value} id="word" className="word" onChange={onChangeInput}/>
<button>클릭!</button>
<div>{answer}</div>
</form>
</>
)}
module.exports = WordRelay;
document로 제어하기 보다는 리액트를 믿고 데이터만 조작하는 방식?
setState 시 render 함수가 실행된다.
useState로 동적인 값을 제어할 수 있다.
함수형 컴포넌트 안에 써야 한다.
const [number, setNumber] = useState(0);
const numberState = useState(0);
//numberState 배열 생성, 0 : 변수, 1 : 변수 변경 함수
const number = numberState[0];
const setNumber = numberState[1];
Hooks 는 리액트 v16.8 에 새로 도입된 기능.
함수형 컴포넌트에서도 상태 관리를 할 수 있는 useState, 그리고 렌더링 직후 작업을 설정하는 useEffect 등의 기능등을 제공.
React.Fragment는 리턴할 때
<script type="text/babel">
function GuGuDan() {
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("");
const inputRef = React.useRef();
const onChangeInput = (e) => {
setValue(e.target.value);
}
const onSubmitForm = (e) => {
e.preventDefault();
const answer = first * second;
if (parseInt(value) === answer) {
setResult("정답" + value);
setFirst(Math.ceil(Math.random() * 9));
setSecond(Math.ceil(Math.random() * 9));
setValue('');
}
else {
setResult("오답...");
setValue('');
}
inputRef.current.focus(); }
return (
<React.Fragment>
<h1>{first} 곱하기 {second} 은?</h1>
<form onSubmit={onSubmitForm}>
<input ref={inputRef} onChange={onChangeInput} value={value}/>
<button>submit</button>
</form>
<h3>{result}</h3>
</React.Fragment>
)
}
</script>
<script type="text/babel">
// 웹에다가 실제로 렌더링 해주는 역할, LikeButton을 root 태그에 붙임
ReactDOM.render(<GuGuDan/> , document.querySelector("#root"));
</script>
hook 에서는 React.useRef()로 DOM에 접근한다.
hook을 사용하면 코드 길이가 확연히 줄어든다.
state가 변하면 바뀐 부분만이 아닌 함수 전체가 통째로 실행된다.
함수 컴포넌트에서도 setState를 써서 state를 하나의 객체로 합칠 수도 있지만 효율적이지는 않다.
비동기이기 때문에 render를 여러 번 실행하더라도 랜더가 끝날 때까지 멈추는 일은 없다.
https://chanhuiseok.github.io/posts/react-7/
리액트에서 DOM에 접근하기 위한 방법. html에서 id, class 붙이는 것과 동일한 맥락
<input ref={(ref) => {this.input=ref}}/>
html에서는 class 대신 className, for 대신 htmlFor로 써줘야 힌다.
html에서 리액트 불러오고, babel 불러오지 않고 npm에서 불러오기.
package.json 생성 및 관리
create-react-app을 쓰면 자동으로 세팅가능.
스크립트의 양이 포화되는 것을 방지, 유지보수를 위해 코드를 압축한다.
src로 다른 파일에서 가져오려니 스크립트 상 중복이 발생한다.
수많은 코드들을 하나로 합쳐서 하나의 자바스크립트 파일로 만드는 것이 웹팩.
웹팩을 하기 위해 노드가 필요. (자바스크립트 실행기)
서비스 할때 쓰이는 것은 dependencies, 개발할 때 쓰이는 것은 devDependencies에 들어간다.
node의 모듈 시스템으로 npm에 설치했던 것을 불러올 수 있다.
웹팩을 설정하는 파일.
npm init으로 packjson을 설정할 수 있다.
npm i react react-dom
npm i -D webpack webpack-cli
-D는 개발에서만 쓰인다는 것을 의미한다.
파일을 쪼갤 때는 클래스 컴포넌트에 extends React.Compont, 마지막 줄에 module.exports 로 내보내주어야 한다.
해당 파일이 필요하면 require나 import로 불러와서 사용한다.
웹팩을 써서 최종적으로 하나의 파일(app.js)로만 합쳐져야 한다.
const path = require('path');
module.exports = {
mode : "development", // 상용화는 production
devtool : "eval",
resolve : {
extensions : [".js", ".jsx"],
},
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'
} // 출력
};
웹팩은 webpack.config.js로 모든 게 정해진다.
기본적인 구조.
path는 node에서 경로를 조작하기 쉽게 한다.
webpack만 터미널에 입력하면 알아서 output에 설정된 파일로 모듈들을 합쳐준다.
자바스크립트 파일 내부에서 다른 파일이 불러오는 파일은 적어주지 않아도 알아서 추적해서 불러온다.
resolve의 extensions을 쓰면 확장자를 적지 않아도 자동으로 추적한다.
webpack으로 실행하려고 했지만 등록되지 않았을 때
package.json의 scripts 객체에 dev 키값으로 webpack 설정 후 npm run (dev)
npx webpack
webpack 명령어를 실행하면 entry를 읽어서 output 한 파일로 만들어준다.
babel을 추가해야만 jsx를 사용할 수 있다.
정규표현식 숙지할 필요
강의의 버전과 현재의 버전이 차이가 너무 남...
npm i -D @babel/core : babel의 기본
npm i -D @babel/preset-env : 브라우저에 맞게 최신 문법을 호환(옛날문법으로)
npm i -D @babel/preset-react : jsx 지원가능
npm i -D babel-loader : babel과 react 연결
const path = require('path');
module.exports = {
mode : "development", // 상용화는 production
devtool : "eval",
resolve : {
extensions : [".js", ".jsx"],
},
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'
}
};
entry에 들어 있는 파일들은 module이 변환시켜 준다.
require(), import는 다른 파일의 모듈을 포함한다는 점에서 동일하지만 import는 ES6에서만 사용되는 문법이라는 점, require가 어디서든 쓸 수 있는 반면 import는 파일의 시작 부분에만 쓸 수 있다는 점이 다르다.
const path = require('path');
module.exports = {
mode : "development", // 상용화는 production
devtool : "eval",
resolve : {
extensions : [".js", ".jsx"],
},
entry : {
app : ['./client'],
}, // 입력
module : {
rules : [{
test : /\.jsx?/,
loader : 'babel-loader',
options : {
presets : [
['@babel/preset-env', {
target : {
browser : ['last 2 chrome versions']
},
}], '@babel/preset-react'
],
plugins : [],
},
}]
},
plugins: [
],
output : {
path : path.join(__dirname, 'dist'),
filename : 'App.js'
} // 출력
};
plugin의 모음이 preset.
preset에 중괄호로 옵션을 줄 수가 있다.
entry 파일에 Loaders 모듈 적용, 플러그인 추가 적용 후 out으로 나온다.
기타 설정들(mode, devtool, resolve) 등은 위에 몰아넣는다.
babel/preset-env
const React = require('react');
const { useState } = React;
class WordRelay extends React.Component {
state = {
word: '제로초',
value: '',
answer: '',
}
onSubmitForm = (e) => { //onSubmitForm : 엔터로 정보가 전송시 실행
e.preventDefault(); // 창이 새로고침 되는 걸 방지
if (this.state.word[this.state.word.length - 1] === this.state.value[0]) {
console.log( this.state.word[this.state.word.length - 1] +" == " + this.state.value[0]);
this.setState({
word : this.state.value,
value : '',
answer : '정답!',
})
this.input.focus();
}
else {
this.setState({
value : '',
answer : '땡!',
})
this.input.focus();
}
}
onRefInput = (e) => {
this.input = e; //html의 input을 이 리액트 컴포넌트에서 input으로 불러올 수 있게 된다.
}
onChangeInput = (e) => {
this.setState({value: e.target.value});
}
render(){
return (
<>
<div>{this.state.word}</div>
<form onSubmit={this.onSubmitForm}>
<input ref={this.onRefInput} value={this.state.value} onChange={this.onChangeInput}/>
<div>{this.state.answer}</div>
</form>
</>
)}
}
module.exports = WordRelay;
리액트로 폼을 다룰 때 vaule를 쓴다면 onChange를 쓰거나 defaultValue를 같이 써줘야만 한다.
form 내부의 button 태그가 존재한다면 이벤트를 주지 않아도 누르면 입력 처리가 된다.
자동으로 빌드하지 않으면 수정할 때마다 수동으로 빌드를 돌려야만 한다.
npm i -D react-refresh : 프론트용 새로고침
npm i -D react-refresh webpack-dev-server : 서버용 새로고침
핫리로딩 시 webpack serve --env development로 webpack-dev-server를 실행할 수 있다.
react-refresh와 react-webpack-plugin이 없어도 새로고침은 작동하지만, 기존의 데이터는 다 날아간다.
기존의 데이터를 유지하냐 하지 않느냐는 굉장히 중요한 요소다.
const React = require('react');
const { useState, useRef } = React;
const WordRelay = () => {
const [word, setWord] = useState('제로초');
const [value, setValue] = useState('');
const [answer, setAnswer] = useState('');
const inputRef = useRef(null);
const onSubmitForm = (e) => { //onSubmitForm : 엔터로 정보가 전송시 실행
e.preventDefault(); // 창이 새로고침 되는 걸 방지
if (word[word.length - 1] === value[0]) {
console.log( word[word.length - 1] +" == " + value[0]);
setWord(value);
setValue('');
setAnswer('정답!');
inputRef.current.focus();
}
else {
setValue('');
setAnswer('땡!');
inputRef.current.focus();
}
}
const onRefInput = (e) => {
inputRef = e; //html의 input을 이 리액트 컴포넌트에서 input으로 불러올 수 있게 된다.
}
const onChangeInput = (e) => {
setValue(e.target.value);
}
return (
<>
<div>{word}</div>
<form onSubmit={onSubmitForm}>
<input ref={inputRef} value={value} onChange={onChangeInput}/>
<button>클릭!</button>
<div>{answer}</div>
</form>
</>
)}
module.exports = WordRelay;
const { useState, useRef } = React;
기존의 메소드는 const 붙여서 객체화
this.state로 쓰던 state는 useState 써서 따로따로 지정
ref는 current 붙여서 사용
HMR : 핫모듈리로더, 어떤 컴포넌트가 바뀌어서 수정되는지를 알려준다.
html 태그에 class 대신 className을 써야 한다.
label에서 for을 쓸 때 htmlFor을 써야 한다.