• [React] State 공부

    2022. 1. 11.

    by. 모모

    해당 게시물은 노마드코더 ReactJS로 영화웹서비스 만들기를 수강하면서 정리한 내용입니다.

    강의는 무려 무료!! 이므로 노마드코어를 검색해서 직접 들어보세요! 😁

    그리고 React를 들어가기 전에 HTML, CSS, Javascript에 대한 학습이 어느정도 되어있어야 합니다.

    무작정 시작하면 동시에 수많은 정보를 학습해야 하니 바닐라JS 부터 차근차근 학습하시길 바랍니다.


    0. 시작하기

    클론코딩을 진행할 예정이므로 저는 visual studio code와 같은 프로그램을 사용하지 않고

    replit 에서 코딩을 진행하고자 합니다. is 도전정신 그리고 콘솔창의 에러범벅을 보고 후회하게 되는데... 

    만약 replit가 아니라면 먼저 React와 React DOM을 import 합시다.

    <script src="https://unpkg.com/react@17.0.2/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.development.js"></script>

    설치가 잘 되었는지는 콘솔창에서 확인할 수 있습니다.

     

     

    replit의 경우에는 이미 import가 되어있어서 따로 진행할 필요가 없습니다.

    import React from 'react'
    import ReactDOM from 'react-dom'

    1. JSX

    javascript의 CreateElement를 대체하기 위해서 사용하는 JSX는 Javascript의 확장언어입니다.

    JSX로 HTML언어를 만들어서 사용하면 굉장히 편리합니다.

    const Title = (
      <h3 id="title" onMouseEnter={() => console.log("mouse enter")}>
        Hello I'm a title
      </h3>
    );
    const Button = (
      <button
        style={{ backgroundColor: "tomato" }}
        onClick={(() => console, log("I'm Clicked"))}
      >
        Click me
      </button>
    );

    이렇게 Title과 Button을 const에 담아주고 필요할때마다 꺼내쓰면 됩니다. 보기에도 굉장히 쉽고 간단해보이죠.

    하지만 브라우저에서 즉시 실행해보면 오류가 발생합니다. 브러우저가 온전히 JSX를 이해하지 못하기 때문이죠.

    브라우저가 JSX를 이해할 수 있도록 설정해주어야 합니다.

    코드를 작성하면 브라우저가 이해할 수 있는 코드로 변환시켜주는 Babel standalone을 이용하였습니다.

    실제로는 굉장히 느리기 때문에 이 방식을 사용하지 않는다고 하네요.

    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/bable">
      // 내부에 javascript코드 넣기
      const container = React.createElement("div", null, [Title, Button]);
      // []안을 Title과 Button으로 변경해주기.
    </script>

    그래도 사용을 하시겠다면 type을 bable로 정의해주는 것을 잊지마세요.

    정의해주지 않으면 새로고침을 해도 제대로 실행되지 않습니다.

     

    기억해야하는 건 컴포넌트의 첫 글자는 반드시 대문자여야 한다는 것입니다.

    만약 전부를 소문자로 작성하면 React와 JSX는 HTML태그라고 인식해버리고 맙니다. 따라서 첫 글자가 대문자로 작성해야 한다는 것은 굉장히 중요합니다.

    또한 직접 만든 요소는 전부 대문자로 적어야 합니다. 만약 HTML 요소라면 소문자로 적어주면 됩니다.


    2. State

    ReactJS는 State가 굉장히 중요합니다.

    일반 javascript로 만든 웹사이트의 경우에는 특정 태그에 있는 데이터를 바꾸기 위해서 부모 태그까지 함께 업데이트를 진행하게 되는데, ReactJS 에서는 UI에서 바뀐 태그 부분만 업데이트를 진행하므로 매우 효율적입니다.

    ReactJS는 컴포넌트를 확인하고 다른 부분을 확인한 후 바뀐 부분만 변경하기 때문에 다시 부모태그들을 생성할 필요가 없는 것입니다.

    const [counter, modifier] = React.useState(0);

    데이터를 업데이트 할때는 useState 함수를 사용해야 합니다.

     

    counter는 data이고 modifier는 data를 업데이트 할 때 (변경할 때) 사용할 modifier 입니다.

    counter를 직접 변경해 주는 것이 아니라 modifier를 이용해 변경한 숫자를 저장하고 꺼내어 사용하는 것입니다.

    import React, { useState } from 'react';
    import './App.css';
    
    function App() {
    
      let [counter, setCounter] = React.useState(0);
      const onclick = () => {
        setCounter(counter + 1);
      }
      return (
        <>
        <div>
          <h3>Total click : {counter}</h3>
          <button onClick={onclick}>Click me</button>
        </div>
        </>
      );
    }
    
    export default App;

    이렇게 클릭을 누르면 변해야 하는 부분만 변하는 것이 바로 ReactJS가 가진 매력입니다.


    2.1 State 실습

    unit conversion app(단위 변환 앱)을 만들어봅시다.

    간단하게 만들어봅시다.

    먼저 일반 html문이 였을 경우 위와 같은 화면을 구성하기 위해서는 다음과 같이 코드를 작성해야합니다.

    <div>
      <h1>Super Converter</h1>
      <label for="minutes">Minutes</label>
      <input id="minutes" placeholder="Minutes" type="number" />
      <label id="hours">Hours</label>
      <input id="hours" placeholder="Hours" type="number" />
    </div>

    하지만 ReactJS 에서는 JSX를 사용한다는것을 명심하세요.

    JSX에서는 몇가지의 용어를 사용할 수 없습니다. 예를 들어 class같은 경우는 javascript에서 이미 선점하고 있는 언어이므로 사용할 수가 없습니다. for도 마찬가지죠.

    <div>
      <h1>Super Converter</h1>
      <label htmlFor="minutes">Minutes</label>
      <input id="minutes" placeholder="Minutes" type="number" />
      <label htmlFor="hours">Hours</label>
      <input id="hours" placeholder="Hours" type="number" />
    </div>

    JSX에서 html의 for를 사용하고 싶다면 htmlFor라고 사용해야만 합니다.

    이제 입력을 받을때마다 console창에 숫자가 변할 수 있도록 코드를 짜봅시다.

    function App() {
    
      const [minutes, setMinutes] = React.useState();
      const onChange = () => {
        console.log("somebody wrote");
      }
      return (
        <>
          <div>
            <h1>Super Converter</h1>
            <label htmlFor="minutes">Minutes</label>
            <input 
              value={minutes} 
              id="minutes"
              placeholder="Minutes" 
              type="number"
              onChange={onChange}
            />
            <label htmlFor="hours">Hours</label>
            <input id="hours" placeholder="Hours" type="number" />
          </div>
        </>
      );
    }

    minutes의 input에 특정 값을 입력하게 되면 onChange가 실행되고 "somebody wrote"가 console창에 출력됩니다.

    하지만 우리가 필요한건 input값에 들어있는 데이터 값입니다.

    ReactJS는 Javascript랑 굉장히 비슷합니다. input에서 변하는 것을 알고싶다면 event를 받아와서 출력시키면 되겠지요.

    onChange의 ( )에 event를 입력하고, 받아온 event를 콘솔창에 출력시켜봅시다.

    const onChange = (event) => {
      console.log(event);
    }

    console창을 확인해보면 SyntheticBaseEvent(합성 기본 이벤트)가 뜨는 것을 확인할 수 있습니다. ReactJS가 가짜 event를 발생시키기 때문입니다. native event를 얻고 싶다면 synthetic base event 내부에서 nativeEvent를 찾으면 됩니다.

    nativeEvent에서 target을 찾습니다. target은 방금 바뀐 input을 의미합니다.

    그리고 target의 value에 제가 input 했던 값이 들어있음을 확인할 수 있습니다.

    저는 input에 숫자 7이 넣어져있는 상황이지만 만약 다른 숫자가 들어가 있다면 value의 값도 바뀌어 있을 것입니다.

    즉, input에 있는 값을 불러오기 위해서는 event.target.value 순서로 불러오면 되는 것입니다.

    const onChange = (event) => {
      console.log(event.target.value);
    }

    이게 바로 ReactJS 에서 form을 다루는 방법입니다. 이제 value를 가지고 왔으니 해당 data를 변경할 수 있는 함수를 만들어봅시다.

    onChange로 data가 호출되면 minutes data를 변경 해봅시다. 아래와 같이 setMinutes에 넣도록 하겠습니다.

    const onChange = (event) => {
      setMinutes(event.target.value);
    }

    실제로 minutes의 값이 잘 바뀌고 있는지 확인해봅시다.

        (...)
        <h4>You want to convert {minutes}</h4>
        <label htmlFor="hours">Hours</label>
        <input id="hours" placeholder="Hours" type="number" />
        </div>
      </>
    );

    창이 작은 자의 폐해

    Minutes input안의 숫자를 바꿔보세요. 숫자에 맞춰서 h4태그에 들어있는 minutes의 수도 변경되고 있나요?

    minutes data가 제대로 잘 변경되고 있음을 확인할 수 있습니다.

    만약 onChange 함수를 지우게 된다면 minutes는 업데이트가 되지 않습니다. 입력은 받지만 숫자가 바뀌지 않게 됩니다.

    실제로 input안의 onChange={onChange}를 지워보고 실행시켜보시기 바랍니다.

     

    Hours는 Minutes에 입력받은 숫자에 따라 계산되도록 코드를 작성해봅시다. 생각만해도 매우 간단하지요.

    1시간은 60분이니 minutes에 60을 나눠주기만 하면 Hours의 값을 얻을 수 있을 겁니다.

    <label htmlFor="hours">Hours</label>
    <input
      value={minutes/60}
      id="hours" 
      placeholder="Hours"
      type="number"
    />

    반올림을 하고 싶다면 Math.round( )를 사용해보세요.

     

    이제 reset을 해줄 버튼을 만들어봅시다. 이 버튼을 누르면 입력했던 숫자가 0로 초기화가 되도록 할 것입니다.

    <button onClick={reset}>Reset</button>

    먼저 button을 만들고 onClick을 하게 되면 reset을 호출하도록 합시다.

    const reset = () => setMinutes(0);

    reset을 하게 되면 setMinutes을 0로 만들어줍시다.

    그리고 실행을 하게 되면 다음과 같이 reset 버튼을 누르면 0로 초기화가 되는 것을 볼 수 있습니다.

     

    반대의 경우는 어떻게 해야할까요? hours를 입력하면 minutes단위로 변환을 해주는건 어떻게 해야할까요?

    즉 지금까지 해왔던 과정을 반대(flip)로 하려는 것인데요.

    flip function 작업을 하기전에 disabled를 이용해 기능 구분을 해주도록 합시다.

    <input
      value={minutes/60}
      id="hours" 
      placeholder="Hours"
      type="number"
      disabled
    />

    disabled 속성을 넣어주면 input에 숫자를 입력할 수 없게 됩니다.

    Fliped 버튼을 만들어서 Minutes → Hours 일때는 Hours를 disabled, Hours → Minutes 일때는 Minutes를 disabled 시키도록 하겠습니다.

    <button onClick={onFlip}>Flip</button>
    const [flipped, setFlipped] = React.useState(false);
    const onFlip = () => setFlipped((current) => !current);

    해당 코드가 무슨 의미를 내포하고 있는것 같나요? flipped에는 useState에 의해 false값이 들어가게 됩니다.

    하지만 Flip버튼을 클릭하면 onFlip가 실행되게 되고 setFlipped의 data가 !data가 되면서 false가 true로 변하게 됩니다. 그리고 다시 클릭하면 true가 false로 변하게 되고요. 이것을 버튼을 클릭할때마다 반복하게 됩니다.

    true와 false로 번갈하가면서 바뀌는것을 이용해 disabled를 제어할 것입니다.

    <input
      value={minutes/60}
      id="hours" 
      placeholder="Hours"
      type="number"
      disabled={flipped === false}
    />

    disabled는 flipped값이 false일때만 실행되게 만듭니다. 그렇다면 minutes는 반대로 true일때만 실행되게 해주면 되겠지요.

    <input 
      value={minutes} 
      id="minutes"
      placeholder="Minutes" 
      type="number"
      onChange={onChange}
      disabled={flipped === true}
    />

    실제로 코드를 실행시켜보면 flip 버튼을 누를때마다 input의 비활성화가 번갈아가면서 발생하는 것을 알 수 있습니다.

    이제 Hours input에 숫자를 입력했을때 Minutes에 변환되어서 나올 수 있도록 합시다.

    그전에 먼저 해야하는 작업이 존재합니다. Hours input 안에 있는 value의 숫자변환함수인데요.

    이게 남아있는다면 우리가 어떤 숫자를 입력하든 그 함수에 의해서 value값이 변하게 될 것입니다.

    따라서 만들었던 flipped를 이용해 특정한 경우에만 해당 함수를 사용할 수 있도록 제어하겠습니다.

    <input
      value={flipped ? minutes : (minutes/60)}
      id="hours" 
      placeholder="Hours"
      type="number"
      disabled={flipped === false}
    />

    조건문 ? True : False 를 이용해서 flipped가 True이고 False일때 각각 어떻게 작동할 것인지를 구분지어주었습니다.

    또한, 이제는 시간을 분으로 바꾸는 기능을 추가할 것이니 minutes가 아닌 amount로 변경해주도록 하겠습니다.

    const [amount, setAmount] = React.useState(0);
    const onChange = (event) => {
      setAmount(event.target.value);
    };
    const reset = () => setAmount(0);
    <input
      value={flipped ? amount : (amount/60)}
      id="hours" 
      placeholder="Hours"
      type="number"
      onChange={onChange}
      disabled={flipped === false}
    />

    또한 value를 입력할때마다 amount의 값을 변경해야 하므로 onChange도 넣어주도록 합시다.

    이렇게 하면 hours값을 변경할때마다 minutes값이 변경되는 것을 확인할 수 있습니다.

    minute input의 내부도 변경을 해주어야겠지요.

    <input 
      value={flipped ? (amount*60) : amount} 
      id="minutes"
      placeholder="Minutes" 
      type="number"
      onChange={onChange}
      disabled={flipped === true}
    />

    하지만 이 코드에는 한가지 문제가 있습니다. flip을 실행시킬때마다 이전에 입력했던 값이 그대로 남아있다는 것이지요.

    그래서 이전에 입력했던 value값이 flip 버튼을 누르면 다른 입력값으로 내려오는 문제가 발생합니다.

    이것을 해결하기 위해 onFlip를 실행시킬때 숫자를 초기화시키도록 합시다.

    const onFlip = () => {
      reset();
      setFlipped((current) => !current);
    };

    짜잔  이렇게 시간 단위 바꾸기가 완성되었습니다!


    2.2 Code Challenge

     

    km / mm 변환이 가능한 코드를 만들어 봅시다.

    그 전에 html의 option을 이용해서 해당 원하는 기능만을 뜰 수 있게 구현해보도록 합시다. 선택에 따라 보여지는 화면이 다르게 만들 예정입니다.

    <div>
      <select value={index} onChange={onSelect}>
        <option value="0">Minute & Hours</option>
        <option value="1">Km & Miles</option>
      </select>
    </div>

    그리고 해당 value값을 가져와 사용할 수 있도록 onSelect함수를 만들어주고 value값을 저장하기 위한 state를 만들어줍시다.

    const [index, setIndex] = React.useState(0);
    const onSelect = (event) => {
      setIndex(event.target.value);
    };

    그리고 이전에 만들어 두었던 시간/분 전환 코드는 모두 다른 곳으로 옮기도록 합니다.

    pages 디렉토리를 만들고 그 안에 MinutesToHours.jsx 파일을 생성합시다.

    해당 파일 내부에 시간/분 전환 코드를 작성하도록 합니다.

    import React from 'react';
    
    function MinutesToHours() {
      const [amount, setAmount] = React.useState(0);
      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 === true}
              />
              /*-------------중략----------------*/
          </div>
        </>
      );
    }
    
    export default MinutesToHours;

    해당 파일을 어떻게 불러올 수 있을까요? 바로 import 를 이용하면 됩니다.

    import MinutesToHours from './pages/MinutesToHours';

    import를 이용해 MinutesToHours를 호출해주고,

    return (
      <>
        <MinutesToHours />
      </>
    );

    return 안에 <MinutesToHours />를 넣어주면 호출한 내용들이 들어가 화면에 출력되게 됩니다.

     

    이제 select에서 받아온 option의 value에 따라 다른 화면이 출력되도록 해봅시다.

    {index === "xx" ? "Please select your units" : null}
    {index === "0" ? <MinutesToHours /> : null}
    {index === "1" ? <KmToMiles /> : null}

    { } 인 중괄호로 묶어주면 java문을 사용할 수 있습니다. 이렇게 작성하면 index에 어떤 값이 들어가 있느냐에 따라서 화면을 다르게 출력할 수 있습니다.

    해결이 되었나요? 이제 KmToMiles는 직접 코딩해봅시다.

     

     

     

    [Clone coding] reactjs-state

    A Node.js repl by heyjin281129

    replit.com

     

    댓글

Designed by Nana