React Components, Elements, and Instances 에 대하여

윤효준·2024년 8월 3일

React

목록 보기
1/6

react로 프로젝트를 시작했는데 아무 것도 모른 체 냅다 하고 있어 react에 대한 공부를 시작했습니다. 이번 내용을 아래 링크에 있는 내용을 토대로 공부한 것을 정리했습니다.

https://ko.legacy.reactjs.org/blog/2015/12/18/react-components-elements-and-instances.html#fn-1

왜 elements가 나오게 되었는가???

전통적인 UI 모델에서는 컴포넌트 클래스와 인스턴스를 다루는 것이 일반적이다.
여기서 자식 컴포넌트 인스턴스를 생성하고 삭제하는 것은 개발자의 몫이다.
다음은 이러한 전통적인 객체 지향 UI 프로그래밍의 예다.

class Form extends TraditionalObjectOrientedView {
  render() {
    // View로 전달된 데이터 읽기
    const { isSubmitted, buttonText } = this.attrs;

    if (!isSubmitted && !this.button) {
      // Form이 아직 제출되지 않았습니다. 버튼을 생성합니다!
      this.button = new Button({
        children: buttonText,
        color: 'blue'
      });
      this.el.appendChild(this.button.el);
    }

    if (this.button) {
      // 버튼이 표시됩니다. 버튼의 텍스트를 업데이트합니다!
      this.button.attrs.children = buttonText;
      this.button.render();
    }

    if (isSubmitted && this.button) {
      // Form이 제출되었습니다. 버튼을 제거합니다!
      this.el.removeChild(this.button.el);
      this.button.destroy();
    }

    if (isSubmitted && !this.message) {
      // Form이 제출되었습니다. 성공 메시지를 표시합니다!
      this.message = new Message({ text: 'Success!' });
      this.el.appendChild(this.message.el);
    }
  }
}

각각의 컴포넌트는 DOM 노드와 자식 컴포넌트의 인스턴스를 계속 참조하고, 이를 생성, 업데이트, 삭제하는 것을 신경 써야 한다.
이로 인해 상태의 개수에 따라 코드의 길이가 제곱 배로 늘어나며, 부모가 자식 컴포넌트에 직접 접근할 수 있어 둘을 분리(decouple)시키기 어렵게 만든다.

그러면 React는 뭐가 다른가???

React에서는 elements가 이러한 문제의 해결사로 등장한다.

elements는 컴포넌트 인스턴스나 DOM 노드를 설명하는 단순한 자바스크립트 객체이다.
component type과 properties, child elements에 대한 정보를 담고 있다.

elements는 우리가 화면에서 무엇을 보고 싶은지 React에 알려주는 친구이다.

elements는 두 개의 필드를 가지고 있다.

  • type(string | ReactClass)
  • props(Object)

type이 string인 경우

elements는 tag(태그)와 attributes(속성)에 해당하는 props를 통해 DOM node를 표현한다.

<button class='button button-blue'>
  <b>
    OK!
  </b>
</button>

위의 html은 아래와 같은 element로 표현이 된다.

{
  type: 'button',
  props: {
    className: 'button button-blue',
    children: {
      type: 'b',
      props: {
        children: 'OK!'
      }
    }
  }
}

type이 ReactClass인 경우

여기서 잠깐!! ReactClass란???

ReactClass(React 컴포넌트 클래스)는 클래스형 컴포넌트나 함수형 컴포넌트를 이야기한다.

  • 클래스형 컴포넌트: React.Component를 상속받아 정의된 컴포넌트
class MyComponent extends React.Component {
  render() {
    return <div>Hello, World!</div>;
  }
}
  • 함수형 컴포넌트: 단순히 props를 받아서 JSX를 반환하는 함수
function MyComponent(props) {
  return <div>Hello, World!</div>;
}

결국 type이 ReactClass라는 것은 아래와 같다.

const DeleteAccount = () => (
  <div>
    <p>Are you sure?</p>
    <DangerButton>Yep</DangerButton>
    <Button color='blue'>Cancel</Button>
  </div>
);

위의 함수형 컴포넌트는 아래와 같은 element로 표현 가능하다.

const DeleteAccount = () => ({
  type: 'div',
  props: {
    children: [{
      type: 'p',
      props: {
        children: 'Are you sure?'
      }
    }, {
      type: DangerButton,
      props: {
        children: 'Yep'
      }
    }, {
      type: Button,
      props: {
        color: 'blue',
        children: 'Cancel'
      }
   }]
});

위와 같이 type이 ReactClass인 element를 React가 마주하게 되면 React는 해당 컴포넌트가 무엇을 렌더링하는지 확인한다.

React는 A는 뭐야?? 라고 물어보고 우리가 A는 B야!라고 답해주면 그럼 B는 뭐야??라고 다시 물어본다.
이 과정을 최종적으로 아는 것(DOM tag elements)이 나올 때까지 반복한다.

이런 점에서 공식 홈페이지에서는 React를 아이와 같다고 표현한다.
React는 계속해서 질문을 통해 모든 요소를 파악한다.

element는 누가 만들어주는가??

React에서 element(요소)를 생성하는 과정은 다음과 같다.

JSX 코드

const element = <div><Button color="blue">Click me</Button></div>;

트랜스파일링

위와 같은 JSX 코드가 있다고 할 때 브라우저는 JSX를 이해할 수 없기에 Babel과 같은 트랜스파일러가 JSX를 순수 JavaScript 코드로 변환해준다.
이 과정에서 React.createElement 함수가 호출이 된다.
이름에서 알 수 있듯이 이 친구가 element를 만들어 준다고 이해하면 된다.

다음은 위 JSX 코드가 Babel에 의해 트랜스파일된 코드이다.

const element = React.createElement(
  'div',
  null,
  React.createElement(
    Button,
    { color: 'blue' },
    'Click me'
  )
);

React.createElement 함수

React.createElement 함수는 type, props, 그리고 children을 받아서 element 객체를 생성한다.
함수의 내부 동작 방식은 다음과 같다.

React.createElement = function(type, props, ...children) {
  return {
    type: type,
    props: {
      ...props,
      children: children.length === 1 ? children[0] : children
    }
  };
};

위 함수는 type, props, children을 받아 다음과 같은 객체를 반환한다.

const element = {
  type: 'div',
  props: {
    children: {
      type: Button,
      props: {
        color: 'blue',
        children: 'Click me'
      }
    }
  }
};

위의 Form class를 element로 다시 작성해보자

전통적인 객체 지향 방식에서 작성된 Form 클래스를 React를 사용하여 더 간단하게 작성할 수 있다.

const Form = ({ isSubmitted, buttonText }) => {
  if (isSubmitted) {
    // Form submitted! Return a message element.
    return {
      type: Message,
      props: {
        text: 'Success!'
      }
    };
  }

  // Form is still visible! Return a button element.
  return {
    type: Button,
    props: {
      children: buttonText,
      color: 'blue'
    }
  };
};

훨씬 간단해졌다!
우리는 React에게 생성, 업데이트 및 삭제를 맡기고 책임 없는 쾌락(코딩)를 즐기면 된다.

컴포넌트의 선언 방식

React에서 컴포넌트를 선언하는 방식은 여러가지가 있다.

함수형 컴포넌트

함수형 컴포넌트는 props를 입력으로 받아 JSX를 반환하는 단순한 함수이다.

const Button = ({ children, color }) => ({
  type: 'button',
  props: {
    className: 'button button-' + color,
    children: {
      type: 'b',
      props: {
        children: children
      }
    }
  }
});

React.createClass() 팩토리 사용

팩토리란???

팩토리 패턴은 객체 생성 로직을 캡슐화하여 직접 객체를 생성하는 대신, 객체를 생성하는 역할을 맡는 메서드나 클래스를 사용하는 디자인 패턴이다.
이 패턴을 사용하면 객체 생성 방식을 추상화하여 코드의 유연성과 확장성을 높일 수 있다.

React.createClass는 React 컴포넌트를 선언하는 초기 방식 중 하나로 주어진 메서도와 속성으로 구성된 컴포넌트를 생성한다.

const Button = React.createClass({
  render() {
    const { children, color } = this.props;
    return {
      type: 'button',
      props: {
        className: 'button button-' + color,
        children: {
          type: 'b',
          props: {
            children: children
          }
        }
      }
    };
  }
});

ES6 클래스 문법 사용

현재는 React.createClass 대신 ES6 클래스 문법을 사용하는 것이 일반적이다.

class Button extends React.Component {
  render() {
    const { children, color } = this.props;
    return {
      type: 'button',
      props: {
        className: 'button button-' + color,
        children: {
          type: 'b',
          props: {
            children: children
          }
        }
      }
    };
  }
}

위의 방식에 대한 비교

방식장점단점
함수형 컴포넌트간결함, 가독성, 성능, Hooks 사용 가능초기에는 기능 제한(현재는 Hooks로 대부분 해결), 클래스 메서드 사용 불가
React.createClass 팩토리자동 바인딩, 초기 React 방식구식 방법, 미래 지원 부족, 복잡성
ES6 클래스 컴포넌트상태 관리, 라이프사이클 메서드, 구조화복잡성, 가독성 저하, 성능 문제
profile
작은 문제를 하나하나 해결하며, 누군가의 하루에 선물이 되는 코드를 작성해 갑니다.

0개의 댓글