React 의 특성

조 은길·2022년 2월 10일
0

React

목록 보기
2/12
post-thumbnail

비교하며 학습하는 리액트 (react) 강의를 보고, 작성한 TIL입니다.

- 리엑트의 특성

특성1. 리엑티브(reactive)

바닐라 JS 는 화면을 변경할 때, (MVC 패턴에서) 모델과 뷰를 매번 같이 사용했어야 했다.
=> 사용자가 검색하면, 검색결과를 모델에 저장하고, 뷰에 데이터를 전달해서 화면에 그리는 순서!!

그러나, 리엑트에서는,
UI를 상태로 관리하게 되면, 데이터 변경만으로 화면은 자동으로 갱신할 수 있다.
=> 즉, 모델만 변경하는 방식으로 코딩을 줄일 수 있다.

  • 바닐라 JS 버전
 <script>
      let data = "Hello World"; // model;
      document.body.innerHTML = `<p>${data}</p>`;

      data = "안녕";
      // 데이터를 변경한다고 해서 화면도 자동으로 변경되는 것은 아니다.
      // 화면에 반영해주는 코드를 추가적으로 작성해줘야 한다.

      document.body.innerHTML = `<p>${data}</p>`;

      // 즉, 모델과 모델을 연결하는 뷰도 같이 바꿔줘야 하는 과정이다.
      // 그러나, 리엑트는 data = "안녕" 만 변경해주면, 뷰는 자동적으로 바뀐다.
    </script>
  • React 버전
<script>
      const state = {
        _data: "Hello World",
      };

      // state에 값을 보고, 화면에 출력하는 render라는 함수를 만들자
      // 화면을 변경해주는 함수
      const render = () => (document.body.innerHTML = `<p>${state.data}</p>`);

      // 그러나, _data와 data 이름이 다르다.

      // 특정 객체 => state 객체에 속성을 추가할 수있다.
      // 3번째 인자가 옵션 객체 게터와 세터를 추가할 수 있다.
      Object.defineProperty(state, "data", {
        get() {
          console.log("get");
          return state._data;
        },
        set(value) {
          console.log("set");
          state._data = value;
          render();
        },
      });

      render();
      // render() 한 번 호출 되면서 state.data에 한 번 접근했기 때문에 get()이 호촐된다.
      state.data = "변경된 값";
      state.data = "변경된 값 2";
      // 모델의 값을 변경할 때마다, 화면은 자동으로 반영된다.
    </script>

=> 이렇게 특정 값에 의존해서 자동으로 반응하는 것을 "리엑티브(reactive)하다"라고 표현한다.
=> state가 뷰와 모델을 연결해주는 컨트롤러 역할을 일부 가져오기 때문에 뷰모델(view model aka VM)이라고 한다. VM을 변경하는 것만으로도 UI가 자동적으로 갱신되기 때문이다.
=> 상태 관리 코드만 기술하는 방식이 좀 더 선언적으로 읽기 쉬운 코드로 만드는 것이다.

정리하면, React로 화면 개발을 했을 때, 그렇지 않을 때보다, 코드가 비교적 단순해지는 효과가 있다.
그냥 맨아래 깃헙 주소 들어가서, 동일하게 짠 검색폼의 바닐라 JS버전리엑트 버전을 슬쩍 해보기만해도 무슨 말인지 알 수있다.


특성2. 가상돔

데이터만 변경하면, UI까지 반응한다는 것은 매력적이다. 그러나, 상태 변화의 횟수만큼 DOM API를 호출해야만 한다. 그것은 곧 브라우져 성능에 영향을 주게 된다.

브라우져가 HTML과 CSS로 화면을 그리는 과정은 다음과 같다.

1. HTML 코드를 파싱해서 DOM 트리를 만든다
2. CSS 코드를 CSSOM 트리를 만든다
3. 두 트리를 합쳐 렌더트리를 만든다
4. 레이아웃을 계산한다
5. 픽셀로 화면에 그린다
자료 출처 : 김정환님 블로그

DOM API가 호출될 때마다 이 작업이 반복된다는 것인데... 브라우져 성능을 높히려면, 어떻게해야 할까??
=> 바로, DOM API 호출 횟수를 줄이면 된다!!

즉, 화면을 변경할 때마다 DOM API를 바로 호출하지 말고, 어딘가 호출 정보를 모아놓은다고 생각해보자!!
예를 들어, 버튼을 빨간색 => 파란색 => 빨간색 으로 바꾸는 3번의 요청을 모았다고 해보자! ( 대신 받았다?? )
그러나, 결과는 빨간색 1번만 DOM API로 호출될 것이다.

즉, 요청을 모아서, 꼭 필요한 요청만 골라서 요청 횟수를 줄여주는 똑똑한 녀석을 우리는 가상돔(virtual DOM)이라고 한다.

  • 어플리케이션이 처음 화면을 그릴 때, 가상 돔에게 요청 => 가상돔은 전체화면을 한 번 그리고 => 실제 DOM API를 호출해서 화면을 그린다.
  • 이 후에, 어플에서 화면 변경이 필요하면, 다시 가상 돔에게 요청 => 가상돔은 다시 전체화면을 한 번 그리고 => 이전의 가상돔과 지금의 가상돔의 차이나는 부분만 계산해서, 실제 DOM API에 반응해서 렌더링 성능이 올라간다.

한마디로, 가상돔은 최소한의 연산만으로 화면을 그린다.

가상돔은 특정 기술이라고보다 패턴 방식이라고 볼 수있다.
즉, 구현하는 방식에 따라 동작하는 것이 조금씩 다를 수 있다.
리엑트에서는 보통 UI를 말하는 개념이다.


특성 3. ReactDOM

보통 웹 개발에서 리엑트를 사용한다고 하면, react + reactDOM을 사용하는 것이다.

리엑트를 웹 브라우져에서 작동하려면, 가상돔이 DOM API를 호출하도록 해야한다. 이러한 역할을 하는 것이 reactDOM 라이브러리이다.

아래의 예제 코드는 CDN 방식으로 ReactreactDOM을 받아오고 있다.

같이 살펴보자!!

<body>
    <div id="app"></div>
    <script
      crossorigin
      src="https://unpkg.com/react@17/umd/react.development.js"
    ></script>

    <script
      crossorigin
      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 element = React.createElement("h1", null, "Hello world");
      console.log("react", element);
      // React.element를 만든다음에, 
	  //ReactDOM은 element를 받아서 가상돔을 만들기 시작합니다.
      // 그리고 실제 돔에 반영을 해주는데, 그 위치가 2번째인자 이다.
      ReactDOM.render(element, document.querySelector("#app"));

      // document.body.innerHTML = "<p>Hello World by DOM</p>";
      // element는 리엑트를 구성하는 최소 단위이다.

      const p = document.createElement("p");
      p.textContent = "content created by DOM method";
      console.log("dom", p);
      // DOM element는 DOM 엘리먼트로 출력 되는 반면에, 
      //React element는 JS 객체로 출력된다.
    </script>
  </body>
  • React element가 JS 객체를 반환하지 않고, DOM element를 반환한다면 어땠을까??
    => DOM element를 사용할 수 있는 웹 브라우져 안에서만 React 사용이 가능했을 것이다. 하지만, 일반 객체로 된 React element는 렌더링하는 환경에 따라서 여러 플렛폼에서 사용할 수있다. ex) IOS, 안드로이드 같은 네이티브 앱에서도 리엑트를 사용할 수 있다.

특성 4. Babel

예제 코드에서 3번째 cdn방식을 받아온 라이브러리이다.

최신 자바스크립트 문법을 웹 브라우져에서 사용하도록 하는 문법 => 바벨

작성한 JS를 대부분의 브라우져에서 동작할 수 있는 버전의 코드로 변환하는 도구!!

리엑트 공식 문서를 보면, 바벨 없이도 리엑트를 사용할 수 있는 방법을 안내한다.

ES6 없이 사용하는 React
=> ES6 문법도 사용할 수 없고, Babel도 사용할 수 없는 환경에서는 class문법을 못 쓰니까 create-react-class라는 라이브러리를 써서 리엑트 컴포넌트를 만들 수 있다.
=> 그렇지만, 실제 개발 프로젝트를 진행하다보면, 이것만으로는 많은 한계가 온다고 한다.
=> 리엑트 컴포넌트는 class 문법을 많이 사용한다.
=> 물론, JSX 문법도 class 못지 않게 사용한다. 단, JSX를 사용할 때는 바벨이 필요하다.

보통 바벨은 터미널에서 명령어를 실행하는 과정으로 코드를 변환한다. 아니면, 웹팩 같은 번들러에서 바벨을 통합해서 사용하는 경우(실무에서는 권장하는 방식)가 많다.

그러나, 수업에서는 React 실습이 목적이라, 최소한의 개발 환경만을 유지하려고, 한다.

  • @babel/standalone 버전
    cdn을 통해 받아온 바벨의 버전이다. 이 버전은 <script type="text/babel">안에 있는 코드를 변환해준다.

정리하면,
Class, JSX 문법같은 React 핵심 문법들을 사용할 수있게 해주기 때문에, React 코딩을 편하게 하려면, Babel같은 트랜스파일러의 도움이 필요하다.

바벨에 관해서 더 자세히 알고 싶다면, 프론트엔드 개발환경의 이해: Babel


특성 5. 탬플릿 언어

트리 형태에 맞게 DOM을 구성하려면, 엘리먼트 간의 부모-자식 관계를 만들어줘야 한다.

const h1 = document.createElement("h1") // 1
const header = document.createElement("header") // 2
header.appendChild(h1) // 3

그러나, 이런 코드가 많아지면, UI에 출력되는 코드를 읽어내기가 어렵다.
=> 가독성이 매우 떨어진다.

그래서, 대안으로 나오는 것이 탬플릿 언어이다.
ex) Node.js => Pug, 앵귤러와 Vue.js도 나름의 탬플릿 언어를 지원함

그러나, React는 탬플릿 언어를 지원하지 않는다.

const h1 = React.createElement("h1", null, "Hello world")
const header = React.createElement("header", null, h1)

그래서 이런 식으로, 부모 자식의 관계를 설정해줘야 하는데, 솔직히, DOM보다 이게 더 가독성이 떨어지는 것같다.

그래서 React에서는 JSX라는 자바스크립트 확장 문법을 사용한다.


특성 6. JSX

JSX(JavaScript XML)는 자바스크립트의 확장 문법이다.

이것 자체가 JS 표현식이 되는 건데, 바벨에 의해서 변환이 필요하다.

존재 목적은 탬플릿 언어를 제공하지 않는 React의 UI를 다루는 가독성을 높이기 위해서 사용한다.

<h1>Hello world</h1> // React.createElement('h1', null, 'Hello world')

부모/자식 관계도 HTML과 같다.

<header>
  <h1>Hello world</h1>
</header>

바벨 REPL에서 직접 JSX 코드가 자바스크립트로 변환되는 모습을 확인해 보자.


특성 7. JSX 작성법

  • JS 엔진이 자동으로 ;을 넣어주는 것을 피하기 위해서 ()를 넣어준다.
    JSX 코드를 사용할 때, 권장되는 방식이다.
const element = (
  <header>
    {/* <h2 class="container">검색</h2> => 이거 안됨!! */}
    <h2 className="container">검색</h2>
  </header>
);
ReactDOM.render(element, document.querySelector("#app"));
  • class는 JS에서 사용하는 "예약어"라서 사용할 수 없다.
    그래서, 대안으로 JSX에서는 className을 사용한다.

  • 또한, IDclass같은 어트리뷰트 이름을 정할 때, HTML은 소문자만 사용한다.
    그러나, JSX는 카멜 케이스를 사용해서 이름을 짓는 것이 일반적이다.
    ex) tabindex → tabIndex, onclick → onClick


해당 github 링크

profile
좋은 길로만 가는 "조은길"입니다😁

0개의 댓글