Reactㅣ리액트 컴포넌트 라이프사이클, 마운팅, 갱신, 언마운팅

휘Bin·2023년 7월 1일
0
post-thumbnail

컴포넌트를 세밀하게 제어해야 할 경우가 생긴다. 예를 들면, 서버에 XHR 요청을 보내 정보를 가져오는 메뉴 컴포넌트를 개발해야 하는 상황이라던가 등등 무수히 많을 것이다.

세밀하게 제어하기 위한 방법으로 컴폰너트 인스턴스 생성 전에 필요한 로직을 구현한 후 새로운 속성을 제공해서 인스턴스를 재생성하는 방법도 있다. 하지만 이 방법으로는 독립적인 컴포넌트를 생성할 수 없어 React의 컴포넌트 기반 아키텍처 이점을 살리기 어렵다.

가장 좋은 방법은 '컴포넌트 라이프사이클 이벤트(component lifecycle events)'를 사용하는 것이다. 마운팅 이벤트를 이용해 컴포넌트에 필요한 로직을 주입할 수 있다.
이외에도 다른 이벤트를 이용하여 뷰가 다시 렌더링하는 것을 결정하는 특별한 로직을 제공해 좀 더 스마트한 컴포넌트를 만들 수도 있다.

React 컴포넌트 라이프사이클 이벤트?

React는 라이프사이클 이벤트를 기반으로 컴포넌트의 동작을 제어하고 사용자 정의를 할 수 있다. 프로그래밍에서 말하는 '후킹'과 비슷하다.
조금 더 설명하면,

  • 마운팅(mounting) 이벤트 : React 엘리먼트(컴포넌트 클래스의 인스턴스)를 DOM 노드에 추가할 때 발생

  • 갱신(updating) 이벤트 : 속성이나 상태가 변경되어 React 엘리먼트를 갱신할 때 발생

  • 언마운팅(unmounting) 이벤트 : React 엘리먼트를 DOM에서 제거할 때 발생

모든 React 컴포넌트는 라이프사이클 이벤트가 있다. 한 번만 실행되기도, 어떤 이벤트는 계속 실행되기도 한다.

라이프사이클 이벤트를 이용하면 컴포넌트의 작업 수행을 향상시키는 사용자 정의 로직을 구현할 수 있다.
예를 들면, 라이프사이클 이벤트 중 재렌더링 여부를 정하는 이벤트가 있다. 이를 이용하여 불필요하게 렌더링되는 것을 방지하여 성능을 개선할 수 있다. 등등 여러가지 많다.

이벤트 분류

React는 여러 컴포넌트 이벤트를 3가지 유형으로 정의한다.

  • 마운팅 : React가 이벤트를 한 번만 실행

  • 갱신 : React가 이벤트를 여러 번 실행

  • 언마운팅 : React가 이벤트를 한 번만 실행

라이프사이클 이벤트+counstructor()까지 포함해 컴포넌트의 전체 라이프사이클의 실행 순서를 보면 아래와 같다.(갱신은 여러 번 일어날 수 있다.)

  • constructor()
    : 엘리먼트를 생성하여 기본 속성과 상태를 설정할 때 실행

  • 마운팅
    1) componentWillMount() : DOM에 삽입하기 전에 실행

    2) componentDidMount() : DOM에 삽입되어 렌더링이 완료된 후 실행

  • 갱신
    1) componentWillReceiveProps(nextProps) : 컴포넌트가 속성을 받기 직전에 실행

    2) shouldComponentUpdate(nextProps, necxtState) : 컴포넌트가 갱신되는 조건을 정의해서 재렌더링을 최적화할 수 있으며 boolean값을 반환한다.

    3) componentWillUpdate(nextProps, nextState) : 컴포넌트가 갱신되기 직전에 실행

    4) componentDidUpdate(prevProps, prevState) : 컴포넌트가 갱신된 후에 실행

  • 언마운팅
    1) componentWillUnmount() : 컴포넌트를 DOM에서 제거하기 전에 실행되며, 구독한 이벤트를 제거하거나 다른 정리 작업을 수행할 수 있다.

this.forceUpdate()를 호출할 수 있고, 호출하는 경우 컴포넌트가 재렌더링된다. 이름에도 나오듯 갱신을 강제하는것이다. 안 쓰는 것을 권장하지만 어쩔 수 없이 상태나 속성을 갱신해서 원하는 대로 다시 렌더링할 수 없는 경우에는 쓸 수 밖에 없다.

이벤트 구현

라이프사이클 이벤트를 구현하기 위해서는 클래스에 메서드를 정의해야한다. React의 규칙이다. React는 이벤트 이름에 해당하는 메서드가 있는지 확인한다. React가 메서드를 찾으면 해당 메서드를 실행한다. 이벤트 이름은 자바스크립트의 다른 이름들과 마찬가지로 대소문자를 구분해서 작성해야 한다.

즉, React는 특정 메서드가 정의되어 있으면 컴포넌트의 실행주기 중에 그 메서드를 호출한다.

예를 들면, componentDidMount()를 정의하면 React는 컴포넌트 클래스의 엘리먼트가 DOM에 추가되었을 때 이 메서드를 호출하게 된다.

작성은 아래와 같이 한다.

class velog extends React.Component{
	componentDidMount(){
    ~~
    }
}

위 메서드를 정의하지 않으면 React는 이 이벤트에 대해 아무런 코드도 실행하지 않는다. 즉, 메서드 이름은 이벤트 이름과 일치해야 한다.

componentDidMount() 메서드 하나만 설명해보자면, 이 컴포넌트는 DOM에 추가될 때 실행된다. 그리고 이 메서드는 다른 프론트엔드 프레임워크나 라이브러리와 통합하는 코드나, 서버에 XHR 요청을 보내는 코드를 작성할 때 사용된다. 그 이유는, 라이프사이클의 시점에서는 컴포넌트의 엘리먼트가 실제 DOM에 반영되어 자식 엘리먼트를 포함한 모든 엘리먼트에 접근할 수 있기 때문이다.

마지막으로 이해를 돕기 위한 코드를 적어보자면,

class velog extends React.Component{
	componentWillMount(){
    	console.log(ReactDOM.findDOMNode(this))	
    }
    componentDidMount(){
    	console.log(ReactDOM.findDOMNode(this))
    }
    render(){
    return(
    )}
}

위 코드를 간단히 해석하자면, 위에는 null 값이 출력되고 밑에는 div가 출력된다.
이유는 메서드 이름을 보면 알 것이다!

마운팅 이벤트

마운팅 이벤트 유형은 모두 실제 DOM에 컴포넌트를 추가하는 것에 대한 이벤트이다. 마운팅은 React 엘리먼트가 DOM에 노출되는 것이라고 생각하면 맞다.

마운팅 이벤트의 종류는 아래와 같다.

  • componentWillMount() : React 엘리먼트가 실제 DOM에 곧 추가될 것을 알려줌

  • componentDidMount() : React 엘리먼트를 실제 DOM에 추가한 시점으로, 이 시점의 React 엘리먼트는 DOM 노드가 된다.

constructor()는 componentWillMount()보다 먼저 실행된다. 또한, React 엘리먼트를 먼저 렌더링하고 나서 DOM에 추가한다.(여기서의 렌더링은 컴포넌트 클래스의 render()를 호출하는 것을 말한다. 실제 DOM에 그리는 것은 아니다!).

1. componentWillMount()

컴포넌트 라이프사이클에서 단 한번만 실행된다. 실행 시점은 초기 렌더링 직전이다.

ReactDOM.render()를 호출해 React 엘리먼트를 브라우저에 렌더링하는 시점에서 실행된다. 이 과정은 브라우저와 프론트엔드에서 이뤄진다.

React 컴포넌트를 서버에서 렌더링하면 기본적으로 HTML 문자열을 얻을 수 있는데, 서버에는 DOM이 없으므로, HTML을 DOM에 추가하는 작업은 없지만, 서버 렌더링 과정에서도 componentWillMount()는 실행된다.

2. componentDidMount()

초기 렌더링을 마친 후에 실행된다. 브라우저에서만 한 번 실행되고, 서버 렌더링에서는 실행되지 않는다. XHR 요청처럼 브라우저에서만 실행해야 하는 코드를 구현할 때 편리하게 사용가능 하다.

componentDidMount()에서 자식 엘리먼트를 참조로 접근할 수 있다. 자식 컴포넌트의 componentDidMount() 메서드는 부모 컴포넌트의 componentDidMount()보다 먼저 호출된다.

componentDidMount() 이벤트는 다른 자바스크립트 라이브러리를 통합하기에 가장 적절한 위치이다. 예를 들면, 사용자 정보 목록이 실린 JSON 데이터를 가져온다던가 말이다.

사용자 정보를 가져오는 것에 대한 연습 코드? 예시 코드를 들어보면 아래와 같다.

class Users extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      users: []
    }
  }
  componentDidMount() {
    fetch(this.props['data-url'])
      .then((response)=>response.json())
      .then((users)=>this.setState({users: users}))
  }
  render() {
    return <div className="container">
      <h1>List of Users</h1>
      <table className="table-striped table-condensed table table-bordered table-hover">
        <tbody>{this.state.users.map((user)=>
          <tr key={user.id}>
            <td>{user.first_name} {user.last_name}</td>
            <td> {user.email}</td>
            <td> {user.ip_address}</td>
          </tr>)}
        </tbody>
      </table>
    </div>
  }
}

생성자에서 users를 빈 배열로 초기화한다. 이러면 render()에서 해당 상태가 존재하는지 확인 안해도 된다. 만약 초기화하지 않고 undefined 값으로 두면, 반복적으로 상태의 존재 여부를 확인하는 코드를 작성하거나 버그가 생길 수 있다.

갱신 이벤트

갱신 이벤트는 컴포넌트를 갱신할 때 쓰인다. 갱신 이벤트의 순서는 이러하다.

  1. componentWillReceiveProps(newProps)

  2. shouldComponentUpdate()

  3. componentWillUpdate()

  4. componentDidUpdate()

componentWillReceiveProps(new Props)

컴포넌트가 새로운 속성을 전달받을 때 실행된다. 이 단계를 '들어오는 속성의 전환' 이라고도 한다.

여기에는 컴포넌트에 새로운 속성을 받아오는 시점에 끼어들어 render()를 호출하기 전에 일부 로직을 추가할 수도 있다.

componentWillReceiveProps(new Props) 메서드는 새로운 속성을 인자로 받는다. 그리고 컴포넌트를 최초로 렌더링할 때는 실행되지 않는다. 이 메서드는 새로운 속성을 전달받고 다시 렌더링이 이뤄지기 전, 새로운 속성에 따라 상태를 변경하려는 경우에 유용하다. 기존 속성 값은 this.props 객체에 있다.

일반적으로 componentWillReceiveProps(new Props)에서 setState() 메서드를 호출해도 추가로 다시 렌더링이 발생하지는 않는다.

새로운 속성을 전달받을 때, 반드시 새로운 값을 가지고 있지 않을 수도 있기 때문이다.
componentWillReceiveProps(new Props)는 속성 값의 변경과 상관없이 재렌더링이 이뤄질 때마다 실행된다. 그러므로 newProps가 항상 현재 속성과 다른 값이라고 가정할 수는 없는 것이다.

또한, 재렌더링(render() 호출)이 반드시 실제 DOM의 변경을 의미하지도 못한다.
갱신을 할지 여부와 실제 DOM에서 무엇을 갱신할지에 대한 부분은 shouldComponentUpdate()와 보정과정에 위임되어 있다.

shouldComponentUpdate()

shouldComponentUpdate() 이벤트는 렌더링 직전에 실행된다. 렌더링은 새로운 속성이나 상태가 전달된 후에 이뤄진다. 또한, 초기 렌더링 시점이나 forceUpdate() 호출 시에는 실행되지 않는다.

shouldComponentUpdate()에서 false를 반환하도록 구현하면 React가 다시 렌더링되지 않게 할 수 있다. 이런 방법을 쓰는 경우는 변경된 부분이 없고, 불필요한 성능 저하를 피하고자 할 때 쓸 수 있다.
false를 반환하면 componentWillUpdate()와 componentDidUpdate()도 호출하지 않는다. 즉, 컴포넌트의 재렌더링을 제어할 수 있는 것이다.

componentWillUpdate()

componentWillUpdate()는 새로운 속성이나 상태를 받은 후 렌더링 직전에 호출된다. 초기 렌더링 시에는 호출되지 않는다. 갱신 전에 필요한 준비 작업을 처리할 때 이 메서드를 사용하고, 이 메서드 내에서 this.setState()를 사용하는 것은 추천하지 않는다. 컴포넌트를 갱신하는 중에 다시 갱신하도록 할 수 있기 때문이다.

shouldComponentUpdate()가 false를 반환하면 실행되지 않는다.

componentDidUpdate()

componentDidUpdate() 이벤트는 컴포넌트의 갱신 결과가 실제 DOM에 반영된 직후에 실행된다. 초기 렌더링 시에는 호출되지 않는다.

언마운팅 이벤트

DOM에서 요소를 분리하거나 제거하는 것을 말한다. 언마운팅 이벤트는 한 가지이며, 컴포넌트 라이프사이클의 마지막 유형이다.

componentWillUnmount()

componentWillUnmount() 이벤트는 DOM에서 컴포넌트가 제거되기 직전에 호출된다. 정리하기 위한 코드를 추가할 수 있는데, 타이머를 제거하거나, DOM 요소를 정리하거나, componentDidMount()에서 연결한 이벤트를 제거하거나 할 때 쓸 수 있다.

profile
One-step, one-step, steadily growing developer

0개의 댓글