공부

리액트 Docs - 주요개념

ko.reactjs.org/docs/getting-started.html

 

시작하기 – React

A JavaScript library for building user interfaces

ko.reactjs.org

리액트 문서 다시 찬찬히 보면서 공부하기

 


주요개념

 

JSX

 

- JS를 확장한 문법으로 UI가 어떻게 생겨야 하는지 설명하기 위해 React와 함께 사용함. JS의 모든 기능이 포함되어 있음.

 

- JSX는 React 엘리먼트를 생성한다.

 

- 리액트에서는 이벤트가 처리되는 방식, 시간에 따라 state가 변하는 방식, 화면에 표시하기 위해 데이터가 준비되는 방식 등 렌더링 로직이 본질적으로 다른 UI로직과 연결된다는 사실을 받아들인다.

 

- 리액트는 별도의 파일에 마크업과 로직을 넣어 인위적으로 분리하는 대신, 둘 다 포함하는 '컴포넌트'라고 부르는 느슨한 연결 유닛을 사용한다.

 

- JSX없이 리액트를 사용할 수도 있다. React.createElement를 사용하면 되는데 쓰기 피곤할것이다.

 

- { } 중괄호로 감싸 js 표현식을 사용할 수 있다.

 

- JSX또한 표현식이다. 컴파일이 끝나면, JSX표현식이 정규 js 함수 호출이 되고 js 객체로 인식된다. 함수의 리턴값으로 jsx를 사용할 수 있다.

 

- JSX는 주입 공격을 방지한다. 삽입된 모든 값을 렌더링 하기 전에 이스케이프('<'를 '&lt;'으로 바꾸는 등 보안에 위협이 될 문자를 안전한 문자로 치환)시킨다.


엘리먼트 렌더링

 

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

 

- 브라우저의 DOM 엘리먼트와 달리 React 엘리먼트는 일반 객체이며 쉽게 생성할 수 있다. 

 

- React DOM에서 React 엘리먼트와 일치시키도록 DOM을 업데이트한다.

 

- 컴포넌트와 엘리먼트는 다른것이다. 컴포넌트 챕터에서 설명함.

 

- 리액트 엘리먼트를 root라는 id의 div에 렌더링하려면 ReactDOM.render()로 전달하면 된다.

 

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

 

- React 엘리먼트는 불변객체이다. 생성 이후 엘리먼트의 자식이나 속성을 변경할 수 없다.

 

- UI를 업데이트 하기 위해선 새로운 엘리먼트를 생성하고 ReactDOM.render()로 전달해야 한다.

 

 

function tick( ) {
	const element = (<div>Time is {new Date().toLocaleTimeString()}</div>;
    ReactDOM.render(element, document.getElementById('root'));
}

setInterval(tick, 1000);

이 코드는 매 초 엘리먼트를 생성하고 새로 리액트돔에서 렌더링한다..

 

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

 


컴포넌트와 props

 

- 컴포넌트를 통해 UI를 재사용 가능한 개별적인 여러 조각으로 나누고, 각 조각을 개별적으로 살펴볼 수 있다.

 

- 컴포넌트는 js 함수와 유사하다. props라는 임의의 입력을 받은 후, 화면에 어떻게 표시되는지를 기술하는 엘리먼트를 반환한다.

 

- 함수형 컴포넌트와 클래스 컴포넌트가 있다.

 

- 리액트 엘리먼트는 사용자 정의 컴포넌트로도 나타낼 수 있다. 사용자 정의 컴포넌트로 작성한 엘리먼트를 발견하면 JSX 어트리뷰트와 자식을 해당 컴포넌트에 단일 객체로 전달한다. 이 객체를 props라고 함.

 

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

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

 

렌더링 순서

 

1. <Welcome name="Sara" /> 엘리먼트로 ReactDOM.render()를 호출함

2. { name : 'Sara' }를 props로 Welcome 컴포넌트 호출.

3. Welcome 컴포넌트는 결과적으로 <h1>Hello, Sara</h1> 엘리먼트를 반환함.

4. React DOM은 <h1>Hellow, Sara</h1> 엘리먼트와 일치하도록 DOM을 효율적으로 업데이트함.

그냥 함수호출이랑 비슷하다.

 

- 컴포넌트 이름은 항상 대문자로 시작해야한다.

 

- 컴포넌트는 자신의 출력에 다른 컴포넌트를 참조할 수 있다.

 

- 컴포넌트를 추출해서 재사용이 가능한 작은 컴포넌트들로 만들자.

 

- props는 읽기 전용이다. 모든 리액트 컴포넌트는 자신의 props를 다룰 때 반드시 순수 함수처럼 동작해야한다. 동적인 값이 필요하면 state를 쓰면 된다.

 

 


State 와 라이프사이클

 

- state는 props와 유사하지만, 비공개이며 컴포넌트에 의해 완전히 제어된다.

 

- 위의 시간을 출력하는 tick함수를 state를 사용하는 컴포넌트로 수정하면 아래와 같다.

 

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

ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);

 

실행 순서

1. ReactDOM.render()가 Clock 컴포넌트의 constructor를 호출한다. Clock은 현재 시각이 포함된 객체로 this.state를 초기화 한다.

2. 리액트는 Clock 컴포넌트의 render() 메서드를 호출한다. 이를 통해 리액트는 화면에 표시되어야 할 내용을 알게되고, Clock의 렌더링 출력값을 일치시키기 위해 DOM을 업데이트한다.

3. Clock 출력값이 DOM에 삽입되면, 리액트는 componentDidMount() 생명주기 메서드를 호출한다. 그 안에 Clock 컴포넌트는 매초 컴포넌트의 tick() 메서드를 호출하기 위한 타이머를 설정하도록 브라우저에 요청한다.

4. 매초 브라우저가 tick() 메서드를 호출한다. 그 안에서 Clock 컴포넌트는 setState()에 현재 시각을 포함하는 객체를 호출하면서 UI 업데이트를 진행한다. setState()호출로 리액트는 state가 변경된 것을 인지하고 화면에 표시된 내용을 인지하기 위해 render를 다시 호출한다. 이때 렌더링 출력값은 업데이트 된 시각을 포함한다.

5. Clock 컴포넌트가 돔에서 삭제된다면 리액트는 타이머를 멈추기 위해 componentWillUnMount() 생명주기 메서드를 호출한다.

 

- 라이프사이클 : 마운트(업데이트) -> 렌더 -> 종료시 언마운트

 

state 올바르게 사용하기

 

- 직접 State를 수정하면 컴포넌트가 리렌더링 되지 않기 때문에 setState()를 사용해야한다. this.state를 직접 지정할 수 있는 유일한 공간은 constructor

 

- state 업데이트는 비동기적일 수 있다. 리액트의 성능을 위해 setState() 호출을 단일 업데이트로 한꺼번에 처리할 수 있다. 그리고 브라우저의 최소한으로 보장해야하는 렌더링을 위해 state 업데이트가 늦춰질 수도 있다.

 

- state 업데이트는 병합된다.

 

 

 

 

- 데이터는 아래로 흐르기 때문에 부모나 자식 컴포넌트에서 모두 특정 컴포넌트가 유상태인지 무상태인지 알 수 없고, 그들이 함수나 클래스로 정의되었는지에 대해 관심가질 필요가 없다. 이를 하향식 또는 단방향식 데이터흐름이라고 한다.

 

 


이벤트 처리하기

 

- DOM엘리먼트에서 이벤트를 처리하는 방식과 유사하다. 문법차이만 조금있다.

    - React의 이벤트는 소문자 대신 캐멀 케이스를 사용한다.

    - JSX를 사용하여 문자열이 아닌 함수로 이벤트 핸들러를 전달한다.

 

- 리액트에서는 false를 반환해도 기본 동작을 방지할 수 없다. 반드시 preventdefault를 명시적으로 호출해야 한다.

 

- 이벤트 핸들러의 인자 e는 합성이벤트이다. 리액트는 W3C 명세에 따라 합성이벤트를 정의하기 때문에 브라우저 호환성에 대해 걱정할 필요가 없다.

 

- DOM 엘리먼트가 생성된 후 리스너를 추가하기위해 addEventListener를 호출할 필요가 없다. 대신 엘리먼트가 처음 렌더링될 때 리스너를 제공하면 된다.

 

 

 


조건부 렌더링

 

- 리액트에서 원하는 동작을 캡슐화하는 컴포넌트를 만들 수 있다. 이렇게 하면 애플리케이션의 상태에 따라서 컴포넌트 중 몇 개만을 렌더링할 수 있다. 조건문으로 리턴할 컴포넌트를 선택하면 됨.

 


 

리스트와 키

 

- JSX내의 중괄호에서 배열의 map 함수를 활용하여 배열내에 있는 요소를 참조해 엘리먼트를 만들어 낼 수 있음.

 

- 이 때 각 항목에 key값을 넣어 줘야 한다. key는 리액트가 어떤 항목을 변경, 추가 또는 삭제할지 식별하는 것을 돕는다.

 

- key값으로 가장 좋은 방법은 리스트의 다른 항목들 사이에서 해당 항목을 고유하게 식별할 수 있는 문자열을 사용하는 것이다.

 

- 안정적인 key값이 없다면 최후의 수단으로 인덱스를 key로 사용하라.

 

- 항목의 순서가 바뀔 수 있는 경우 key에 인덱스를 사용하는 것은 권장하지 않는다. 이로 인해 성능이 저하되거나 컴포넌트의 state와 관련된 문제가 발생할 수 있다. - medium.com/sjk5766/react-%EB%B0%B0%EC%97%B4%EC%9D%98-index%EB%A5%BC-key%EB%A1%9C-%EC%93%B0%EB%A9%B4-%EC%95%88%EB%90%98%EB%8A%94-%EC%9D%B4%EC%9C%A0-3ce48b3a18fb 

 

[React] 배열의 index를 key로 쓰면 안되는 이유

React 공식 Document를 보다가 아래 노란색으로 캡쳐한 부분을 보게 되었고 확인한 사항들을 정리합니다.

medium.com

- 리액트는 key가 동일 할 경우 동일한 DOM Element를 보여준다.

 

 


 

- HTML 폼 엘리먼트는 폼 엘리먼트 자체가 내부 상태를 가지기 때문에, 리액트의 다른 DOM 엘리먼트와 조금 다르게 동작한다.

 

- HTML에서 <input>, <textarea>, <select>와 같은 폼 엘리먼트는 일반적으로 사용자의 입력을 기반으로 자신의 state를 관리하고 업데이트한다. 리액트에서는 변경할 수 있는 state가 일반적으로 컴포넌트의 state 속성에 유지되며 setState()에 의해 업데이트 된다.

 

- 그래서 리액트는 제어 컴포넌트들에 대해 이벤트 핸들러를 따로 작성해 줘야 함.

 

예시 생략

 


State 끌어 올리기

 

- 종종 동일한 데이터에 대한 변경사항을 여러 컴포넌트에 반영해야 할 필요가 있다. 이럴 때 가장 가까운 공통 조상으로 state를 끌어올리는 것이 좋다.

 


합성 vs 상속

 

- 리액트는 강력한 합성 모델을 가지고 있으며, 상속 대신 합성을 사용하여 컴포넌트 간에 코드를 재사용하는 것이 좋다.

 

- children prop을 사용하면 자식엘리먼트를 그대로 출력할 수 있다.

function FancyBorder(props) {
  return (
    <div className={'FancyBorder FancyBorder-' + props.color}>
      {props.children}
    </div>
  );
}

function WelcomeDialog() {
  return (
    <FancyBorder color="blue">
      <h1 className="Dialog-title">
        Welcome
      </h1>
      <p className="Dialog-message">
        Thank you for visiting our spacecraft!
      </p>
    </FancyBorder>
  );
}

 

- 자식컴포넌트에 props 이름을 붙여서 각 컴포넌트마다 다른 기능을 수행할 수도 있다.

function SplitPane(props) {
  return (
    <div className="SplitPane">
      <div className="SplitPane-left">
        {props.left}
      </div>
      <div className="SplitPane-right">
        {props.right}
      </div>
    </div>
  );
}

function App() {
  return (
    <SplitPane
      left={
        <Contacts />
      }
      right={
        <Chat />
      } />
  );
}

 

 

 

 

 

 

'공부' 카테고리의 다른 글

CORS  (0) 2020.12.29
리액트 Docs - 고급 안내서  (0) 2020.12.16
실용적인 프론트엔드 전략 3  (0) 2020.12.10
실용적인 프론트엔드 테스트 전략 - 2  (0) 2020.12.09
실용적인 프론트엔드 테스트 전략 - 1  (0) 2020.12.09