패스트 캠퍼스 MGS 3기 - 5월 3일(React 맛보기)

JY·2022년 5월 3일
0

들어가기 전..


라이브러리 vs 프레임워크

라이브러리프레임워크
개발 편의를 위한 도구의 모음기반 구조까지 잡혀있음
공구공장
ReactVue.js, Angular

React 생태계

생태계가 풍부하다!
즉, 구글링 하기 좋다. 해당 기술에 대한 관심도 / 실제 사용 빈도 / 사용자 수가 많다.

  • 관련 라이브러리가 많다.
  • 문제를 해결할 방법을 찾기 쉽다. (stackover flow 등)
  • 나와 같은 고민을 하는, 했던 사람이 많다.
  • 실무에서 사용할 확률이 높다.


React 맛보기

1. DOM


Document Object Model

  • Element: 우리 눈에 보여지는 것.
  • DOM: 브라우저가 이해하는 Element의 원형이다.

🤔 CodeSandBox (https://codesandbox.io/)
프론트엔드 코드를 작성하고 이것저것 시도해볼 수 있는 모래상자이다.
리액트 등 다양한 환경에 대한 기본 설정이 되어있다.


DOM을 사용한 Element 생성하기

Static 환경에서 element를 생성하는 코드를 작성했다.

이제 위에서 작성한 코드를 CDN을 통해 리액트를 사용해서 바꾸어보자.
React-dom의 render 함수는 appendChild와 같은 역할을 한다.

🤔 CDN (https://ko.reactjs.org/docs/cdn-links.html)
웹에서 사용되는 다양한 컨텐츠(리소스)를 저장하여 제공하는 시스템이다.


정리


2. JSX와 Babel


JSX와 Babel을 사용한 Element 생성하기

JSX와 Babel을 사용해서 위에서 작성한 element를 생성하는 코드를 더 단순하게 바꿀 수 있다.

정리

  1. React.createElement 표현식을 더 편하게 사용하기 위한 'JSX'. 이는 JS도, HTML도 아닌 리액트에서 제공하는 제 3의 표현법이다.
  2. 이를 브라우저에서 JS처럼 인지하도록 만드는 것이 'Babel'이라는 컴파일러이다.

🤔 spread(...)
객체(props)에 있는 요소를 각각 분해해서 보여주는 것이다.

<div id="root"></div>
<script type="text/babel">
  const rootElement = document.getElementById("root");
  const text = "Hello, world!";
  const titleClassName = "title123"
  const props = { className: titleClassName, children: text };
  const customH1 = <h1 {...props} />;
  const element = customH1;
  ReactDOM.render(element, rootElement);
</script>

위의 코드에서 다음 두 코드는 같은 표현이다. ...을 사용해서 더 간단하게 표현했다.

const customH1 = ( { <h1 className={props.className} children={props.children} /> );
const customH1 = <h1 {...props} />;

3. 멀티 Element 생성하기


지금까지 root Element에 하나의 Element 밖에 넣지 못했다. 어떻게 다중 Element를 넣을 수 있을까?

  1. root Element에 투입될 부모 Element를 하나 만들고 그곳에 여러 Element를 넣었다.
  2. 부모 Element를 없애는 방법은 없을까?
  3. React.Fragment를 사용하자!

🤔 React.Frament / <></>
자식 요소들을 묶어주는 역할만 하고, 실제로 그릴 때는 아무 내용도 남지 않는다.


👩‍💻 예제 1

React.Fragment 또는 <></>를 통해서 여러 가지 Element를 주입할 수 있다.
다음 두 코드는 동일한 결과가 출력된다.

<div id="root"></div>
<script type="text/babel">
  const rootElement = document.getElementById("root");
  const element = (
    <React.Fragment
                    children={[
                    React.createElement("h1", null, "Hi"),
                    React.createElement("h3", null, "Bye"),
                    React.createElement("h5", null, "Children")
                    ]}
    />
  );
  ReactDOM.render(element, rootElement);
</script>
<div id="root"></div>
<script type="text/babel">
  const rootElement = document.getElementById("root");
  const element = (
    <>
      <h1>Hi</h1>
      <h3>Bye</h3>
      <h5>Children</h5>
    </>
  );
  ReactDOM.render(element, rootElement);
</script>

👉 실행 결과

정리


4. Element 찍어내기


  1. 함수를 이용하여 Element를 반복해서 찍어낼 수 있다.
  2. 이 함수가 JSX를 리턴하면 그것을 Custom Element라고 부른다.
  3. Children은 제한이 없다.

🤔 Custom Element
Upper case로 작성해야 한다. 소문자로 작성하게 되면 HTML에서 이미 사용되고 있는 Element들과 충돌을 일으킬 수 있는 확률이 있고, 리액트가 인지하기 어려울 수 있다.


👩‍💻 예제 1

함수를 사용해서 여러 개의 Element를 찍어낼 수 있다.

<div id="root"></div>
<script type="text/babel">
  const rootElement = document.getElementById("root");

  const paint = () => (
    <>
      <h1>Hi</h1>
      <h3>Bye</h3>
    </>
  );
  const element = (
    <>
      {paint()}
      {paint()}
      {paint()}
    </>
  );
  ReactDOM.render(element, rootElement);
</script>

👉 실행 결과

👩‍💻 예제 2

위의 코드가 '이게 뭐가 편하지?' 라고 생각할 수도 있는데, 이는 Element가 계속 바뀌는 상황이 왔을 때 편리하게 작용한다.

<div id="root"></div>
<script type="text/babel">
  const rootElement = document.getElementById("root");

  const paint = (title, description) => (
    <>
      <h1>{title}</h1>
      <h3>{description}</h3>
    </>
  );
  const element = (
    <>
      {paint("Good", "good")}
      {paint("Bad", "bad")}
      {paint("Soso", "soso")}
    </>
  );
  ReactDOM.render(element, rootElement);
</script>

👉 실행 결과

다음처럼 작성할 수도 있다.

<div id="root"></div>
<script type="text/babel">
  const rootElement = document.getElementById("root");

  // props를 인자로 넘긴 것.
  // Uppercase로 작성해야 함.
  const Paint = ({ title, description }) => (
    <>
      <h1>{title}</h1>
      <h3>{description}</h3>
    </>
  );
  const element = (
    <>
      <Paint title="Good" description="good" />
      <Paint title="Bad" description="bad" />
      <Paint title="Soso" description="soso" />
    </>
  );
  ReactDOM.render(element, rootElement);
</script>

정리

Children 제한이 없다는 것은 자기가 자기 자신을 계속 찍어낼 수도, 새로운 Children을 찍어낼 수도 있다는 것이다. 이는 확장성이 좋다는 의미이다!

5. JS와 JSX 섞어쓰기


👩‍💻 예제 1

첫 글자가 대문자면 큰 글자로, 소문자면 작은 글자로 출력하는 예제이다.

  • const Text 전체는 JS지만 JSX를 리턴한다.
  • if(...)는 JS
  • return은 JS
  • <h1>, </h1>은 JSX
  • {text}는 JS
<div id="root"></div>
<script type="text/babel">
  const rootElement = document.getElementById("root");

  const Text = ({ text }) => {
    if (text.charAt(0) === text.charAt(0).toUpperCase()) {
    return (
      <h1>
        {text}
      </h1>
    );
    } else {
      return <h3>{text}</h3>;
    }
  };
  const element = (
    <>
      <Text text="Text" />
      <Text text="text" />
    </>
  );

  ReactDOM.render(element, rootElement);
</script>

👉 실행 결과

👩‍💻 예제 2

숫자가 짝수이면 큰 글자로, 홀수이면 작은 글자로 출력하는 예제이다.

  • return ... ?은 JS
  • <h1>, </h1>은 JSX
  • {number}는 JS
  • :는 JS
<div id="root"></div>
<script type="text/babel">
  const rootElement = document.getElementById("root");

  function Number({ number }) {
    return number % 2 === 0 ? <h1>{number}</h1> : <h3>{number}</h3>;
  }

  const element = (
    <>
      <Number number={1} />
      <Number number={2} />
      <Number number={3} />
      <Number number={4} />
    </>
  );

  ReactDOM.render(element, rootElement);
</script>


👩‍💻 예제 3

selected을 사용해서 1~10 사이의 숫자 중 3을 선택해서 큰 글자로 출력하는 예제이다.

<div id="root"></div>
<script type="text/babel">
  const rootElement = document.getElementById("root");

  function Number({ number, selected }) {
    return selected ? <h1>{number}</h1> : <h3>{number}</h3>;
  }

  const element = (
    <>
      {[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((number) => (
      <Number number={number} selected={number === 3} />
      ))}
    </>
  );

  ReactDOM.render(element, rootElement);
</script>

👉 실행 결과

정리

리액트에서 Element를 그릴 때 자바스크립트의 장점을 사용할 수 있다.

6. 리액트와 리렌더링


👩‍💻 예제 1 in Vanilla JS

바닐라 JS를 사용해서 1초마다 랜덤한 숫자가 생성되는 버튼을 만드는 예제이다.

<div id="root"></div>
<script>
  const rootElement = document.getElementById("root");

  function random() {
    const number = Math.floor(Math.random() * (10 - 1) + 1);

    const element = `
      <button>${number}</button>
    `;
    rootElement.innerHTML = element;
  }

  setInterval(random, 1000);
</script>

👉 실행 결과
매번 버튼 자체를 새로 만든다.
그래서 포커싱이 유지되지 않고 새로 만들 때마다 포커싱이 사라진다.

👩‍💻 예제 2 in React

이번에는 리액트로 1초마다 랜덤한 숫자가 생성되는 버튼을 만드는 예제이다.

<div id="root"></div>
<script type="text/babel">
  const rootElement = document.getElementById("root");

  function random() {
    const number = Math.floor(Math.random() * (10 - 1) + 1);

    const element = <button>{number}</button>;
    ReactDOM.render(element, rootElement);
  }

  setInterval(random, 1000);
</script>

👉 실행 결과
언뜻 보면 바닐라 JS와 별다른 차이가 없어보이지만, 버튼을 선택했을 때 차이를 알 수 있다.
리액트로 구현한 코드는 버튼에서 변경되는 곳만 바꾼다. 즉, 버튼 전체를 바꾸는 것이 아닌 안의 내용만 바꾸는 것이다.
그래서 포커싱이 유지되는 것을 볼 수 있다.

정리

리액트 엘리먼트는 불변객체(immutable)이다. 불변객체란 변하지 않는 객체를 뜻한다. 우리는 그저 ReactDOM.render(element, rooElement);로 전달을 할 뿐, 변경 판단 및 반영은 리액트가 알아서 하는 것이다.

엘리먼트 타입이 바뀌면 이전 엘리먼트는 버리고 새로 그린다. 만약, 엘리먼트 타입이 같다면 key를 먼저 비교하고, props를 비교해서 변경사항을 반영한다.

DOM은 화면에 그려지거나 보여지는 것이 아닌 자체 논리적으로 브라우저가 미리 인지하고 있는 트리 개념이다. 그렇다면 리액트는 DOM을 어떻게 비교할까?
리액트는 'Virtual DOM'이라는 것을 가지고 있다. 이를 가지고 이전 가상 돔과 새로 들어온 가상 돔을 비교해서 업데이트를 하는 것이다.
돔에 계속 접근하는 것이 아닌 가상 돔을 가지고 변화를 반영한다.

7. 이벤트 핸들러


https://www.w3schools.com/js/js_events.asp

  • in Vanilla JS
  • in React (Camel case)

👩‍💻 예제 1

버튼을 클릭하면 'pressed' 알림창이 나타나고, 마우스를 떼면 'bye' 알림창이 나타나는 예제이다.

<script type="text/babel">
  const rootElement = document.getElementById("root");
  const handleClick = () => alert("pressed");
  const handleMouseOut = () => alert("bye");

  const element = (
    <button onClick={handleClick} onMouseOut={handleMouseOut}>
      Press
    </button>
  );
  ReactDOM.render(element, rootElement);
</script>

👉 실행 결과

👩‍💻 예제 2

간단한 검색창을 만드는 예제이다.

<script type="text/babel">
  const rootElement = document.getElementById("root");

  const state = { keyword: "", typing: false, result: "" };

  const App = () => {
  function handleChange(event) {
  setState({ keyword: event.target.value, typing: true });
  }

  function handleClick() {
    setState({
      typing: false,
      result: `We find results of ${state.keyword}`
    });
  }

    return (
      <>
        <input onChange={handleChange} />
        <button onClick={handleClick}>search</button>
        <p>
          {state.typing ? `Looking for ${state.keyword}...` : state.result}
        </p>
      </>
    );
  };

  // 기존에 있는 state에 값을 넣어주는 함수
  function setState(newState) {
 	// 첫 번째 인자의 값에 두 번째 인자의 값을 넣는다. (달라진 값만)
      Object.assign(state, newState);
      render();
    }

    function render() {
      ReactDOM.render(<App />, rootElement);
  }

  render();
</script>

👉 실행 결과

정리

  • Object.assign: 첫 번째 인자(객체)에 두 번째 인자(객체)를 담는데, 변경이 있다면 덮어씌우고 변경이 없다면 그대로 유지한다.

profile
🙋‍♀️

0개의 댓글