[React][공식문서] React의 개념과 '엘리먼트'

Gyuwon Lee·2022년 5월 30일
1
post-thumbnail

React 공식 튜토리얼을 바탕으로, 필요한 개념을 보충하여 학습한 기록입니다.

1. React란 무엇인가?

React는 "UI를 만들기 위한 JavaScript 라이브러리" 이다. 그렇다. '라이브러리'다.

  • 프레임워크 : 원하는 기능 구현에 집중하여 개발할 수 있도록 필요한 기능을 갖추고 있는 것, 일정한 형태를 가지고 다양한 형태의 결과물을 만드는 것

    • 정해진 프로그램의 틀에 맞게 사용자가 필요한 기능을 입력함
  • 라이브러리 : 소프트웨어를 개발할 때 프로그래밍 사용하는 비휘발성 자원의 모임, 공통으로 사용될 수 있는 특정한 기능들을 모듈화한 것

    • 호출하는 개발자가 필요한 기능을 원할 때 호출함

Angular, Vue 등의 다른 프레임워크와는 달리 React는 오직 View만 담당하는 라이브러리다.

UI = View(State)

그래서 UI를 위의 수식과 같이 표현한다면, React가 이 수식의 View 함수에 해당한다고 설명한다. (출처: JisuPark 님의 medium)

그 말은, ViewState가 같다면 항상 같은 UI를 결과로 갖는 함수로 본다는 것이다.

이러한 관점에서의 장점은 다음과 같다:

  • 함수의 정의가 그러하듯 단방향 사고를 강제한다.
  • 함수가 그러하듯 특정 state, props에 따른 render 결과가 바뀌지 않는다.
  • 함수 내용을 정의하듯 JSX를 통해 어떻게 화면을 그릴지 정의한다.
  • 함수 간 합성(Composition)이 가능하듯이 컴포넌트 간 합성을 할 수 있다.

2. JSX와 엘리먼트

const element = <h1>Hello, world!</h1>

앞선 글에서 말했듯, React는 JSX 문법을 사용하여 화면을 구성한다. 이 JSX란 자바스크립트에 XML을 더한 형태로, 마크업과 로직을 각각 별도의 파일로 구분하는 기존의 방식에서 벗어나 이 둘을 함께 포함하는 각각의 컴포넌트들로 프로그램의 각 영역을 구분하는 데 사용된다.


2-1. 🤷‍♀️ 포함된 표현식도 표현식일 수 있는거임? JSX도?

🪄 JSX에 표현식 포함하기

JSX의 특징이자 장점은 마크업 요소 안에 유효한 자바스크립트 표현식(값을 반환하는 식 또는 코드)을 넣을 수 있다는 것이다. 정확히는 중괄호 안에는 유효한 모든 자바스크립트 표현식을 넣을 수 있다.

function formatName(user) {
  return user.firstName + ' ' + user.lastName;
}

const user = {
  firstName: 'Harper',
  lastName: 'Perez'
};

const element = (
  <h1>
    Hello, {formatName(user)}!
  </h1>
);

위 코드는 공식문서에 나와 있는 예시로, 자바스크립트 함수 호출의 결과인 formatName(user)<h1> 엘리먼트에 포함시켰다.

🪄 JSX도 표현식이다

"값을 반환하는 식 또는 코드"라는 표현식의 정의를 생각해보자. 표현식은 곧 어떤 값으로 표상되기 때문에 JSX에서는 이것을 엘리먼트에 들어갈 값으로 사용할 수 있었다.

같은 관점에서, 자바스크립트 표현식을 사용한 JSX는 정규 자바스크립트 함수를 호출하게 되고, 즉 자바스크립트 객체로 인식된다. 즉 표현식으로써 if 구문 및 for loop 안에 사용되고, 변수에 할당되고, 인자와 리턴값으로 사용될 수 있다.

function getGreeting(user) {
  if (user) {
    return <h1>Hello, {formatName(user)}!</h1>;
  }
  return <h1>Hello, Stranger.</h1>;
}

🪄 JSX 속성(어트리뷰트) 정의

어트리뷰트, 즉 속성이란 html 문서에서 엘리먼트에 추가적인 정보를 넣을 때 사용되는 요소다.

const element = <a href="https://www.reactjs.org"> link </a>;
const elementTwo = <img src={user.avatarUrl}></img>;

위와 같이 어트리뷰트의 값에 문자열 리터럴이나 자바스크립트 표현식을 넣을 수 있다.
이처럼 JSX에서는 중괄호를 사용해서 값이 필요한 부분에 자바스크립트 표현식을 넣을 수 있다는 점을 상기하자.

🪄 JSX 트랜스파일

JSX 엘리먼트는 React.createElement(component, props, ...children)를 호출하여 트랜스파일(컴파일)된다. 그래서 사실 JSX로 할 수 있는 모든 것들은 순수 자바스크립트로도 구현된다:

class Hello extends React.Component {
  render() {
    return <div>Hello {this.props.toWhat}</div>;
  }
}

ReactDOM.render(
  <Hello toWhat="World" />,
  document.getElementById('root')
);

위와 같은 JSX 코드는 아래와 같은 순수 자바스크립트 코드로 변환될 수 있다.

class Hello extends React.Component {
  render() {
    return React.createElement('div', null, `Hello ${this.props.toWhat}`);
  }
}

ReactDOM.render(
  React.createElement(Hello, {toWhat: 'World'}, null),
  document.getElementById('root')
);

React.Component가 무엇인지는 아래에서 후술할 것이다. 지금 눈여겨볼 점은 <div>Hello {this.props.toWhat}</div> 라는 하나의 엘리먼트React.createElement('div', null, 'Hello ${this.props.toWhat}') 라는 하나의 함수 호출에 대응된다는 점이다.

  • (마크다운 표기로 인해 마지막 인자에 백틱 대신 작은따옴표를 사용했다. 실제로는 중괄호를 사용하기 위해 백틱 기호를 사용해야 한다.)
React.createElement(
  type, // string | React.createClass()
  [props], // null | object
  [...children] // null | string | React.createClass() | React.createElement()
)

React.createElement의 기본 형태는 위와 같다. 이 API를 내부적으로 들여다본 글을 찾았는데, 인자를 받아서 아래와 같은 값을 가지는 ReactElement 객체로 만들어준다고 한다.

const ReactElement = function(type, key, ref, self, source, owner, props){ 
    const element = {
        $$typeof : REACT_ELEMENT_TYPE,
        type: type,
        key: key,
        ref: ref,
        props: props,
        _owner: owner,
    }
...
    return element
}

즉, React.createElement를 통해 엘리먼트에 대한 정보를 가지는 객체를 생성하고, 이를 In-Memory에 저장한 후 ReactDOM.render 함수를 통해 Web API(document.createElement)를 이용해서 실제 웹 브라우저에 그려주는 방식으로 동작한다고 한다. (아직은 이 내용이 조금 어렵다)


2-2. 🧬 엘리먼트와 React 엘리먼트 렌더링

다시 한 번 엘리먼트에 대해 간단히 짚고 넘어가자.

🌐 브라우저 DOM 엘리먼트

엘리먼트는 웹 페이지의 일부이며, XML 혹은 HTML 내부에서 텍스트나 이미지의 일부 혹은 데이터 아이템을 가지고 있을 수 있다. 물론 아무것도 가지고 있지 않는 것도 가능하다. 일반적인 엘리먼트는 어트리뷰트(속성)와 함께, 열린 태그로 시작하며 그 내부에 텍스트가 있고 닫는 태그로 끝나게 된다. (참고: jakeseo-javascript.js)

다만, 위의 도식에서도 알 수 있듯 엘리먼트태그는 동일한 것이 아니다. 태그는 소스코드에서 엘리먼트를 시작하거나 끝내긴 하지만, 엘리먼트는 브라우저에서 페이지를 보여주는 document modelDOM의 일부이다.

🌐 리액트 엘리먼트

이와 달리, React에서 엘리먼트React 앱의 가장 작은 단위이다. 엘리먼트는 화면에 표시할 내용을 기술한다.

const element = <h1>Hello, world</h1>;

브라우저 DOM 엘리먼트와 달리, React 엘리먼트는 일반 객체이며 쉽게 생성할 수 있다. React DOM은 React 엘리먼트와 일치하도록 화면이 보여질 수 있게 DOM을 업데이트한다. 이후 언급되는 '엘리먼트'란 이 React 엘리먼트를 뜻한다.

🌐 DOM에 엘리먼트 렌더링하기

공식문서를 보면, "HTML 파일 어딘가에 <div>가 있다고 가정해 봅시다" 라며 아래와 같은 코드를 보여준다:

<div id="root"></div>

"이 안에 들어가는 모든 엘리먼트를 React DOM에서 관리하기 때문에 이것을 “루트(root)” DOM 노드라고 부릅니다" 라고 한다. 나는 잠깐 여기서 루트 노드가 어떤 의미인지 의문이 생겼다.

리액트에서는 직접 HTML을 코딩하는 것이 아닌, src 폴더에서 JSX 문법을 이용해서 HTML뷰를 생성해 낸다. 즉 HTML로 모든 뷰를 이미 만들어놓은 뒤 보여주는 정적 방식이 아니라, 동적으로 HTML 뷰를 생성하여 idrootdiv안에 들어가게 된다. create-react-app 으로 리액트 프로젝트를 생성한 직후 index.html 파일을 열어 보면 이를 확인할 수 있다. <body> 태그 안에는 id="root"<div> 태그 하나가 덩그러니 있을 것이다.

따라서 만들어낸 React 엘리먼트를 이 루트 DOM 노드에 렌더링하려면 둘 다 ReactDOM.render()로 전달하면 된다.

const element = <h1>Hello, world</h1>;
ReactDOM.render(element, document.getElementById('root'));

위 코드를 보면 render() 함수의 두 번째 인자로 getElementById('root')가 주어져 있는 것을 볼 수 있다. 즉 React에서는 일반적으로 id가 root인 요소를 찾아 루트 노드로 삼기로 한다.


🌐 렌더링된 엘리먼트 업데이트하기

React 엘리먼트는 불변객체다. 엘리먼트를 생성한 이후에는 해당 엘리먼트의 자식이나 속성을 변경할 수 없다. 엘리먼트는 영화에서 하나의 프레임과 같이 특정 시점의 UI를 보여준다.

지금까지 정리한 내용을 바탕으로 하면 UI를 업데이트하는 유일한 방법은 새로운 엘리먼트를 생성하고 이를 ReactDOM.render()로 전달하는 것이다.

React DOM은 해당 엘리먼트와 그 자식 엘리먼트를 이전의 엘리먼트와 비교하고 DOM을 원하는 상태로 만드는데 필요한 경우에만 DOM을 업데이트한다.

profile
하루가 모여 역사가 된다

0개의 댓글