안녕하세요 Lannstark 입니다.

개인 플젝을 하다보니 본업인 백엔드 말고 프론트도 직접 건드려야 해서, 예전에 잠깐 공부했던 React를 다시 한 번 복습했습니다.

React 공식 Docu의 MAIN CONCEPTS을 읽으며 공부/정리한 내용을 공유합니다.

도움이 되셨으면 좋겠습니다 :)

총 2개의 포스트로 작성될 예정이며, ADVANCED GUIDES도 읽게 된다면 정리해서 포스팅에 올려보겠습니다.

Hello World

Hello World 렌더링

ReactDOM.render(
  <h1>Hello, world!</h1>,
  document.getElementById("root")
)

Introducing JSX

JSX, JS 문법을 확장시킨 것. UI가 어떻게 생겼는지 표현하기 위해서 JSX를 사용한다.

이런식으로 응용할 수 있다.

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

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

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

ReactDOM.render(
  element,
  document.getElementById('root')
);

string literal 속성을 표현하기 위해서 ""를 사용할 수 있다. 다른 속성을 표현하기 위해서는 {}를 사용해야 한다.

const element = <div tabIndex="0"></div>;
const element = <img src={user.avatarUrl}></img>;

ReactDOM은 camelCase를 사용한다. font-size 대신 fontSize를 사용해야 한다! 또한, class를 넣고 싶으면 className을 사용해야 한다.

태그쌍이 없다면 끝에는 항상 />가 붙어야 한다.

const element = <img src={user.avatarUrl} />;

기본적으로 React DOM은 렌더링 되기 전에 JSX에 값을 넣어버린다. 때문에 애플리케이션에 명시적으로 다른 값을 넣을 수 없다. 모든 것은 넣어지기 전에 문자열로 변환되며 때문에 XSS 공격을 막는데 도움을 준다.

Rendering Elements

렌더링을 해서 그려진 결과물은 동영상의 특정 시점과 같다. immutable하다.

또한, React는 전체를 다시 그리는 것이 아니라, 필요한 것만 update한다.

Components and Props

Component는 UI를 독립적으로 분리하여, 재사용할 수 있도록 해준다. 개념적으로 Component는 JS의 함수와 비슷하다. Component는 무작위의 input을 허용하며 스크린에 그릴 수 있는 React element를 반환한다.

function 키워드를 이용하여 component를 만들 수 있다.

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

하지만 보통 ES6 class 문법을 활용하는 것이 일반적이다. (컴포넌트는 대문자로 시작해야 한다.)

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

// 사용법
const element = <Welcome name="Sara" />;
ReactDOM.render(
  element,
  document.getElementById("root");
)

구체적으로 이런 순서로 코드가 호출된다.

  1. React.render() 안에 있는 element, 즉 가 호출되면
  2. React는 Welcome 컴포넌트를 {name: "Sara"}라는 prop과 함께 호출한다.
  3. Welcome 컴포넌트가 <h1>Hello, Sara</h1>를 반환한다.
  4. ReactDOM이 효율적으로 업데이트된다.

한 컴포넌트는 다른 컴포넌트를 부를 수 있다. 또한, 여러 작은 컴포넌트로 쪼개지는 것이 좋다.

예를 들어 Comment라는 하나의 큰 컴포넌트를 UserInfo와 Avatar를 이용해 중첩된 컴포넌트로 바꿀 수 있다.

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>
  );
}
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>
  );
}

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>
  );
}

Prop은 read-only이기 때문에 값을 변경할 수 없다. 모든 react component는 그들의 props에 관하여 하나의 pure function처럼 행동해야 한다.

State and Lifecycle

시간을 보여주는 component 하나를 생각해보자.

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

setInterval(tick, 1000);

우선 props를 이용해 이렇게 바꿀 수 있다.

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

function tick() {
  ReactDOM.render(
    <Clock date={new Date()} />,
    document.getElementById("root")
  )
}

setInterval(tick, 1000);

여기서 우리는 props 대신 state를 사용하기로 결정할 수 있다.

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      date: new Date()
    };
  }
  
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2> It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    )
  }
}

우리는 이 컴포넌트에 LifeCycle 메소드를 추가하기로 결정할 수 있다.

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }

  componentWillUnmount() {
    clearInterval(this.timerID);
  }

  tick() {
    this.setState({
      date: new Date()
    });
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

어려울게 없다.

setState()에 대해서 알아야 할 것이 세 가지 있다.

  1. state를 직접 변경하면 안된다.
    // Wrong
    this.state.comment = "Hello"
    
  2. state 업데이트는 비동기적으로 이루어진다. 동기적인 처리를 하고 싶다면 함수를 사용해야 한다.
    this.setState((state, props) => ({
      counter: state.counter + props.increment
    }));
    

    setState()에 function을 넣으면, 첫 번째 argument로 state를 받고 두 번째 argument로 그때 당시의 prop을 받는다.

  3. state를 업데이트 할 때 다른 프로퍼티 key를 사용하면 merge된다.

다음 포스트