#2 끝말잇기 / 함수 컴포넌트, 웹팩
인프런에 올라간 제로초님의 강의를 보고 정리한 내용입니다.
https://www.inflearn.com/course/web-game-react
코드
클래스 버전
WordReplay.jsx
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;
함수 버전
WordReplay.jsx
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;
#1-9 ~ 2-2 함수형 컴포넌트
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);
- 상태의 기본값을 파라미터로 넣어서 호출한다.
- 이 함수를 호출해주면 배열이 반환된다.
- 첫번째 원소는 현재 상태, 두번째 원소는 Setter 함수.
- useState의 괄호 안에 초기값을 설정해준다.
- 배열 비구조화 할당으로 줄여서 쓴 것.
- number는 변수, setNumber는 변수를 변경하는 함수.
함수형 컴포넌트 안에 써야 한다.
const [number, setNumber] = useState(0);
const numberState = useState(0);
//numberState 배열 생성, 0 : 변수, 1 : 변수 변경 함수
const number = numberState[0];
const setNumber = numberState[1];
hook
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를 하나의 객체로 합칠 수도 있지만 효율적이지는 않다.
- const [state, setState]React.useState()
- setState마다 모든 state 값을 설정해줘야 한다.
- 하나라도 빼먹는다면 해당 state가 사라진다.
비동기이기 때문에 render를 여러 번 실행하더라도 랜더가 끝날 때까지 멈추는 일은 없다.
ref
https://chanhuiseok.github.io/posts/react-7/
리액트에서 DOM에 접근하기 위한 방법. html에서 id, class 붙이는 것과 동일한 맥락
<input ref={(ref) => {this.input=ref}}/>
- this.input으로 해당 input 태그에 접근할 수 있다.
html에서는 class 대신 className, for 대신 htmlFor로 써줘야 힌다.
#2-3 웹팩 설치
html에서 리액트 불러오고, babel 불러오지 않고 npm에서 불러오기.
package.json 생성 및 관리
create-react-app을 쓰면 자동으로 세팅가능.
웹팩
스크립트의 양이 포화되는 것을 방지, 유지보수를 위해 코드를 압축한다.
src로 다른 파일에서 가져오려니 스크립트 상 중복이 발생한다.
수많은 코드들을 하나로 합쳐서 하나의 자바스크립트 파일로 만드는 것이 웹팩.
웹팩을 하기 위해 노드가 필요. (자바스크립트 실행기)
서비스 할때 쓰이는 것은 dependencies, 개발할 때 쓰이는 것은 devDependencies에 들어간다.
client.jsx
node의 모듈 시스템으로 npm에 설치했던 것을 불러올 수 있다.
webpack.config.js
웹팩을 설정하는 파일.
npm init으로 packjson을 설정할 수 있다.
npm i react react-dom
npm i -D webpack webpack-cli
-D는 개발에서만 쓰인다는 것을 의미한다.
#2-4 모듈, 웹팩 설정
파일을 쪼갤 때는 클래스 컴포넌트에 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에서 경로를 조작하기 쉽게 한다.
- C:\users\zerocho... 를 현재 경로 기준으로 검색할 수 있게 된다.
webpack만 터미널에 입력하면 알아서 output에 설정된 파일로 모듈들을 합쳐준다.
자바스크립트 파일 내부에서 다른 파일이 불러오는 파일은 적어주지 않아도 알아서 추적해서 불러온다.
resolve의 extensions을 쓰면 확장자를 적지 않아도 자동으로 추적한다.
#2-5 웹팩 빌드
webpack으로 실행하려고 했지만 등록되지 않았을 때
package.json의 scripts 객체에 dev 키값으로 webpack 설정 후 npm run (dev)
npx webpack
webpack 명령어를 실행하면 entry를 읽어서 output 한 파일로 만들어준다.
- 원래대로라면 dist 부분에 App.js 가 만들어져야 한다.
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 연결
#2-6 웹팩으로 빌드
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는 파일의 시작 부분에만 쓸 수 있다는 점이 다르다.
#2-7 @babel/preset-env와 플러그인
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에 중괄호로 옵션을 줄 수가 있다.
- 웹팩에서 합쳐주는 모듈 말고 별도로 무언가를 추가하고 싶다면 pugins
웹팩의 핵심 구성 요소
- entry - input 파일
- Loaders -모듈
- Plugin - 추가적으로 하고 싶은 작업
- output - 결과로 나올 파일
- mode - 개발자 용인지, 실용인지 (development)
entry 파일에 Loaders 모듈 적용, 플러그인 추가 적용 후 out으로 나온다.
기타 설정들(mode, devtool, resolve) 등은 위에 몰아넣는다.
babel/preset-env
- preset는 플러그인들의 모음, browers의 조건에 해당하는 브라우저를 지원한다.
2-8 끝말잇기
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 태그가 존재한다면 이벤트를 주지 않아도 누르면 입력 처리가 된다.
#2-9 웹팩데브서버, 핫리로딩
자동으로 빌드하지 않으면 수정할 때마다 수동으로 빌드를 돌려야만 한다.
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이 없어도 새로고침은 작동하지만, 기존의 데이터는 다 날아간다.
기존의 데이터를 유지하냐 하지 않느냐는 굉장히 중요한 요소다.
#2-10 클래스 → 훅
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 붙여서 사용
- inputRef.focus → inputRef.current.focus
HMR : 핫모듈리로더, 어떤 컴포넌트가 바뀌어서 수정되는지를 알려준다.
html 태그에 class 대신 className을 써야 한다.
label에서 for을 쓸 때 htmlFor을 써야 한다.
Author And Source
이 문제에 관하여(#2 끝말잇기 / 함수 컴포넌트, 웹팩), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@sham/2-끝말잇기-함수-컴포넌트-웹팩저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)