[TIL] React 01: Basic

Junyong-Ahn·2019년 12월 9일
0

과거

목록 보기
3/5

React

리엑트가 뭐지

리엑트(React)는 Interactive UI를 손쉽게 만들 수 있게 해주는 자바스크립트 라이브러리이다. 웹사이트를 개발하는 방법은 여러가지가 있고, html+css+javascript를 통한 개발도 그 중 한가지다. 또 다른 한 가지는 React, Vue와 같은 프론트엔드 라이브러리를 이용하는 것이다.

오늘날의 웹 애플리케이션은 단순하게 정적인 html페이지 만을 서비스하지 않고 사용자와 끊임없이 상호작용 한다. 사용자와의 인터렉션이 많아짐에 따라 관리해야 할 DOM이 많아졌는데, 리엑트는 개발자가 기능 개발에만 집중할 수 있도록 도와주는 라이브러리이다. 리엑트는 컴포넌트(Component)라는 개념을 기반으로, 코드의 재활용성을 높임과 동시에, 직관적인 코드를 만들 수 있게 해준다. 또한, 리엑트는 노드를 사용하여 서버에 렌더할 수 있고, 리엑트 네이티브(React Native)를 사용해 모바일 앱을 구동시킬 수도 있다.

리엑트를 사용하기에 앞서 알아야 할 두 가지가 있다. 첫번째는 ES6 문법. 리엑트가 ES6 문법을 사용하기 때문인데, 특히 컴포넌트를 만들 때 class 키워드를 사용해야하기 때문이다. 두번째는 JSX. JSX는 자바스크립트의 확장 문법이다. 컴포넌트는 JSX에 맞춰서 리턴을 해줘야하고, Babel 이라는 녀석이 이를 JS코드로 변환시켜준다.

각 컴포넌트들은 자신들의 state를 객체 형태로 가지고 있고, 이 필요한 부분만 효과적으로 렌더링한다. 각 컴포넌트들은 자신들의 state를 지니고있고, 복잡한 UI들을 만들기 위해 사용한다.

또, 커다란 개념은 아니지만 다음 네가지를 주의해야한다.

1. 하나의 Element로 전체를 감싸야한다.
2. JSX내부에서 자바스크립트를 작성할 때는 {} 안에 한다.
3. if문을 사용할 수 없으므로 삼항연산자를 사용한다.
4. 클래스를 정의할 때는 className을 사용한다.

Component, props

컴포넌트(component)는 UI들을 독립적이고, 재사용이 가능하도록 나눌 수 있게 해준다. 컨셉은 자바스크립트의 함수와 비슷하다. prop 이라는 것을 입력으로 받고, 화면에 나타내야 할 react element를 반환한다. 참고로, 컴포넌트를 어떻게 선언하든지 입력(prop)은 변경하면 안된다.

리엑트 컴포넌트들을 render() 메소드를 지니고있다. render()는 입력 데이터를 가지고 작업하여 화면에 표시할 수 있다. 입력 데이터는 render() 안에서 this.props를 통해 접근이 가능하다.

함수형 컴포넌트

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

ES6 class 컴포넌트

class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

컴포넌트 예제: 컴포넌트 합성 및 추출을 통한 단순화

기본 코드

function Comment(props) {
  return (
    <div className="Comment">
      <div className="UserInfo">
        <img className="Avatar"
          src={props.author.avatarUrl}
          alt={props.author.name}
        />
        <div className="UserInfo-name">
          {props.author.name}
        </div>
      </div>
      <div className="Comment-text">
        {props.text}
      </div>
      <div className="Comment-date">
        {formatDate(props.date)}
      </div>
    </div>
  );
}

Avater, UserInfo 컴포넌트로 분리

function Comment(props) {
  return (
    <div className="Comment">
      <UserInfo user={props.author} />
      <div className="Comment-text">
        {props.text}
      </div>
      <div className="Comment-date">
        {formatDate(props.date)}
      </div>
    </div>
  );
}
function Avatar(props) {
  return (
    <img className="Avatar" src={props.user.avatarUrl} alt={props.user.name}/>
  );
}
function UserInfo(props) {
  return (
    <div className="UserInfo">
      <Avatar user={props.user} />
      <div className="UserInfo-name">
        {props.user.name}
      </div>
    </div>
  );
}

JSX, Element

JSX

자바스크립트의 확장 문법이다. 언뜻 보면 HTML과 비슷해보이고 개발자들에게 친숙한 문법을 사용하여 컴포넌트 렌더링을 구조화하는 방법을 제공한다. 반드시 JSX를 사용하여 작성해야하는 것은 아니며 순수 자바스크립트로 작성할 수도 있다. JSX를 사용한다면 웹 브라우저가 인식할 수 있도록 바벨과 같은 도구를 이용한 변환이 필요하다. JSX, React.createElement()를 사용한 엘리먼트 생성은 기능적으로 같으며 세번째 코드 처럼 시스템이 인식한다.

JSX를 사용한 엘리먼트(element) 선언

const element = (
  <h1 className="greeting"> Hello, world! </h1>
);

React.createElement()를 사용한 엘리먼트 선언

const element = React.createElement(
  'h1',
  {className: 'greeting'},
  'Hello, world!'
);

단순화된 구조

// 주의: 다음 구조는 단순화되었습니다
const element = {
  type: 'h1',
  props: {
    className: 'greeting',
    children: 'Hello, world!'
  }
};

Element

엘리먼트는 리엑트앱에서 가장 작은 단위이며, 화면에 표시할 내용을 기술한다. 브라우저 DOM 엘리먼트와 달리 React 엘리먼트는 일반 객체이며(plain object) 쉽게 생성할 수 있다. React DOM은 React 엘리먼트와 일치하도록 DOM을 업데이트한다. 컴포넌트와는 다른 개념이다. 엘리먼트는 컴포넌트의 구성요소이다.

Immutable Element
리엑트의 엘리먼트들은 immutable하다. 한번 만들어지면 그것의 자식노드나 특성들을 변경할 수 없다. UI를 업데이트하는 방법은 새로운 엘리먼트를 만들어서 ReactDOM.render()로 넘기는 방법 뿐이다.

간단한 엘리먼트 렌더링

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

수정된 부분만 업데이트하는 render()
아래 코드에서 tick()이 1초마다 실행되기 때문에, 화면 전체가 매번 렌더링이 될 것 같지만 그렇지 않다. render()는 이전의 엘리먼트와 비교하여, 변경이 있는 경우에만 DOM을 업데이트한다. 하지만, 이 방법도 효율적이지 못한 것 같다. 필요할 때만 업데이트 되기는 하지만, 엘리먼트 전체가 렌더링 된다. 엘리먼트는 건드리지 않고 시간만 바꼈으면 좋겠다. State & Life Cycle에서 수정 해보자

function tick(){
  const element = (
    <div>
      <h1>Hello, world!</h1>
      <h2>It is {new Date().toLocaleTimeString()}.</h2>
    </div>
  )
  ReactDOM.render(element, document.getElementById('root'));
}

setInterval(tick, 1000);

State, Life Cycle

State

state는 컴포넌트가 갖는 상태이다. 객체형태로 컴포넌트에 저장되며 setState()메서드를 통해서 상태를 수정할 수 있다. setState()를 통해 상태 정보가 변경되면, render() 메서드가 다시 호출되어 마크업이 다시 렌더링 된다.

바로 위에 있는 tick()에는 앞서 말한 것 처럼, 전체가 렌더링 되는 문제가 있다. 엘리먼트가 계속 렌더링 되는 것을 막고, 시간만 업데이트 하려면 캡슐화가 필요하다.

Encapsulating

// reusable and encapsulated
function Clock(props) {
  return (
    <div>
      <h1>Hello, world!</h1>
      <h2>It is {props.date.toLocaleTimeString()}.</h2>
    </div>
  );
}

function tick() {
  ReactDOM.render(
    // {date : new Date()} 를 props로 가지는 Clock 컴포넌트
    <Clock date={new Date()} />,
    document.getElementById('root')
  );
}

setInterval(tick, 1000);

하지만, Clock자체가 결국에는 타이머를 매번 설정하고 렌더링하기 때문에 <Clock date={new Date()} />이 부분을 변경해야한다. 함수형으로 선언된 컴포넌트를 class 로 선언하고, state를 설정해주자. render()는 컴포넌트에 변화가 있을 때 실행되는데, 아래 수정한 코드에서는 컴포넌트인 Clock은 한번만 생성이 되고, 그 안에서 render()가 일어나기 때문에 계속해서 컴포넌트 전체가 렌더되는 것을 피할 수 있다.

// Class와 state 적용
class Clock extends React.Component {
  constructor(props) {
    super(props); // Class components should always call the base constructor with props.
    this.state = {date: new Date()};
  }
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}
ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);

Life Cycle Method: React Lifecycle Methods – A Deep Dive

수많은 컴포넌트를 가지는 애플리케이션에서 컴포넌트의 생성(mounting)과 제거(unmounting)에 따른 자원 관리는 매우 중요하다. Clock 컴포넌트는 생성될 때마다 각각 다른 타이머를 만들수 있어야하며, 제거될 때는 자신의 타이머를 리셋할 수 있어야한다. 이렇게 컴포넌트의 생성, 제거 혹은 업데이트를 Life Cycle이라고 하고, 그 전후로 자동적으로 실행되는 메서드를 Life Cycle Method 라고 한다. Life Cycle Method들은 Class 선언 내부의 메서드로 존재한다.

// 컴포넌트 생성(Mounting)시 실행
componentDidMount() {
  this.timerID = setInterval( () => this.tick(), 1000 );
}
// 컴포넌트 제거(Unmounting)시 실행
componentWillUnmount() {
  clearInterval(this.timerID);
}

최종 코드

class Timer extends React.Component {
  constructor(props) {
    super(props);
    this.state = { seconds: 0 };
  }
  tick() {
    // State는 setState()를 통해서만 업데이트가 가능하다
    this.setState(state => ({
      seconds: state.seconds + 1
    }));
  }
  componentDidMount() {
    this.interval = setInterval(() => this.tick(), 1000);
  }
  componentWillUnmount() {
    clearInterval(this.interval);
  }
  render() {
    return (
      <div>
        Seconds: {this.state.seconds}
      </div>
    );
  }
}
ReactDOM.render(
  <Timer />,
  document.getElementById('timer-example')
);
  1. When is passed to ReactDOM.render(), React calls the constructor of the Clock component. Since Clock needs to display the current time, it initializes this.state with an object including the current time. We will later update this state.

  2. React then calls the Clock component’s render() method. This is how React learns what should be displayed on the screen. React then updates the DOM to match the Clock’s render output.

  3. When the Clock output is inserted in the DOM, React calls the componentDidMount() lifecycle method. Inside it, the Clock component asks the browser to set up a timer to call the component’s tick() method once a second.

  4. Every second the browser calls the tick() method. Inside it, the Clock component schedules a UI update by calling setState() with an object containing the current time. Thanks to the setState() call, React knows the state has changed, and calls the render() method again to learn what should be on the screen. This time, this.state.date in the render() method will be different, and so the render output will include the updated time. React updates the DOM accordingly.

  5. If the Clock component is ever removed from the DOM, React calls the componentWillUnmount() lifecycle method so the timer is stopped.

0개의 댓글