차이점은 class보다 hooks가 좀 더 간결하다.

 

리렌더 시 차이점

hooks를 사용하면 해당 함수 자체가 재실행된다.

const Gugudan = () => {
	...
}

Gugudan() 함수가 재실행 된다고 생각하면 됨. 하지만 class 컴포넌트를 사용하면 render()함수만 재실행 되었다.

그래서 리렌더 시 hooks를 사용한 함수 컴포넌트가 좀 더 느릴 순 있다.

 

JSX에서 HTML 태그와 차이점

class -> className

for(label) -> htmlFor

 

setState가 여러 번 동시에 일어날 경우?

그렇게되면 리랜더가 여러 번 일어나는 것 아니냐는 걱정 섞인 댓글이 있었다.

하지만 이러한 경우 React가 알아서 setState를 모아서 한번에 처리 후 리랜더는 한번만 일어난다고 한다.

const onSubmit = (e) => {
                e.preventDefault();
                const ans = first * second;
                if(parseInt(value) === ans){
                    setFirst(()=>Math.ceil(Math.random() * 9));
                    setSecond(()=>Math.ceil(Math.random() * 9));
                    setValue(()=>'');
                    setResult(()=>`정답 ${ans}`);
                }else{
                    setResult(() => `땡`);
                    setValue(()=>'');
                }
                inputRef.current.focus();
            }

이렇게 setFirst, setSecond, setValue, setResult 처럼 동시에 state 변경을 여러 번 하면 리랜더가 여러번 일어날 줄 알았지만 React가 setState관련 함수를 모아서 한번에 처리하기 때문에 리랜더는 한번만 일어난다고 한다.

Class 문법 사용하지말라?

React 에서 추천하는 방법이 class 를 사용하지말고 Hooks를 사용하라는 것이다.

하지만 class 문법도 알긴해야한다. 실무에 class로 작성된 코드가 있을 수 있기 때문.

 

함수 컴포넌트

//클래스 컴포넌트
class GuGuDan extends React.Component{
	...
}
//함수 컴포넌트
const GuGuDan = () => {
	return (<div>...</div>);
}

원래 함수 컴포넌트에서는 state와 ref같은 것들을 사용하지 못하였다. 그래서 state, ref같은 것이 필요 없는 컴포넌트에서만 함수 컴포넌트를 사용 했었다고 한다.

하지만 점점 많은 사람들이 함수 컴포넌트에서도 state, ref를 사용할 수 있게 해달라고 요청이 많아지자 React 개발팀에서 함수 컴포넌트에서도 state, ref를 사용할 수 있게 만들어 준 것이 바로 React Hooks이다.

 

React Hooks

함수 컴포넌트에 state, ref, useEffect와 같은 기능이 추가 된 것.

use~ 이렇게 use로 시작하는 것들이 Hooks이다.

클래스 컴포넌트를 사용하는 것보다 코드가 간결하고 단순하다.

 

state 선언하기 변화

//함수 컴포넌트(Hooks 사용)
const [value, setValue] = React.useState('');
const [result, setResult] = React.useState('');

//클래스 컴포넌트
state = {
	value: '',
    result: '',
}

 

setState 함수 변화

//React Hooks 사용
const onChange = (e) => {
	setValue(() => {
    	return e.target.value;
	})
};

//클래스 컴포넌트
onChangeInput = (e) => {
	const {value} = e.target;
    this.setState({value});
    console.log(this.state)
};

 

ref 변화

//React Hooks 사용
const inputRef = React.useRef(null); //선언
inputRef.current.focus(); //함수 내에서 사용

//클래스 컴포넌트
onRefInput = (c) => {this.input = c;} //선언
this.input.focus(); //사용

 

전체코드 (React Hooks 사용)

<script type="text/babel">
        const 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(null);

            const onChange = (e) => {
                setValue(() => {
                    return e.target.value;
                })
            }
            const onSubmit = (e) => {
                e.preventDefault();
                const ans = first * second;
                if(parseInt(value) === ans){
                    setFirst(()=>Math.ceil(Math.random() * 9));
                    setSecond(()=>Math.ceil(Math.random() * 9));
                    setValue(()=>'');
                    setResult(()=>`정답 ${ans}`);
                }else{
                    setResult(() => `땡`);
                    setValue(()=>'');
                }
                inputRef.current.focus();
            }

            return (
                <React.Fragment>
                    <div>{first} 곱하기 {second}는?</div>
                    <form onSubmit={onSubmit}>
                        <input ref={inputRef} onChange={onChange} type="number" value={value}/>
                        <button>입력</button>
                    </form>
                    <div id="result">{result}</div>
                </React.Fragment>
            );
        }
    </script>
    <script type="text/babel">
        ReactDOM.createRoot(document.querySelector('#root')).render(<div><Gugudan/><Gugudan/><Gugudan/></div>);
    </script>

 

ref

현재까지는 state를 바꾸면 React가 알아서 화면을 변경해줬다. 그래서 우리가 직접 DOM을 참조할 필요가 없었지만 만약에 input에 focus를 주고 싶은 경우에는 어떻게 할까?

 

기존 바닐라에서는 아래와 같이 하면 됐다.

document.querySelector("input").focus();

하지만 React에서는 대부분 document.querySelector를 사용하지 않는다.

React가 화면을 컨트롤 할 수 있게 해주는 것이 중요하다. 

최대한 React가 제공해주는 것을 사용하자. 이때는 ref를 사용하면 된다.

 

input;

return (
...
<input ref={(c)=>{this.input = c;}} type="number" />
...
)

input을 선언하고, jsx에서 ref={(c) => {this.input = c; }}

 

input을 사용할 때는 js 부분에서 아래와 같이 사용 가능.

 

this.input.focus();

>>>> 따로 메서드로 빼면 아래와 같이 간편해진다.

onRefInput = (c) => {this.input = c;}

return (
	...
	<input ref={this.onRefInput} type="number" />
    ...
);

 

 

state를 변경했을 때 일어나는 일

setState를 할 때 reder()함수가 재호출되면서 리렌더가 일어난다.

해당 개념이 중요한 이유는 나중에 렌더를 너무 많이 하면 느려진다. render에 10초 걸리는 작업이 있다고 하면 state를 변경할 떄마다 10초씩 걸린다고 생각하면 된다.

 jsx부분의 onChange, onSubmit, ref 이런 부분도 모두 render()함수 위의 js 부분에 선언하는 것이 좋은 이유이다. 만약에 jsx 부분에 inline으로 onChange, onSubmit과 같은 함수를 작성해버리면 state를 변경할 때마다 똑같은 함수가 계속 생기게 된다. (state를 변경하면 render()가 재호출되기 때문)

함수형 setState

setState에 함수를 넣고 그 안에서 새로운 state를 리턴을 하는 것.

함수형 setState 구조

this.setState((prevState)=>{
	return {
    	first: Math.ceil(Math.random() * 9),
        second: Math.ceil(Math.random() * 9),
        value:'',
        result : '정답',
        prevAns : prevState.value,
	}
});

함수형 setState와 기존 setState의 차이점

예전 state값을 다음 state 값에 활용 가능.

기존 state 바꾸는 방식으로 하면 비동기 문제가 생긴다.

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

이렇게 setState를 3번 호출 하면 this.state.value에 +3이 된 값이 들어있어야 하는데, setState가 비동기라 바로 적용되지 않아서 +1만 되는 경우가 생길 가능성이 크다. 

그렇기 때문에 setState를 할 때는 그냥 함수형으로 사용하자. (특히, 기존 state값을 활용할 경우에는 꼭)

Fragment

React에서 화면을 최종적으로 반환할 때는 딱 하나의 태그만 반환해야한다. (부모 태그가 1개여야한다는 것)

그래서 쓸데없는 <div></div> 로 감싸줘야하는데 이것이 css를 적용할 때 방해가 될 때가 많다.

 

그럴 때 사용하는 것이 빈 껍데기 태그인 <></> Fragment를 사용하는 것이 좋다. 

(이것이 에러나면 <React.Fragment></ React.Fragment>로 감싸주면 된다.)

return (
	<React.Fragment>
    	~
	</React.Fragment>
);

 

클래스 컴포넌트에서 주의점

함수 메서드를 따로 만들어서 이벤트 리스너와 연결시킬 때는 화살표 함수를 사용해라. function을 사용하면 this가 달라져서 작동하지 않는다. 

실무에서는 constructor를 사용하지않고 바로 state= {..} 이렇게 선언한다.

class GuGuDan extends React.Component{
            state = {
                first: Math.ceil(Math.random() * 9),
                second: Math.ceil(Math.random() * 9),
                value:'',
                result : '',
            }

            onChangeInput = (e) => {
                this.setState({value: e.target.value});
                console.log(this.state)
            };

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

            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.onChangeInput}/>
                            <button>입력!</button>
                        </form>
                        <div>{this.state.result}</div>
                    </React.Fragment>
                );
            }
        }

 

어떤 데이터를 state로 설정하는 능력이 중요하다.

보통은 바뀌는 데이터를 state로 설정하는 것 같다. 

> state는 화면과 연결된 데이터 라고 생각하면 편하다.

> state가 바뀌면 화면이 리렌더되면서 화면도 바뀌게 된다.

 

<input /> 주의점

value도 변하는 값이기 때문에 state로 관리해준다.

value가 state이기 때문에 사용자가 키보드로 값을 입력하면? -> value가 바뀐다 -> 화면이 value와 같이 바뀐다.

라는 로직으로 동작해야하기 때문에 onChange이벤트가 발생할 떄마다 state(value)를 바꿔줘야 화면에 반영이 된다는 것을 주의해야한다.

class를 안쓰는 이유

class는 this문제가 있었다. constructor를 무조건 사용해야한다던가 bind를 해야했었다. (화살표 함수를 써도 되긴했음)

아무튼 그래서 function으로 되돌아 갔다고한다.

그리고 복잡하고 코드가 길어짐!

<script type="text/babel">
    'use strict';

    const e = React.createElement;

    class LikeButton extends React.Component {
        constructor(props) {
            super(props);
            this.state = {liked: false};
        }

        render() {
            if (this.state.liked) {
                return 'You liked this.';
            }

            //return e('button', {onClick: () => this.setState({liked: true})}, 'Like');
            return (
                <button onClick={()=>this.setState({liked:true})}>
                    Like
                </button>
            );
        }
    }
</script>
<script type="text/babel">
    //ReactDOM.render(<LikeButton/>, document.querySelector('#root')) //17버전 코드 (18버전에서도 잘 돌아감)
    ReactDOM.createRoot(document.querySelector('#root')).render(<LikeButton/>);
</script>

 

함수 컴포넌트

함수 컴포넌트를 사용하면 코드가 매우 간결해지는 효과가 있음.

<script type="text/babel">
    'use strict';

    function LikeButton(){ //함수 컴포넌트
        const [liked, setLiked] = React.useState(false);
        const onClicked = () => {
            setLiked(true);
        }

        if(liked){
            return "You liked this";
        }
        return (
            <button onClick={onClicked}>Like</button>
        )
    }
</script>
<script type="text/babel">
    //ReactDOM.render(<LikeButton/>, document.querySelector('#root')) //17버전 코드 (18버전에서도 잘 돌아감)
    ReactDOM.createRoot(document.querySelector('#root')).render(<LikeButton/>);
</script>

 

라이브러리를 혼자서 재구성해보는 능력 (혹은 라이브러리 안쪽을 상상할 수 있는 능력)은 매우 중요하므로 연습하면 매우 좋다고함.

 

아직까지 클래스 문법을 사용하는 곳이 있을 것이기 때문에 클래스 문법을 보여주고 함수 컴포넌트를 학습하자.

객체를 함부로 바꾸지 마라!

React에서의 불변성은 매우 중요하다.

혹시 Array의 메서드 중에 

pop,push,splice,shift,unshift / concat, slice 의 차이점을 아는가?

> 불변성이 차이점이다. pop, push ~ : 원본을 변경한다. concat, slice는 새로운 배열을 만들어낸다.

React에서는 concat, slice처럼 새로운 배열을 만들어내는 것들을 굉장히 많이 사용한다. 원본을 수정하는 것들은 기존 객체를 변경하기 때문! 

 

 

 

클래스 문법으로 React 코딩하는 것을 한번 보여준다.
클래스 문법은 진짜 복잡하다.
그래서 JSX가 생김.

class 문법으로 코딩하기 (너무 복잡함)

return e('button', {onClick: () => this.setState({liked: true})}, 'Like');


그래서 JSX가 생김 (좀 더 인간 친화적으로 바뀜)

jsx : javascript + xml

return (
<button onClick={()=>this.setState({liked:true})}>
	Like
</button>
);

 

Babel

하지만 js에서 < 이런 태그 같은거 해석하지 못해서 babel 이라는 라이브러리를 사용해야한다. babel이 <button> 이러한

jsx 태그를 React.createElement 같은 것으로 바꿔준다.

<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>

 

바벨을 사용하는 가장 쉬운 방법은 위의 코드를 붙여넣기하고, 바벨이 바꿔주길 원하는 부분의 <script>를

<script type="text/babel">로 바꿔놓으면 된다.

 

React 규칙

1. 컴포넌트는 대문자로 시작

2. jsx에서 자바스크립트 코드를 사용하기 위해서는 { } 사용 (jsx에서 객체를 생성하려면 {{a:'b', b:'c'}} 이런식으로 해야한다.)

3. jsx에서 html 태그를 사용하기 위해서는 소문자로 사용 (닫는 태그 필수)

4. jsx에서는 if와 for를 사용하지 못한다. if 대신에 삼항연산자, for 대신에 Array.map 을 사용한다.

5. jsx의 return() 에서 최상위 태그는 1개만 존재해야한다. (<> ~ </> Fragment라는 태그를 많이 사용함.)

 

 

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)

Maven이나 Gradle에 DB Connector 을 추가해두면 datasource.url과 관련 정보들을 써야 하는데.. 아직 확정 되지 않은 정보들이라면 위의 사진처럼 exclude를 추가하면 해결!

+ Recent posts