본문 바로가기

Front-End: Web/React.js

[제로초] ReactJS Chpater 1. 리액트 시작하기

반응형

1. 리액트를 왜 쓰는가

React의 미래

  • 현재 한국에선 React.js가 Vue.js보단 대세!
  • 앞으로 10년 정도는 더 사용될 것

React 사용하는 이유

Single Page Application(SPA)

  • 전통적인 웹은 Multiple Page Application(MPA). 문서같은 페이지(공식문서)는 React를 사용할 필요없이 MPA로 만들면 됨.
  • 이전에는 JQuery, JSP로 백엔드 개발자가 프론트엔드를 함께 개발했는데, 현재는 프론트에 다양한 기능들이 추가되면서 프론트엔드 개발자가 추가되었음.
  • 지메일 ➡️ 첫 웹 애플리케이션. 웹 사이트가 아니라 하나의 애플리케이션이라는 느낌.
  • 웹에서 사이트 뿐만 아니라 다양한 애플리케이션(워드, 파워포인트, ... 등 사용 가능)이 나옴
  • 전통적인 웹보다 데이터가 많아짐. 데이터가 바뀌면 화면도 따라서 실시간으로 바뀜. 단 전체가 아니라 변경된 데이터만 바뀜. ➡️ 자바스크립트만으로 구현하기 어려움. 이 문제를 해결하기 위해 나온 게 Angular.js, React.js, Vue.js ... 등 SPA로 개발하는 라이브러리가 나옴.
  • 그러면 Vue, React, Angular 등 다양한 라이브러리가 왜 있느냐?라고 묻는다면, 데이터와 화면을 일치하는 문제를 각각 다른 방식으로 해결함.

웹 애플리케이션

  • 모바일 앱처럼 기능적으로
  • 웹 애플리케이션 웹에서 다양한 애플리케이션을 돌릴 수 있게 됨

React

  • 페이스북이 개발
  • **복잡한 웹앱에서 데이터와 화면 일치 문제**를 쉽게 해결할 수 있음
  • 단순한 문서 같은 경우는 React로 만들 필요가 없음 (React 공식 문서는 React로 만들 필요는 없지만 React로 만들어졌음)
  • 화면 흰색 깜빡임 문제 없이 화면이 부드럽게 넘어감

**단점**

  • **검색 엔진 문제(SEO)**가 뛰어나지 않음
    • React는 검색 엔진에 노출이 잘 안됨.
    • 검색 엔진이 중요하다면 React보다는 html, css, js로 만들자.

2. 첫 리액트 컴포넌트(아직은 Class)

  • **React도 JavaScript일 뿐이므로, HTML, CSS, JavaScript로 이뤄져 있음**
    • 그래서 기본기인 JavaScript가 탄탄해야 함!
  • 리액트는 자바스크립트 코드가 화면도 그려주고 css도 입혀주기 때문에, **전체를 컨트롤할 수 있음**
  • **데이터(state) 중심으로 움직임**
    • **Component는 데이터(state)와 화면(render()의 return부분)을 하나로 묶어둔 것임**
    • **화면에 변경될 부분을 state로 만듦**
    • 데이터가 변경되면 화면도 자동으로 변경됨(원시적인 웹은 화면을 먼저 만들고 자바스크립트로 데이터를 변경함. 리액트는 원시적인 방법과 정반대임)

만드는 방법

~~(1) class ➡️ old 문법~~

  • 원시적인 형태(현재는 이렇게 안씀)
  • 99% 사용 안되는데 1%인 Error boundary에서만 사용됨.

(2) function (혹은 화살표 함수) ➡️ new 최신 문법

  • constructor 쓰기 귀찮은데 안써도 됨

**🕹️ class(클래스) 기본 예제 코드**

  • LikeButton을 'root'라는 ReactDOM에 그리기
 
 
      "use strict"; // 엄격한 규제 버전

      class LikeButton extends React.Component {
        constructor(props) {
          super(props);
          this.state = { liked: false };
        }
        render() {
          if (this.state.liked) {
            return "You liked this.";
          }
          return React.createElement(
            "button",
            { onClick: () => this.setState({ liked: true }) },
            "Like"
          );
        }
      }
    
      ReactDOM.render(
        React.createElement(LikeButton),
        document.querySelector("#root")
      );
    

script

  • 상단의 development는 개발단계에서 사용하고, 실무에서는 하단의 production을 사용해야 한다.
  • 하지만 실제 실무에서는 script로 CDN을 가져오지 않고, **webpack, vite, babel**을 사용한다.

 

3. 가독성을 위한 JSX(XML임!)

JSX(JavaScript + Xml)

  • 하단의 코드는 return부분이 가독성이 좋지 않음

🕹️ **코드1**

return React.createElement(
	"button",
	{ onClick: () => this.setState({ liked: true }) },
	"Like"
);
  • HTML하다가 갑자기 코드1처럼 코딩하라고하면 미쳐버림
  • React에서 코드의 가독성을 높이기 위해 **HTML느낌이 나는 문법을 만들어줌** => **JSX**

**🕹️ 코드2**

return (
	<button onClick={() => this.setState({ liked: true })}>Like</button>
);
  • 코드1보다 더 가독성이 있다!
  • JSX가 코드2를 코드1로 바꿔준다.

Babel

🕹️ **코드3**

<script>
	ReactDOM.render(<LikeButton />, document.querySelector("#root"));
</script>

근데 자바스크립트인데 코드2처럼 HTML에서 쓰는 태그들을 그대로 쓸 순 없다. 그래서 코드2는 실행해보면 해석하지 못해서 안돌아간다. 태그(<)를 해석하지 못하기 때문이다. 그러면 리액트에서 어떻게 JSX코드를 자바스크립트 코드로 해석할 수 있을까?

![01](.\images\01.png)

이 역할을 하는게 바로 **babel** 라이브러리다.

Babel이란?

  • **JSX코드(코드2)를 자바스크립트 코드(코드1)로 바꿔준다.**

Babel 사용법

가장 쉽게 babel 라이브러리를 추가하는 방법은 script에 추가하는거다.

  1. html에서 script를 추가해준다.
  1. js 작성하는 script 태그에 다음과 같이 추가한다.
<script type="text/babel">

babel이라는 라이브러리가 **text/babel**이라는 걸 발견하고 JSX코드를 자바스크립트코드로 바꿔준다. 그래서 이제 에러가 안뜬다.

**🕹️ 전체 코드**

 
 
      "use strict";

      class LikeButton extends React.Component {
        constructor(props) {
          super(props);
          this.state = { liked: false };
        }
        render() {
          if (this.state.liked) {
            return "You liked this.";
          }
          return (
            <button onClick={() => this.setState({ liked: true })}>Like</button>
          );
        }
      }
    
      ReactDOM.render(<LikeButton />, document.querySelector("#root"));
    

하지만 warning이 발생한다.

![01](.\images\02.png)

왜냐하면 하단의 text/babel에 적힌 코드가 React 17 버전의 코드이기 때문이다. 그럼에도 돌아가는 이유는 **React 18 버전이 17버전도 지원**하기 때문이다.

<script type="text/babel">
	ReactDOM.render(<LikeButton />, document.querySelector("#root")); // React 17버전 코드
</script>

React 18 버전으로 코드를 변경하면 아래와 같다.

<script type="text/babel">
	ReactDOM.createRoot(document.querySelector('#root')).render(<LikeButton/>) // React 18버전 코드
</script>
  • **render ➡️ createRoot**
  • **⚠️render를 사용하면 React 17 버전으로 인식해서 React 18 코드가 돌아가지 않는다.**
  • 따라서 지금 React 어떤 버전을 사용하고 있는지 확인해보아야 함

🍯 Tips

  • **Component는 대문자(LikeButton)로 시작**하고, 기존의 일반 태그들은 소문자(button)로 시작한다**(JSX는 HTML이 아니라 XML이다. 그래서 더 엄격하다).**
  • **JavaScript 코드는 중괄호{}**로 감싸줌
  • if문 대신 **삼항연산자**를 많이 사용함
  • for문 대신 **[].map**을 많이 사용함
  • return은 꼭 하나의 부모 태그로 감싸주어야 함. **fragment (<></>)**를 자주 사용함

4. 클래스 컴포넌트의 형태와 리액트 dev tools

의문점 with setState

Q. <code>this.state = { liked: false };</code> 로 정의하고 liked를 true로 변경할 때, 아래와 같이 변경할 순 없나?

this.state.liked = true;

A. ⭐ React에선 객체를 함부로 바꾸지 말고 복사해라! (불변성)

  • pop, push, shift, unshift, splice
  • **concat, slice**

⁉️목록에서 위와 아래의 차이점은?

  • 위: **기존 배열을 직접적으로 수정함**
  • 아래: **새로운 배열을 만들어냄**
  • React에서는 위의 것을 사용하지 말고, 아래와 같이 새로운 배열을 만들어내는 것을 사용해야 함(배열도 객체니까! 함부로 바꾸면 안된다!)

React에서 state를 바꾸려면 setState를 사용해서 바꿔야만 한다. state의 데이터를 변경하면 화면이 변경되기 때문이다.

크롬 React Dev Tool 설치

  • **Components**: 현재 state, props 확인 가능
  • **Profiler**: 성능문제 해결 시 사용

onClick과 같이 함수를 바깥으로 꺼내려면 화살표 함수를 쓰면 된다.

onClick = () => { ... } ⬅️

...
return (
	<button onClick={onClick}>button</button>
)

1. 리액트를 왜 쓰는가

React의 미래

  • 현재 한국에선 React.js가 Vue.js보단 대세!
  • 앞으로 10년 정도는 더 사용될 것

React 사용하는 이유

Single Page Application(SPA)

  • 전통적인 웹은 Multiple Page Application(MPA). 문서같은 페이지(공식문서)는 React를 사용할 필요없이 MPA로 만들면 됨.
  • 이전에는 JQuery, JSP로 백엔드 개발자가 프론트엔드를 함께 개발했는데, 현재는 프론트에 다양한 기능들이 추가되면서 프론트엔드 개발자가 추가되었음.
  • 지메일 ➡️ 첫 웹 애플리케이션. 웹 사이트가 아니라 하나의 애플리케이션이라는 느낌.
  • 웹에서 사이트 뿐만 아니라 다양한 애플리케이션(워드, 파워포인트, ... 등 사용 가능)이 나옴
  • 전통적인 웹보다 데이터가 많아짐. 데이터가 바뀌면 화면도 따라서 실시간으로 바뀜. 단 전체가 아니라 변경된 데이터만 바뀜. ➡️ 자바스크립트만으로 구현하기 어려움. 이 문제를 해결하기 위해 나온 게 Angular.js, React.js, Vue.js ... 등 SPA로 개발하는 라이브러리가 나옴.
  • 그러면 Vue, React, Angular 등 다양한 라이브러리가 왜 있느냐?라고 묻는다면, 데이터와 화면을 일치하는 문제를 각각 다른 방식으로 해결함.

웹 애플리케이션

  • 모바일 앱처럼 기능적으로
  • 웹 애플리케이션 웹에서 다양한 애플리케이션을 돌릴 수 있게 됨

React

  • 페이스북이 개발
  • **복잡한 웹앱에서 데이터와 화면 일치 문제**를 쉽게 해결할 수 있음
  • 단순한 문서 같은 경우는 React로 만들 필요가 없음 (React 공식 문서는 React로 만들 필요는 없지만 React로 만들어졌음)
  • 화면 흰색 깜빡임 문제 없이 화면이 부드럽게 넘어감

**단점**

  • **검색 엔진 문제(SEO)**가 뛰어나지 않음
    • React는 검색 엔진에 노출이 잘 안됨.
    • 검색 엔진이 중요하다면 React보다는 html, css, js로 만들자.

2. 첫 리액트 컴포넌트(아직은 Class)

  • **React도 JavaScript일 뿐이므로, HTML, CSS, JavaScript로 이뤄져 있음**
    • 그래서 기본기인 JavaScript가 탄탄해야 함!
  • 리액트는 자바스크립트 코드가 화면도 그려주고 css도 입혀주기 때문에, **전체를 컨트롤할 수 있음**
  • **데이터(state) 중심으로 움직임**
    • **Component는 데이터(state)와 화면(render()의 return부분)을 하나로 묶어둔 것임**
    • **화면에 변경될 부분을 state로 만듦**
    • 데이터가 변경되면 화면도 자동으로 변경됨(원시적인 웹은 화면을 먼저 만들고 자바스크립트로 데이터를 변경함. 리액트는 원시적인 방법과 정반대임)

만드는 방법

~~(1) class ➡️ old 문법~~

  • 원시적인 형태(현재는 이렇게 안씀)
  • 99% 사용 안되는데 1%인 Error boundary에서만 사용됨.

(2) function (혹은 화살표 함수) ➡️ new 최신 문법

  • constructor 쓰기 귀찮은데 안써도 됨

**🕹️ class(클래스) 기본 예제 코드**

  • LikeButton을 'root'라는 ReactDOM에 그리기
 
 
      "use strict"; // 엄격한 규제 버전

      class LikeButton extends React.Component {
        constructor(props) {
          super(props);
          this.state = { liked: false };
        }
        render() {
          if (this.state.liked) {
            return "You liked this.";
          }
          return React.createElement(
            "button",
            { onClick: () => this.setState({ liked: true }) },
            "Like"
          );
        }
      }
    
      ReactDOM.render(
        React.createElement(LikeButton),
        document.querySelector("#root")
      );
    

script

  • 상단의 development는 개발단계에서 사용하고, 실무에서는 하단의 production을 사용해야 한다.
  • 하지만 실제 실무에서는 script로 CDN을 가져오지 않고, **webpack, vite, babel**을 사용한다.

 

3. 가독성을 위한 JSX(XML임!)

JSX(JavaScript + Xml)

  • 하단의 코드는 return부분이 가독성이 좋지 않음

🕹️ **코드1**

return React.createElement(
	"button",
	{ onClick: () => this.setState({ liked: true }) },
	"Like"
);
  • HTML하다가 갑자기 코드1처럼 코딩하라고하면 미쳐버림
  • React에서 코드의 가독성을 높이기 위해 **HTML느낌이 나는 문법을 만들어줌** => **JSX**

**🕹️ 코드2**

return (
	<button onClick={() => this.setState({ liked: true })}>Like</button>
);
  • 코드1보다 더 가독성이 있다!
  • JSX가 코드2를 코드1로 바꿔준다.

Babel

🕹️ **코드3**

<script>
	ReactDOM.render(<LikeButton />, document.querySelector("#root"));
</script>

근데 자바스크립트인데 코드2처럼 HTML에서 쓰는 태그들을 그대로 쓸 순 없다. 그래서 코드2는 실행해보면 해석하지 못해서 안돌아간다. 태그(<)를 해석하지 못하기 때문이다. 그러면 리액트에서 어떻게 JSX코드를 자바스크립트 코드로 해석할 수 있을까?

![01](.\images\01.png)

이 역할을 하는게 바로 **babel** 라이브러리다.

Babel이란?

  • **JSX코드(코드2)를 자바스크립트 코드(코드1)로 바꿔준다.**

Babel 사용법

가장 쉽게 babel 라이브러리를 추가하는 방법은 script에 추가하는거다.

  1. html에서 script를 추가해준다.
  1. js 작성하는 script 태그에 다음과 같이 추가한다.
<script type="text/babel">

babel이라는 라이브러리가 **text/babel**이라는 걸 발견하고 JSX코드를 자바스크립트코드로 바꿔준다. 그래서 이제 에러가 안뜬다.

**🕹️ 전체 코드**

 
 
      "use strict";

      class LikeButton extends React.Component {
        constructor(props) {
          super(props);
          this.state = { liked: false };
        }
        render() {
          if (this.state.liked) {
            return "You liked this.";
          }
          return (
            <button onClick={() => this.setState({ liked: true })}>Like</button>
          );
        }
      }
    
      ReactDOM.render(<LikeButton />, document.querySelector("#root"));
    

하지만 warning이 발생한다.

![01](.\images\02.png)

왜냐하면 하단의 text/babel에 적힌 코드가 React 17 버전의 코드이기 때문이다. 그럼에도 돌아가는 이유는 **React 18 버전이 17버전도 지원**하기 때문이다.

<script type="text/babel">
	ReactDOM.render(<LikeButton />, document.querySelector("#root")); // React 17버전 코드
</script>

React 18 버전으로 코드를 변경하면 아래와 같다.

<script type="text/babel">
	ReactDOM.createRoot(document.querySelector('#root')).render(<LikeButton/>) // React 18버전 코드
</script>
  • **render ➡️ createRoot**
  • **⚠️render를 사용하면 React 17 버전으로 인식해서 React 18 코드가 돌아가지 않는다.**
  • 따라서 지금 React 어떤 버전을 사용하고 있는지 확인해보아야 함

🍯 Tips

  • **Component는 대문자(LikeButton)로 시작**하고, 기존의 일반 태그들은 소문자(button)로 시작한다**(JSX는 HTML이 아니라 XML이다. 그래서 더 엄격하다).**
  • **JavaScript 코드는 중괄호{}**로 감싸줌
  • if문 대신 **삼항연산자**를 많이 사용함
  • for문 대신 **[].map**을 많이 사용함
  • return은 꼭 하나의 부모 태그로 감싸주어야 함. **fragment (<></>)**를 자주 사용함

4. 클래스 컴포넌트의 형태와 리액트 dev tools

의문점 with setState

Q. <code>this.state = { liked: false };</code> 로 정의하고 liked를 true로 변경할 때, 아래와 같이 변경할 순 없나?

this.state.liked = true;

A. ⭐ React에선 객체를 함부로 바꾸지 말고 복사해라! (불변성)

  • pop, push, shift, unshift, splice
  • **concat, slice**

⁉️목록에서 위와 아래의 차이점은?

  • 위: **기존 배열을 직접적으로 수정함**
  • 아래: **새로운 배열을 만들어냄**
  • React에서는 위의 것을 사용하지 말고, 아래와 같이 새로운 배열을 만들어내는 것을 사용해야 함(배열도 객체니까! 함부로 바꾸면 안된다!)

React에서 state를 바꾸려면 setState를 사용해서 바꿔야만 한다. state의 데이터를 변경하면 화면이 변경되기 때문이다.

크롬 React Dev Tool 설치

  • **Components**: 현재 state, props 확인 가능
  • **Profiler**: 성능문제 해결 시 사용

onClick과 같이 함수를 바깥으로 꺼내려면 화살표 함수를 쓰면 된다.

onClick = () => { ... } ⬅️

...
return (
	<button onClick={onClick}>button</button>
)

 

5. 함수 컴포넌트(함수형 컴포넌트 아님)

**🕹️ 함수 컴포넌트로 변경한 코드**

function LikeButton() { // 함수형 컴포넌트X 함수 컴포넌트O
	const [liked, setLiked] = React.useState(false);
    
	if (liked) {
		return "You liked this";
	}
    return (
		<button onClick={() => { setLiked(true); }}>Like</button>
	);
}

useState ( = this.state)

  • **구조분해** **(배열)** : useState 함수가 배열을 return함
const liked = result[0]; // 데이터(state)
const setLiked = result[1]; // 데이터를 바꾸는 함수

**🕹️ 화살표 함수로 변경한 코드**

const LikeButton = () => {
	...
}
  • this 쓸 일이 없음(헷갈리지 않음)
  • 코드가 짧아서 간결하고 깔끔함

🍯 Tips

  1. 코드 깔끔하게 짜는 방법
    • **최대한 안짜면 됨**(this안쓰면 arrow function으로 짜기) ➡️ 성능도 좋아짐
  2. 어떤 사이트든지 살펴보면서 state 요소들 찾아보기 (추측. 감🍊 잡기)
  3. 라이브러리의 코드를 상상해보기 (ex. setState, useState, ... )

6. 구구단 리액트로 만들기

**state로 만들 요소** (총 4개)

  • 문제(**5**x**6**=?)에 있는 숫자 2개 ➡️ first, second
  • 답 입력하는 input칸 ➡️ value
  • 정답여부(땡, 딩동딩) ➡️ Result
class GuGuDan extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            first: Math.ceil(Math.random() * 9),
            second: Math.ceil(Math.random() * 9),
            value: "",
            result: "",
        };
    }

    render() {
        return (
            <div>
                <div>
                    {this.state.first} 곱하기 {this.state.second}는?
                </div>
                <form>
                    <input
                        type="number"
                        value={this.state.value}
                        />
                    <button>입력!</button>
                </form>
                <div>{this.state.result}</div>
            </div>
        );
    }
}

코드에 숫자를 입력하면 input에 value가 변경되지 않는다. 변경되기 위해선 <input>에 onChange이벤트를 추가해야 한다(onClick, onChange, onSubmit, onInput, onLoad, ...).

<input
    type="number"
    value={this.state.value}
    onChange={(e) => { this.setState({ value: e.target.value }); }}
/>

그리고 form태그에 결과값을 전송해주는 **onSubmit**을 추가해야 한다.

<form onSubmit = {(e) => {
    e.preventDefault();
    if (parseInt(value) === this.state.first * this.state.second) {
        this.setState({
            result: "정답",
            first: Math.ceil(Math.random() * 9),
            second: Math.ceil(Math.random() * 9),
            value: "",
        });
    } else {
        this.setState({
            result: "땡",
            value: "",
        });
    }
}}/>

🕹️ **전체 코드**


     
    
 

      class GuGuDan extends React.Component {
        constructor(props) {
          super(props);
          this.state = {
            first: Math.ceil(Math.random() * 9),
            second: Math.ceil(Math.random() * 9),
            value: "",
            result: "",
          };
        }

        render() {
          return (
            <div>
              <div>
                {this.state.first} 곱하기 {this.state.second}는?
              </div>
              <form
                onSubmit={(e) => {
                  e.preventDefault();
                  if (
                    parseInt(value) ===
                    this.state.first * this.state.second
                  ) {
                    this.setState({
                      result: "정답",
                      first: Math.ceil(Math.random() * 9),
                      second: Math.ceil(Math.random() * 9),
                      value: "",
                    });
                  } else {
                    this.setState({
                      result: "땡",
                      value: "",
                    });
                  }
                }}
              >
                <input
                  type="number"
                  value={this.state.value}
                  onChange={(e) => {
                    this.setState({ value: e.target.value });
                  }}
                />
                <button>입력!</button>
              </form>
              <div>{this.state.result}</div>
            </div>
          );
        }
      }
    
      ReactDOM.createRoot(document.querySelector("#root")).render(<GuGuDan />);
    

클래스 메서드

onSubmit = (e) => {
	...
}
...
<form onSubmit={this.onSubmit}>
	...

다음과 같이 함수를 밖으로 빼주어서 코드를 더 직관적이고 깔끔하게 만들어주기. onSubmit, onChange을 밖으로 꺼내자.

**🕹️ 전체 코드**

    
 

      class GuGuDan extends React.Component {
        constructor(props) {
          super(props);
          this.state = {
            first: Math.ceil(Math.random() * 9),
            second: Math.ceil(Math.random() * 9),
            value: "",
            result: "",
          };
        }

        onSubmit = (e) => {
          e.preventDefault();
          if (
            parseInt(this.state.value) ===
            this.state.first * this.state.second
          ) {
            this.setState({
              result: `${this.state.value} 정답`,
              first: Math.ceil(Math.random() * 9),
              second: Math.ceil(Math.random() * 9),
              value: "",
            });
          } else {
            this.setState({
              result: "땡",
              value: "",
            });
          }
        };

        onChange = (e) => {
          this.setState({ value: e.target.value });
        };

        render() {
          return (
            <div>
              <div>
                {this.state.first} 곱하기 {this.state.second}는?
              </div>
              <form onSubmit={this.onSubmit}>
                <input
                  type="number"
                  value={this.state.value}
                  onChange={this.onChange}
                />
                <button>입력!</button>
              </form>
              <div>{this.state.result}</div>
            </div>
          );
        }
      }
    
      ReactDOM.createRoot(document.querySelector("#root")).render(<GuGuDan />);
    

**결과**

  • 정답 시

![03](.\images\03.png)

  • 틀렸을 시

![03](.\images\04.png)

그리고 <GuGuDan/>을 세번 입력해서(<GuGuDan/><GuGuDan/><GuGuDan/>) 세 개의 다른 구구단을 실행할 수도 있다.

5. 함수 컴포넌트(함수형 컴포넌트 아님)

**🕹️ 함수 컴포넌트로 변경한 코드**

function LikeButton() { // 함수형 컴포넌트X 함수 컴포넌트O
	const [liked, setLiked] = React.useState(false);
    
	if (liked) {
		return "You liked this";
	}
    return (
		<button onClick={() => { setLiked(true); }}>Like</button>
	);
}

useState ( = this.state)

  • **구조분해** **(배열)** : useState 함수가 배열을 return함
const liked = result[0]; // 데이터(state)
const setLiked = result[1]; // 데이터를 바꾸는 함수

**🕹️ 화살표 함수로 변경한 코드**

const LikeButton = () => {
	...
}
  • this 쓸 일이 없음(헷갈리지 않음)
  • 코드가 짧아서 간결하고 깔끔함

🍯 Tips

  1. 코드 깔끔하게 짜는 방법
    • **최대한 안짜면 됨**(this안쓰면 arrow function으로 짜기) ➡️ 성능도 좋아짐
  2. 어떤 사이트든지 살펴보면서 state 요소들 찾아보기 (추측. 감🍊 잡기)
  3. 라이브러리의 코드를 상상해보기 (ex. setState, useState, ... )

6. 구구단 리액트로 만들기

**state로 만들 요소** (총 4개)

  • 문제(**5**x**6**=?)에 있는 숫자 2개 ➡️ first, second
  • 답 입력하는 input칸 ➡️ value
  • 정답여부(땡, 딩동딩) ➡️ Result
class GuGuDan extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            first: Math.ceil(Math.random() * 9),
            second: Math.ceil(Math.random() * 9),
            value: "",
            result: "",
        };
    }

    render() {
        return (
            <div>
                <div>
                    {this.state.first} 곱하기 {this.state.second}는?
                </div>
                <form>
                    <input
                        type="number"
                        value={this.state.value}
                        />
                    <button>입력!</button>
                </form>
                <div>{this.state.result}</div>
            </div>
        );
    }
}

코드에 숫자를 입력하면 input에 value가 변경되지 않는다. 변경되기 위해선 <input>에 onChange이벤트를 추가해야 한다(onClick, onChange, onSubmit, onInput, onLoad, ...).

<input
    type="number"
    value={this.state.value}
    onChange={(e) => { this.setState({ value: e.target.value }); }}
/>

그리고 form태그에 결과값을 전송해주는 **onSubmit**을 추가해야 한다.

<form onSubmit = {(e) => {
    e.preventDefault();
    if (parseInt(value) === this.state.first * this.state.second) {
        this.setState({
            result: "정답",
            first: Math.ceil(Math.random() * 9),
            second: Math.ceil(Math.random() * 9),
            value: "",
        });
    } else {
        this.setState({
            result: "땡",
            value: "",
        });
    }
}}/>

🕹️ **전체 코드**


     
    
 

      class GuGuDan extends React.Component {
        constructor(props) {
          super(props);
          this.state = {
            first: Math.ceil(Math.random() * 9),
            second: Math.ceil(Math.random() * 9),
            value: "",
            result: "",
          };
        }

        render() {
          return (
            <div>
              <div>
                {this.state.first} 곱하기 {this.state.second}는?
              </div>
              <form
                onSubmit={(e) => {
                  e.preventDefault();
                  if (
                    parseInt(value) ===
                    this.state.first * this.state.second
                  ) {
                    this.setState({
                      result: "정답",
                      first: Math.ceil(Math.random() * 9),
                      second: Math.ceil(Math.random() * 9),
                      value: "",
                    });
                  } else {
                    this.setState({
                      result: "땡",
                      value: "",
                    });
                  }
                }}
              >
                <input
                  type="number"
                  value={this.state.value}
                  onChange={(e) => {
                    this.setState({ value: e.target.value });
                  }}
                />
                <button>입력!</button>
              </form>
              <div>{this.state.result}</div>
            </div>
          );
        }
      }
    
      ReactDOM.createRoot(document.querySelector("#root")).render(<GuGuDan />);
    

클래스 메서드

onSubmit = (e) => {
	...
}
...
<form onSubmit={this.onSubmit}>
	...

다음과 같이 함수를 밖으로 빼주어서 코드를 더 직관적이고 깔끔하게 만들어주기. onSubmit, onChange을 밖으로 꺼내자.

**🕹️ 전체 코드**

    
 

      class GuGuDan extends React.Component {
        constructor(props) {
          super(props);
          this.state = {
            first: Math.ceil(Math.random() * 9),
            second: Math.ceil(Math.random() * 9),
            value: "",
            result: "",
          };
        }

        onSubmit = (e) => {
          e.preventDefault();
          if (
            parseInt(this.state.value) ===
            this.state.first * this.state.second
          ) {
            this.setState({
              result: `${this.state.value} 정답`,
              first: Math.ceil(Math.random() * 9),
              second: Math.ceil(Math.random() * 9),
              value: "",
            });
          } else {
            this.setState({
              result: "땡",
              value: "",
            });
          }
        };

        onChange = (e) => {
          this.setState({ value: e.target.value });
        };

        render() {
          return (
            <div>
              <div>
                {this.state.first} 곱하기 {this.state.second}는?
              </div>
              <form onSubmit={this.onSubmit}>
                <input
                  type="number"
                  value={this.state.value}
                  onChange={this.onChange}
                />
                <button>입력!</button>
              </form>
              <div>{this.state.result}</div>
            </div>
          );
        }
      }
    
      ReactDOM.createRoot(document.querySelector("#root")).render(<GuGuDan />);
    

**결과**

  • 정답 시

![03](.\images\03.png)

  • 틀렸을 시

![03](.\images\04.png)

Fragment와 기타 팁들

🍯 return에서 하나의 부모 <code><div></code>로 감싸주어야 하는 이유는 뭘까? 쓸데도 없는데.

render() {
    return (
        <div>
            <div>
            	{this.state.first} 곱하기 {this.state.second}는?
            </div>
                <form onSubmit={this.onSubmit}>
                    <input
                    type="number"
                    value={this.state.value}
                    onChange={this.onChange}
                    />
                    <button>입력!</button>
                </form>
            <div>{this.state.result}</div>
        </div>
    );
}

Elements에도 <div>태그가 쓸데없게 나온다.

![03](.\images\05.png)

상단에 <div>태그로 컴포넌트들을 감싸줘야하는게 리액트의 예전 단점이었다. 그런데 현재는 개선되어서 **빈 태그<code><></></code>**를 사용하면 된다. 그러면 Elements에도 <code><div></code>가 뜨지 않는다.

  • 하지만 **Babel을 설치하지 않은 상태에서 빈 태그를 쓰면 에러가 뜬다**. (지원안함)
  • 따라서 빈 태그 대신 **<React.Fragment>** 태그를 사용해야 한다.
render() {
    return (
    <React.Fragment>
        <div>
        	{this.state.first} 곱하기 {this.state.second}는?
        </div>
        <form onSubmit={this.onSubmit}>
            <input
                type="number"
                value={this.state.value}
                onChange={this.onChange}
            />
            <button>입력!</button>
        </form>
        <div>{this.state.result}</div>
    </React.Fragment>
    );
}

🍯() 그룹 우선 연산자

  • 5 * ( 2 + 3 ) 와 같이 **우선 순위 높이기 위한 목적이 아니라면 다 아무 의미가 없음**.
  • return **(**...**)** 에서 사용하는 것도 그냥 보기좋기 위해 쓰는 것.

🍯 form이 없는 경우

  • **<code><button type="submit"></code>**로 작성하면 됨

🍯 직접 만들어준 함수는 화살표 함수로 작성하기

  • 그냥 함수와 화살표 함수는 this의 의미가 다르기 때문이다(this.setState를 써야하기 때문).

함수형 setState

  • 그냥 this.state로 작성하면 변경되기 전의 state인지, 혹은 변경된 후의 state 값인지 헷갈릴 수 있다(상태가 꼬일 수도 있음).
  • 이를 위해 API가 있다!!
  • **setState 안에 변경된 state 값을 return하는 함수를 작성**하면 됨
this.setState(() => {
    return {
        result: `${this.state.value} 정답`,
        first: Math.ceil(Math.random() * 9),
        second: Math.ceil(Math.random() * 9),
        value: "",
    };
});
  • 사용법
    • **prevState(예전 상태값)**를 작성해주자
this.setState((prevState) => {
    return {
        result: `${prevState.value} 정답`,
        first: Math.ceil(Math.random() * 9),
        second: Math.ceil(Math.random() * 9),
        value: "",
    };
});

만약 버튼을 누르면 1씩 오르는 '카운터'가 3개 있다고 가정하자. 그러면 아래와 같이 작성할거다.

this.setState({
	value: this.state.value + 1
});
this.setState({
	value: this.state.value + 1
});
this.setState({
	value: this.state.value + 1
});

그러면 (새로운 value) == (기존 value + 3) 일 것으로 예상하지만, 비동기이기 때문에 기존 value + 1이 될 수도 있다. 그래서 예전 state 값으로 새로운 state값을 만들어 줄 때에는 다음과 같이 return해주는 함수로 작성해주도록 한다.

this.setState((prevState) => {
	return {
        value: prevState.value + 1
    };
});
this.setState((prevState) => {
	return {
        value: prevState.value + 1
    };
});
this.setState((prevState) => {
	return {
        value: prevState.value + 1
    };
});

**✨즉, setState 안에 this.state가 들어가면 함수를 작성하도록 하자!**

ref (reference)

목적

'입력!' 버튼을 누를 때마다 input에 자동으로 포커싱 주기

방법

방법 1. document

document.querySelector('input').focus	

이렇게도 할 순 있지만, React가 제공하는 방법으로 해보자.

방법 2. ref

ref: 요소 선택하기

(1) GuGuDan 클래스 내에 input 선언

input;

(2) input 태그에 ref 작성

 <input
    ref={(c) => { this.input = c; }} ⬅️					
    type="number"
    value={this.state.value}
    onChange={this.onChange}
/>

(3) onSubmit의 끝 부분에 this.input.focus() 작성

onSubmit = (e) => {
    e.preventDefault();
    if (
        parseInt(this.state.value) ===
        this.state.first * this.state.second
    ) {
        this.setState((prevState) => {
            return {
                result: `${prevState.value} 정답`,
                first: Math.ceil(Math.random() * 9),
                second: Math.ceil(Math.random() * 9),
                value: "",
            };
        });
        this.input.focus(); ⬅️
    } else {
        this.setState({
            result: "땡",
            value: "",
        });
        this.input.focus(); ⬅️
    }
};

✨ setState를 할 때 render() 함수 전체가 다시 실행된다!

  • 예로 들어서, render할 때마다 10초씩 걸린다면 서비스가 제대로 돌아가지 않는다.
  • **성능 최적화**를 생각해서 render 내에 코드를 작성하자!
  • **태그 안의 함수(onSubmit, onChange)를 따로 밖으로 뺀 이유도 render가 될 때마다 함수가 다시 새로 생성되기 때문**이다(render는 생각보다 자주 실행된다).
반응형