[React] ReactJS로 영화 웹 서비스 만들기 (3) state

hyobbang·2022년 9월 1일
0
post-thumbnail

3-0

state 개념 이해하기 1 - 함수 호출로 리렌더링

  • 안 좋은 방식과 좋은 방식

안 좋은 방식 : 함수 호출로 리렌더링

<!DOCTYPE html>
<body>
    <div id="root"></div>
</body>
    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
        const root = document.getElementById("root");
        let counter = 0;
        function countUp(){
            counter = counter + 1;
            render();
        }
        function render() {
            ReactDOM.render(<Container/>, root);
        }
        const Container = () => (
        <div>
           <h3>Total clicks: {counter}</h3>
           <button onClick={countUp}>Click me</button>
        </div>
        );
        render();
    </script>
</html>

버튼을 클릭할 때마다 클릭 수가 증가하는 간단한 화면을 만들었다.

vanilla js에서는 body와 span이 전부 업데이트된다.
그러나 React js에서는 딱 onClick 이벤트를 준 h3의 일부 영역만 업데이트가 된다!

3-1

state 개념 이해하기 2 - useState() 사용법

<!DOCTYPE html>
<body>
    <div id="root"></div>
</body>
    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
        const root = document.getElementById("root");

        function App(){
            const data = React.useState(0);
            console.log(data)
            return(
                <div>
                    <h3>Total clicks: 0</h3>
                    <button>Click me</button>
                </div>
            );
        }
        ReactDOM.render(<App/>, root);

    </script>
</html>

const data = React.useState(0); 부분이 핵심이다.

useState를 콘솔에 찍어보면 위와 같은 형태의 배열이 나온다.
여기서 data[0]은 let counter = 0; 과 같이 변수를 초기화해준 것과 같고,
data[1]은

function countUp(){
            counter = counter + 1;
            render();
        }

위와 같이 변수의 값을 바꾸는 함수 하나를 정의한 것과 같다.

그 다음엔 useState의 요소들을 간편하게 컨트롤 할 수 있게끔 data 선언부분을
const [counter, modifier] = React.useState(0); 로 변경한다.

<!DOCTYPE html>
<body>
    <div id="root"></div>
</body>
    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
        const root = document.getElementById("root");

        function App(){
            const [counter, modifier] = React.useState(0);
            
            return(
                <div>
                    <h3>Total clicks: 0</h3>
                    <button>Click me</button>
                </div>
            );
        }
        ReactDOM.render(<App/>, root);

    </script>
</html>

여기에서 counter로 변수를 초기화한 건 알겠다.
그런데 왜 굳이 modifier라는 함수를 사용해서 값을 바꿔야할까?
그건 다음 강의에서!

3일차

3-2

state 개념 이해하기 3 - useState() 적용

좋은 방식 : useState()를 사용하여 값 변경된 부분만 자동으로 리렌더링

<!DOCTYPE html>
<body>
    <div id="root"></div>
</body>
    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
        const root = document.getElementById("root");

        function App(){
            const [counter, setCounter] = React.useState(0);
            const onClick = () => {
                setCounter(counter + 1);
            };
            return(
                <div>
                    <h3>Total clicks: {counter}</h3>
                    <button onClick={onClick}>Click me</button>
                </div>
            );
        }
        ReactDOM.render(<App/>, root);

    </script>
</html>

useState()를 완전히 사용한 모습이다.

  • modifier의 기능

앞 수업에서 const [counter, modifier] = React.useState(0); 에서 modifier는 counter, 즉 변수의 값을 변경해주는 역할을 한다고 배웠다.
이때, 굳이 modifier 함수로 값을 변경해주는 이유는 무엇일까?
다름 아닌 리렌더링 때문이다!
3-0 예제에서는 ReactDOM.render(<App/>, root); 을 함수로 만들어서 일일이 호출해주며 리렌더링을 했다.
그러나 여기서는 modifier, 즉 setCounter가 변수가 변경되는 부분만 알아서 리렌더링을 해준다.

3-4

state 개념 이해하기 4 - 변수의 현재값을 보호하며 변경하기

const root = document.getElementById("root");

        function App(){
            const [counter, setCounter] = React.useState(0);
            const onClick = () => {
               // setCounter(counter + 1);
               setCounter((current) => current + 1);
            };
            return(
                <div>
                    <h3>Total clicks: {counter}</h3>
                    <button onClick={onClick}>Click me</button>
                </div>
            );
        }
        ReactDOM.render(<App/>, root);

위의 코드에서 변경된 부분은 modifier에 해당하는 부분이다.
setCounter(counter + 1); 이렇게 단순히 함수 안에 매개변수로 직접 넣어주는 방식에서
setCounter((current) => current + 1); current라는 변수를 선언한 뒤에 수식이 들어갔다.
이 방법은 counter 변수의 변형을 예방하고, 다음 state의 값이 무조건 현재 값을 바탕으로 나오게 된다.

3-5

JSX와 state 활용

  • JSX
    리액트 안에서 사용할 수 있는 문법으로, js에서 html을 작성하듯이 쓸 수 있어 편하다.
    단, 주의할 점이 있다.
function App(){
            return(
                <div>
                    <h1 className="hi">Super Converter</h1>
                    <label htmlFor="minutes">Minutes</label>
                    <input id="minutes" placeholder="Minutes" type="number"></input>
                </div>
            );
        }

보다시피 class는 className으로, for는 htmlFor라고 적어야 무사히 돌아간다.

  • state
function App(){
            const [minutes, setMinutes] = React.useState();
            const onChange = (event) => {
                setMinutes(event.target.value);
            };
            return(
                <div>
                    <h1 className="hi">Super Converter</h1>
                    <label htmlFor="minutes">Minutes</label>
                    <input value={minutes} id="minutes" placeholder="Minutes"
					type="number" onChange={onChange}></input>
                    <h4>You want convert {minutes}</h4>
                </div>
            );
        }

위와 같이 코드를 작성하면 input 칸안에 적은 숫자를 인터렉티브하게 띄울 수 있게 된다.

리액트에서 이와 같이 value값을 컨트롤하려면 state를 사용해야한다.

const [minutes, setMinutes] = React.useState();
const onChange = (event) => {
      setMinutes(event.target.value);
};

위의 경우처럼 먼저 useState 함수를 정의해준 후 변화가 생길 때마다 리렌더링 할 수 있도록 onChange 이벤트를 정의한다.

3-6

시간 변환 프로그램 만들기

function App(){
            const [minutes, setMinutes] = React.useState();
            const onChange = (event) => {
                setMinutes(event.target.value);
            };
            const reset = () => {
                setMinutes(0);
            }

            return(
                <div>
                    <h1 className="hi">Super Converter</h1>
                    <div>
                        <label htmlFor="minutes">Minutes</label>
                        <input value={minutes} id="minutes" placeholder="Minutes" type="number" onChange={onChange}></input>
                    </div>
                    <div>
                        <label htmlFor="hours">Hours</label>
                        <input value={Math.round(minutes / 60)} id="hours" placeholder="Hours" type="number"></input>
                    </div>
                    <button onClick={reset}>Reset</button>
                </div>
            );
        }

해당 프로그램은 분을 시로 변환해주는 프로그램이다.
state를 사용해 minutes에 /60 을 줘서 간단하게 만들었다.
또한, reset 함수도 state를 통해 간단하게 구현할 수 있었다.

3-7

기능 추가하기

<!DOCTYPE html>
<body>
    <div id="root"></div>
</body>
    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
        const root = document.getElementById("root");

        function App(){
            const [amount, setAmount] = React.useState();
            const [flipped, setFlipped] = React.useState(false);
            const onChange = (event) => {
                setAmount(event.target.value);
            };
            const reset = () => setAmount(0);
            
            const onFlip = () => {
                reset();
                setFlipped((current) => !current);
            };
            return(
                <div>
                    <h1 className="hi">Super Converter</h1>
                    <div>
                        <label htmlFor="minutes">Minutes</label>
                        <input value={flipped ? amount * 60 : amount} id="minutes" placeholder="Minutes" type="number" onChange={onChange} disabled={flipped}></input>
                    </div>
                    <div>
                        <label htmlFor="hours">Hours</label>
                        <input value={flipped ? amount : Math.round(amount / 60)} id="hours" placeholder="Hours" type="number" disabled={!flipped} onChange={onChange}></input>
                    </div>
                    <button onClick={reset}>Reset</button>
                    <button onClick={onFlip}>Filp</button>
                </div>
            );
        }

        ReactDOM.render(<App/>, root);

    </script>
</html>

Flip이라는 버튼을 눌렀을 때 시간, 분 변환을 바꿀 수 있으며 결과값이 나오는 부분은 disabled 속성이 적용되도록 기능을 추가했다.
amount와 flipped 두 개의 state를 사용한다.
amount는 input의 value를 컨트롤 하는 데에 사용되고, flipped는 true/false 값을 부여하여 input의 disabled 속성을 컨트롤 하는 데에 사용된다.

value={flipped ? amount * 60 : amount}
value={flipped ? amount : Math.round(amount / 60)}
value 값을 컨트롤하는 부분에서는 삼항연산자가 사용되었는데, 두 가지의 state를 혼합하여 사용하여 state를 좀 더 적극적으로 활용한 부분이라 할 수 있다.

3-9

컴포넌트 활용과 변환 프로그램 실습

  • 전체 코드
<!DOCTYPE html>
<body>
    <div id="root"></div>
</body>
    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
        const root = document.getElementById("root");

        function App(){
            const [index, setIndex] = React.useState("x");
            const onSelect = (event) => {
                setIndex(event.target.value);
            };
            return(
                <div>
                    <h1 className="hi">Super Converter</h1>
                    <select value={index} onChange={onSelect}>
                        <option value="x">select your units</option>
                        <option value="0">Minutes & Hours</option>
                        <option value="1">Km & Miles</option>
                    </select>
                    <hr/>
                    {index === "x" ? "Please select your units" : null}
                    {index === "0" ? <MinutesToHours/> : null}
                    {index === "1" ? <KmToMiles/> : null}
                </div>
            );
        }

        function KmToMiles(){
            const [amount, setAmount] = React.useState();
            const [flipped, setFlipped] = React.useState(false);
            const onChange = (event) => {
                setAmount(event.target.value);
            };
            const reset = () => setAmount(0);
            
            const onFlip = () => {
                reset();
                setFlipped((current) => !current);
            };
            return(
                <div>
                    <div>
                        <label htmlFor="kilometers">Kilometers</label>
                        <input value={flipped ? Math.round(amount / 0.6214) : amount} id="kilometers" placeholder="Kilometers" type="number" onChange={onChange} disabled={flipped}></input>
                    </div>
                    <div>
                        <label htmlFor="miles">Miles</label>
                        <input value={flipped ? amount : Math.round(amount * 0.6214)} id="miles" placeholder="Miles" type="number" disabled={!flipped} onChange={onChange}></input>
                    </div>
                    <button onClick={reset}>Reset</button>
                    <button onClick={onFlip}>Filp</button>
                </div>
            );
        }
        function MinutesToHours(){
            const [amount, setAmount] = React.useState();
            const [flipped, setFlipped] = React.useState(false);
            const onChange = (event) => {
                setAmount(event.target.value);
            };
            const reset = () => setAmount(0);
            
            const onFlip = () => {
                reset();
                setFlipped((current) => !current);
            };
            return(
                <div>
                    <div>
                        <label htmlFor="minutes">Minutes</label>
                        <input value={flipped ? amount * 60 : amount} id="minutes" placeholder="Minutes" type="number" onChange={onChange} disabled={flipped}></input>
                    </div>
                    <div>
                        <label htmlFor="hours">Hours</label>
                        <input value={flipped ? amount : Math.round(amount / 60)} id="hours" placeholder="Hours" type="number" disabled={!flipped} onChange={onChange}></input>
                    </div>
                    <button onClick={reset}>Reset</button>
                    <button onClick={onFlip}>Filp</button>
                </div>
            );
        }
        ReactDOM.render(<App/>, root);

    </script>
</html>
  • 화면

이번 강의는 state 챕터의 마지막 강의다.
여기서는 본격적으로 컴포넌트 단위로 화면을 구성하는 법을 익힌다.
전 강의까지는 App이라는 함수 하나로 화면을 구성했지만, 여기서 App은 KmToMiles 함수와 MinutesToHours 함수를 select 박스를 활용해 띄우는 역할만 한다.

  • App
function App(){
            const [index, setIndex] = React.useState("x");
            const onSelect = (event) => {
                setIndex(event.target.value);
            };
            return(
                <div>
                    <h1 className="hi">Super Converter</h1>
                    <select value={index} onChange={onSelect}>
                        <option value="x">select your units</option>
                        <option value="0">Minutes & Hours</option>
                        <option value="1">Km & Miles</option>
                    </select>
                    <hr/>
                    {index === "x" ? "Please select your units" : null}
                    {index === "0" ? <MinutesToHours/> : null}
                    {index === "1" ? <KmToMiles/> : null}
                </div>
            );
        }

App에서 index라는 state를 추가했다.
index는 <select value={index} onChange={onSelect}> 를 통해 알 수 있듯이 select 박스의 value를 컨트롤하는 역할을 한다.

const onSelect = (event) => {
                setIndex(event.target.value);
            };

modifier에 해당하는 onSelect 함수 부분은 select 박스를 선택 했을 때, 해당 option value가 index값으로 바뀌게 만들고 있다.


const [index, setIndex] = React.useState("x"); 이 코드를 보면 알 수 있듯이, 초기값을 x로 설정하여 첫 번째 select 박스가 Please select your units 을 띄우게 만든다.


{index === "0" ? <MinutesToHours/> : null}
{index === "1" ? <KmToMiles/> : null}

jsx 부분에서는 MinutesToHours와 KmToMiles함수를 적용한 게 보인다.


  • KmToMiles
function KmToMiles(){
            const [amount, setAmount] = React.useState();
            const [flipped, setFlipped] = React.useState(false);
            const onChange = (event) => {
                setAmount(event.target.value);
            };
            const reset = () => setAmount(0);
            
            const onFlip = () => {
                reset();
                setFlipped((current) => !current);
            };
            return(
                <div>
                    <div>
                        <label htmlFor="kilometers">Kilometers</label>
                        <input value={flipped ? Math.round(amount / 0.6214) : amount} id="kilometers" placeholder="Kilometers" type="number" onChange={onChange} disabled={flipped}></input>
                    </div>
                    <div>
                        <label htmlFor="miles">Miles</label>
                        <input value={flipped ? amount : Math.round(amount * 0.6214)} id="miles" placeholder="Miles" type="number" disabled={!flipped} onChange={onChange}></input>
                    </div>
                    <button onClick={reset}>Reset</button>
                    <button onClick={onFlip}>Filp</button>
                </div>
            );
        }

이 함수는 내가 직접 작성한 킬로미터와 마일 변환 프로그램 소스이다.
시간 변환 프로그램과 기본적인 형태는 똑같고, 변수명과 계산식 부분만 변경해주었다.

<input value={flipped ? Math.round(amount / 0.6214) : amount} 
id="kilometers" placeholder="Kilometers" type="number" 
onChange={onChange} disabled={flipped}></input>

킬로미터에서 마일로 변환하는 부분은 나누기 0.6214를 해준 뒤 math.round로 소숫점 반올림을 해주었다.

<input value={flipped ? amount : Math.round(amount * 0.6214)} 
id="miles" placeholder="Miles" type="number" 
disabled={!flipped} onChange={onChange}></input>

반대로 마일에서 킬로미터 변환은 0.6214를 곱해주었다.

profile
매일 따끈따끈한 빵을 굽는 베이커리처럼 코딩하기

0개의 댓글