한번쯤 들어본 FE 기술

[react] state와 생명주기

프론트루나 2024. 9. 17. 16:38
반응형

이전 섹션에서 엘리먼트 렌더링에서는 UI를 업데이트 하기 위해서 한 가지 방법만 배웠으며, 렌더링된 출력값을 변경하기 위해 root.render()를 호출했습니다.

Clock 컴포넌트를 완전히 재사용하고 캡슐화하는 방법을 배울 것 입니다. 이 컴포넌트는 스스로 타이머를 설정할 것이고 매번 스스로 업데이트 할 것입니다.

const root = ReactDOM.createRoot(document.getElementById('root'));

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

function tick() {
  root.render(<Clock date={new Date()} />);
}

setInterval(tick, 1000);

그러나 여기에는 중요한 요건이 누락되어 있습니다. Clock이 타이머를 설정하고 매초 UI를 업데이트 하는 것이 Clock의 구현 세부사항이 되어야 합니다. 이상적으로 한번만 코드를 작성하고 Clock이 스스로 업데이트 되도록 만들려고 합니다.

이것을 구현하기 위해서 Clock 컴포넌트에 "state"를 추가해야 합니다.
state는 props와 유사하지만, 비공개이며, 컴포넌트에 의해 완전히 제어됩니다.

함수에서 클래스로 변환하기

다섯단계로 Clock 과 같은 함수 컴포넌트를 클래스로 변환할 수 있습니다.

  1. React.Component를 확장하는 동일한 이름의 ES6 class를 생성합니다.
  2. render()라고 불리는 빈 메서드를 추가합니다.
  3. 함수의 내용을 render() 메서드 안으로 옮깁니다.
  4. render() 내용 안에 있는 props를 this.props로 변경합니다.
  5. 남아있는 빈 함수 선언을 삭제합니다.
class Clock extend React.Component {
	render(){
    	<div>
        	<h2> It is {this.props.date.toLocaleTimeString()}</h2>
        </div>
    }
}

render 메서드는 업데이트가 발생할 때마다 호출되지만, 같은 DOM 노드로 를 렌더링하는 경우 클락 클래스의 단일 인스턴스만 사용됩니다. 이것은 로컬 스테이트와 생명주기 메서드와 같은 부가적인 기능을 사용할 수 있게 해줍니다.

클래스에 로컬 State 추가하기

세 단계에 걸쳐서 date를 props에서 state로 이동해 보겠습니다.
1. render() 메서드 안에 있는 this.props.date를
this.state.date로 변경합니다.

  1. 초기 this.state를 지정하는 class contructor를 추가합니다.
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>
    );
  }
}

클래스 컴포넌트는 항상 Props로 기본 constrcutor를 호출해야 합니다.
요소에서 date prop을 삭제합니다.

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

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Clock />);

생명주기 메서드를 클래스에 추가하기

많은 컴포넌트가 있는 애플리케이션에서 컴포넌트가 삭제될 때 해당 컴포넌트가 사용중이던 리소스를 확보하는 것이 중요합니다. clock이 처음 DOM에 렌더링 될 때마다 타이머를 설정하려고 하비다. 이것은 react에서 "마운팅" 이라고 합니다.

또한 clock에 의해 생성된 DOM이 삭제될 때마다 타이머를 해제하려고 합니다. 이것을 react에서는 "언마운팅"이라고 합니다.

컴포넌트 클래스에서 특별한 메서드를 선언하여 컴포넌트가 마운트되거나 언마운트 될 때 일부 코드를 작동할 수 있습니다.

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

  componentDidMount() {
  }

  componentWillUnmount() {
  }

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

이러한 메서드들은 "생명주기 메서드" 라고 불립니다.
componentDidMount() 메서드는 컴포넌트 출력물이 DOM에 렌더링된 후에 실행됩니다. 이 장소가 타이머를 설정하기에 좋은 장소입니다.

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

this(this.timerID)에서 어떻게 타이머 ID를 설정하는지 주의해 주세요.
this.props가 react에 의해 스스로 설명되고 this.state가 특수한 의미가 있지만, 타이머 ID와 같이 데이터 흐름 안에 포함되지 않는 어떤 항목을 보관할 필요가 있다면, 자유롭게 클래스에 수동으로 부가적인 필드를 추가해도 됩니다.

componentWillUnmount() 생명주기 메서드 안에 있는 타이머를 분해해 보겠습니다.

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

마지막으로 Clock 컴포넌트가 매 초 작동하도록 하는 tick()이라는 메서드를 구현해 보겠습니다.
이것은 컴포넌트 로컬 state를 업데이트 하기 위해 this.setState()를 사용합니다.

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

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Clock />);
  1. 가 root.render()로 전달되었을 때 react는 클락 컴포넌트의 생성자(constructor)를 호출합니다. 클락 컴포넌트가 현재 시각을 표시해야 하기 때문에 시각이 포함된 객체로 this.state를 초기화 합니다. 나중에 이 state를 업데이트할 것 입니다.
  2. react는 clock 컴포넌트의 render() 메서드를 호출합니다. 이를 통해 react는 화면에 표시되어야할 내용을 알게 됩니다. 그 다음 react는 clock의 렌더링 출력값을 일치시키기 위해 DOM을 업데이트합니다.
  3. Clock 출력값이 DOM에 삽입되면, reacct는 componentDidMount() 생명주기 메서드를 호출합니다. 그 안에서 Clock 컴포넌트는 매초 컴포넌트의 tick()메서드를 호출하기 위한 타이머를 설정하도록 브라우저에 요처합니다.
  4. 매 초 브라우저가 tick()메서드를 호출합니다. 그 안에서 Clock 컴포넌트는 setState()에 현재 시각을 포함하는 객체를 호출하면서 UI 업데이트를 진행합니다. setState() 호출 덕분에 react는 state가 변경된 것을 인지하고 화면에 표시될 내용을 알아내기 위해 render() 메서드를 다시 호출합니다. 이때 render()메서드 안에 this.sate.date가 달라지고, 렌더링 출력값은 업데이트 된 시각을 포함합니다. react는 이에 따라 DOM을 업데이트 합니다.
  1. Clock 컴포넌트가 DOM으로부터 한번이라도 삭제된 적이 있다면 react는 타이머를 멈추기 위해 componentWillunMount() 생명주기 메서드를 호출합니다.
반응형