본문 바로가기

React

20210604 React01 : React 개념, Virtual DOM, CSR vs SSR, 리액트 라이브러리, Component(Class, Function), React.createElement, JSX, Props 와 State, Render함수

React 01

React Concepts


Component

  • src, class, name, props 밖에서 넣어주는 데이터
  • html, CSS, JS을 합쳐서 내가 만든 일종의 태그
  • 재사용을 할 수 있는 개인이 커스텀한 완전한 묶음

Component Based Development

  • 화면을 구성하는 부분인 component들이 새로운 component를 구성하면서 개발하는 방식

Component Tree

  • Component Tree를 Dom Tree로 변환 되어 동작함
  • Dom Tree의 경우 브라우저에 내장되어 있는 표현의 목록임
  • Component Tree의 경우에는 개인이 내장되어 있는 태그의 조합, 스타일, 동작을 입혀서 재사용 가능

Virtual DOM

  • DOM 을 직접 제어하는 경우
    • 바뀐 부분만 정확히 바꿔야 함
  • DOM을 직접 제어하지 않는 경우
    • 가상의 돔트리를 이용해서, 이전 이후의 상태를 비교하여 바뀐 부분을 찾아내서 바꿈
    • Diff를 찾아 Rerender 함

React CSR

  • Client Side Rendering
  • React가 화면을 출력하기 까지의 과정
    • 빈 껍데기 HTML 다운 -> HTML에서 가르키는 JS 다운 -> JS 안에 있는 React src(소스코드)를 실행 -> 리액트 앱이 실행 -> 컴포넌트 출력

React SSR

  • Server Side Rendering
    • 이미 표현된 HTML을 다운 (대신 기능은 못함) -> JS 다운 (React src) -> 리액트 앱이 실행 -> 다시 rerendering 후 동작이 가능

CSR vs SSR

  • 모두 react가 실행 된 후 기능 동작을 할 수 있을 것임
  • 하지만, SSR은 화면의 출력에 대한 시간적 간극을 줄여줄 수 있음 (동작이 시급한 페이지가 아니라면 사용했을 때 좋다.)

JS다운 및 앱 실행
CSR 화면출력X, 유저 인터렉션X 화면출력O, 유저 인터렉션 O
SSR 화면출력O, 유저 인터렉션X 화면출력O, 유저 인터렉션 O



개발 환경 체크


  • 운영 환경과 개발 환경은 다름

개발 환경

  • Node.js (installer or nvm 방법으로 설치)
  • Browser (Chrome)
  • Git
  • VSCode



리액트 라이브러리


리액트의 핵심 모듈


  • react-dom : 리액트 컴포넌트 HTMLelement에 연결하는 모듈
    • ReactDOM이라는 전역변수로 받아서 사용을 시작 함
    • render() : JS, JSX로 만들어진 React 컴포넌트들을 실제 HTMLelement에 연결함
import ReactDOM from "react-dom";

ReactDOM.render(<HelloMessage name="Taylor" />);
  • react : 리액트 컴포넌트 만드는 모듈
    • React이라는 전역변수로 받아서 해당 변수의 Component class를 만들 컴포넌트 class에 상속(extends)받아서 사용함
import React from "react";
class HelloMessage extends React.Component {
  render() {
    return <div>Hello {this.props.name}</div>;
  }
}



React Component 만들기


Hooks 이전


  • 컴포넌트 내부에 상태가 있는 경우 -> Class 컴포넌트
  • 컴포넌트 내부에 상태가 없는 경우
    • 라이프사이클이 필요한 경우 -> class 컴포넌트
    • 라이프사이클이 필요하지 않은 경우 -> function 컴포넌트

Hooks 이후

  • class 컴포넌트. function 컴포넌트

Class 컴포넌트

  • ESM 방식으로 react 라이브러리를 가져와서 사용함
// 컴포넌트 만드는 라이브러리 가져오기
import React from "react";

// 정의
class ClassComponent extends React.Component {
  render() {
    return <div>Hello</div>;
  }
}

// 사용
<ClassComponent />;

Function 컴포넌트

// 정의 방법01
function FunctionComponent() {
  return <div>Hello</div>;
}

// 정의 방법02 (element만 출력하는 경우는 바로 return하는 arrow함수로)
const FunctionComponent = () => <div>Hello</div>;

// 사용
<FunctionComponent />;



React.createElement로 컴포넌트 만들기


  • JSX를 활용하기 전에 순수 자바스크립트로만 React에서 제공하는 API인 createElement 로 컴포넌트를 만들면 복잡성이 증가하는 한계가 생김

createElement 사용법


  • craeteElement는 3개의 인자를 받음 (type, props, children)
  • type에는 태그, 컴포넌트로 표기할 문자가 온다.
    • React에서는 Fragment라는 것을 제공함
  • props에는 컴포넌트에 넣어줄 데이터를 말함
  • children에는 컴포넌트안에 구성되어 있는 자식요소들을 말함
React.createElement(
  type, // 태그 이름 문자열 | 리액트 컴포넌트 | React.Fragment
  [props], // 리액트 컴포넌트에 넣어주는 데이터 객체
  [...children] // 자식으로 넣어주는 요소들 또는 내용
);

사용 예시


  1. 태그 이름 문자열 type
  • <h1>type 이 "태그 이름 문자열" 입니다.</h1>
  • h1, span등의 우리가 이미 알고 있는 html 태그 이름으로 component를 만드는 경우
ReactDOM.render(
  React.createElement("h1", null, 'type 이 "태그 이름 문자열" 입니다.'),
  document.querySelector("#root")
);

  1. 리액트 컴포넌트 type
  • <Component></Component> => <Component />
  • <p>type이 "React 컴포넌트" 입니다.</p>
  • React에서 나만의 컴포넌트를 만드는 경우
// 정의
const Component = () => {
  return React.createElement("p", null, 'type이 "React 컴포넌트" 입니다.');
};
// 사용
ReactDOM.render(
  React.createElement(Component, null, null),
  document.querySelector("#root")
);

  1. React.Fragment type
  • <div id='root'>type이 "React Fragment" 입니다.</div>
  • 하위 태그 없이 바로 자식 및 내용들을 넣을 수 있음
ReactDOM.render(
  React.createElement(React.Fragment, null, 'type이 "React Fragment" 입니다.'),
  document.querySelector("#root")
);

createElement의 한계 : 복잡한 리액트 엘리먼트들의 사용


  • 복잡성이 증가하고, 코드의 양이 무지하게 많아지고 가독성이 떨어짐

  • 표현 할려는 요소 모양

<div>
  <div>
    <h1>주제</h1>
    <ul>
      <li>1</li>
      <li>2</li>
    </ul>
  </div>
</div>

  • createElement 로 표현
ReactDOM.render(
  React.createElement(
    "div",
    null,
    React.createElement(
      "div",
      null,
      React.createElement(
        "div",
        null,
        React.createElement("h1", null, "주제"),
        React.createElement(
          "ul",
          null,
          React.createElement("li", null, "1"),
          React.createElement("li", null, "2")
        )
      )
    )
  ),
  document.querySelector("#root")
);



JSX


  • createElement API의 복잡성에서 벗어나 실제 html요소를 표현하는것 처럼 간단하게 쓰고자 했음

  • babel이 JSX 문법의 코드를 해석해서 브라우저에서 동작하는 순수한 자바스크립트로 변환(컴파일) 시켜줌

// CDN으로 babel 연결한 상태
ReactDOM.render(
  <div>
    <div>
      <h1>JSX + Babel</h1>
      <ul>
        <li>1</li>
        <li>2</li>
      </ul>
    </div>
  </div>,
  document.querySelector("#root")
);

JSX 사용 이유


  • 가독성의 엄청난 향상
  • babel과 같은 컴파일 과정에서 문법적 오류를 인지하기 쉬움 (컴파일 오류로 확인 가능)

JSX 문법


  • 최상위 요소는 하나 이어야 함(최상위 요소를 병렬적으로 사용 못함)
  • 최상위 요소 리턴하는 경우, ()로 감싸야 함
  • 자식들을 바로 랜더링 하고 싶으면, <>자식들</>로 Fragment사용
    • -> 최상위 요소 두개사용 못하는 것을 Fragment로 대체해서 사용 가능
  • 자바스크립트 표현식을 사용하려면, {표현식}를 이용함
  • if문은 사용할 수 없음 (if문은 표현식이 아닌 그냥 문장 임)
    • 삼항 연산자 혹은 &&를 사용함
  • style prop을 이용해 인라인 스타일링이 가능함
  • class 대신 className을 사용해 class를 적용할 수 있음 (js 예약어라서)
  • 자식요소가 있으며, 꼭 닫아야 하고, 자식요소가 없으면 열면서 닫아야 함
    • <p></p>
    • <br/>



Props 와 State


  • Props : 컴포넌트 외부에서 컴포넌트에게 주는 데이터
  • State : 컴포넌트 내부에서 변경할 수 있는 데이터
  • 둘 다 변경이 발생하면, Component에서 render가 다시 일어 날 수 있음

Render 함수


  • Props 와 State를 바탕으로 컴포넌트를 그림
  • Props와 State가 변경되면 컴포넌트를 다시 그림
  • 컴포넌트를 그리는 방법을 기술하는 함수가 랜더 함수

Props


  • Props를 function 컴포넌트와 class 컴포넌트에서 사용 방법이 다름

Function Component - props, default


  • render시 component 사용 부분에서 받은 props를 인자로 받아서 JSX에서 사용함
  • {props.message}로 사용
  • component 사용시에 prop에 값을 넣지 않아도 기본값을 줄수 있음 (defaultProps)
function Component(props) {
  return (
    <div>
      <h1>함수로 만든 컴포넌트 - 값 : {props.message}</h1>
    </div>
  );
}
Component.defaultProps = {
  message: "DefaultProps!",
};
ReactDOM.render(
  <>
    <Component message="Functional Component!" />
    <Component />
  </>,
  document.querySelector("#root")
);
// 함수로 만든 컴포넌트 - 값 : Functional Component!
// 함수로 만든 컴포넌트 - 값 : DefaultProps!

Class Component - props, default


  • class 컴포넌트의 경우 props를 인자로 받진 않지만 사용시 this 키워드로 props에 접근할 수 있음
  • class 컴포넌트에서 default 값 설정 방법은 2가지임
    • class 내부 static 키워드를 통한 Default값 설정
    • function 컴포넌트와 같은 방법인 Component 클래스 컴포넌트로 props 접근
class Component extends React.Component {
  render() {
    return (
      <div>
        <h1>함수로 만든 컴포넌트 - 값 : {this.props.message}</h1>
      </div>
    );
  }
  // 방법2 : class 내부 static 키워드를 통한 Default값
  static defaultProps = {
    message: "static keyword DefaultProps!",
  };
}
// 방법1 : function component와 같은 방법
// Component.defaultProps = {
//   message: "DefaultProps!",
// };

ReactDOM.render(
  <>
    <Component message="Class Component!" />
    <Component />
  </>,
  document.querySelector("#root")
);
// 함수로 만든 컴포넌트 - 값 : Class Component!
// 함수로 만든 컴포넌트 - 값 : static keyword DefaultProps!



State


  • state를 정의하려면 class 컴포넌트여야 함
  • state를 정의하는 방법 2가지
    • 방법01: 클래스의 멤버변수 방식
      • 무조건 값은 객체 형태 {} 이여야 함
    • 방법02: constructor 방식
      • constructor 키워드로 선언하고 props를 받아 해당 porps로 super 키워드로 부모클래스인 React.Component 클래스를 찾음, 그리고 this 키워드로 state에 접근하여 객체를 할당함

  • state의 값을 재할당하여 변경하는 법 2가지
    • 기본적으로 변경 하려면 componenetDidMount 함수를 이용하여 기본적인 render가 모두 마쳤을 때 함수들을 실행함, 그함수에서 state 값을 재설정 하여 다시 render 시킴
      • 방법01: setState함수를 이용해서 새로운 객체의 모양으로 값을 넣음
      • 방법02: setState함수 안에 callback 함수를 넣어 이전 state 값을 이용하여 값을 넣는 방법
class Component extends React.Component {
  // 클래스의 멤버변수 방식 (무조건 값은 객체 여야함)
  // state = {
  //     count: 0,
  // };

  // constructor 방식
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }
  render() {
    return (
      <div>
        <h1>{this.props.message} Class로 만든 컴포넌트</h1>
        <p>{this.state.count}</p>
      </div>
    );
  }

  componentDidMount() {
    setTimeout(() => {
      // 잘못된 방식
      this.state.count = this.state.count + 1;

      // 방법01: 객체를 통째로 다시 넣는 방식
      this.setState({
        count: this.state.count + 1,
      });

      // 방법02: 이전 값을 사용하는 방식
      this.setState((prevState) => {
        const newState = { count: prevState.count + 1 };
        return newState;
      });
    }, 1000);
  }

  static defaultProps = {
    message: "기본값static",
  };
}

ReactDOM.render(
  <Component message="새로 넣은 값" />,
  document.querySelector("#root")
);