React 라이프 사이클

김진영·2024년 6월 20일
0

React

목록 보기
1/2

모든 리액트 컴포넌트에는 라이프 사이클이 존재한다. 컴포넌트의 수명은 페이지에 렌더링 되기 전 준비과정부터 시작해서 페이지에서 사라질 때 끝이난다. 라이프 사이클 메서드는 클래스형 컴포넌트에서만 사용할 수 있고 함수형 컴포넌트에서는 Hooks기능을 사용해 비슷한 작업을 처리할 수 있다.

라이프 사이클 Method이해

라이프 사이클 Method는 총 9가지 있다.

  • Will 접두사가 붙은 Method -> 작업을 작동하기 전에 실행
  • Did 접두사가 붙은 Method -> 작업을 작동한 후에 실행

라이프 사이클은 총 3가지 마운트, 업데이트, 언마운트 카테고리로 나뉜다.

  • 마운트(mount) -> DOM이 생성되고 웹 브라우저상에 나타나는 것

    • constructor: 컴포넌트를 새로 만들때 마다 호출되는 클래스 생성자
    • getDerivedStateFromProps: props에 있는 값을 state에 넣을 때 사용
    • render: UI를 렌더링
    • componentDidMount: 컴포넌트가 웹 브라우저에 나타난 후 호출하는 메서드
  • 업데이트 -> 4가지의 경우에 업데이트
    props가 바뀔 때, state가 바뀔 때, 부모 컴포넌트가 리렌더링될 때, this.forceUpdate로 강제 렌더링 트리거할 때

    • getDerivedStateFromProps: 마운트 과정에서도 호출되며, 업데이트가 시작하기 전에도 호출된다. props의 변화에 따라 state 값에도 변화를 주고 싶을 때 사용
    • shouldComponentUpdate: 컴포넌트가 리렌더링을 해야 할지 말아야 할지를 결정하는 Method, 이 Method에서는 true 또는 false 값을 반환해야 하며 true를 반환하면 다음 라이플 사이클 Method를 계속 실행하고, false를 반환하면 작업을 중지함. 특정 함수에서 this.forceUpdate함수를 호출하면 이 과정을 생략하고 render함수를 호출
    • render: 컴포넌트를 렌더링
    • getSnapshotBeforeUpdate: 컴포넌트 변화를 DOM에 반영하기 바로 직전에 호출
    • componentDidUpdate: 컴포넌트의 업데이트 작업이 끝난 후 호출
  • 언마운트(unmount) -> 마운트의 반대 과정, 컴포넌트를 DOM에서 제거하는 것

    • componentWillUnmount: 컴포넌트가 웹 브라우저상에서 사라지기 전에 호출

라이프 사이클 Method 자세히 살펴보기

render() 함수

이 메서드는 클래스형 컴포넌트 React를 사용해봤다면 아마도 매우 익숙할것이다. 이 Method는 컴포넌트 모양을 정의합니다 그렇기 때문에 가장 중요한 메서드라고 할 수 있고, 라이프 사이클 Method중 유일한 필수 Method이기도 합니다. 이 Method 안에서는 이벤트 설정이 아닌 다른곳에서 setState를 사용하면 안되며, 브라우저 DOM에도 접근하면 안된다. state에 변화를 주거나 DOM접근할 때는 componentDidMount에서 처리해야 한다.

constructor

constructor는 컴포넌트의 생성자 Method로 컴포넌트를 만들 때 처음으로 실행된다. 이 Method에서는 초기 state를 정할 수 있다.

getDerivedStateFromProps

getDerivedStateFromPropsreact v16.3이후에 새로 만든 라이프 사이클 Method입니다.
props로 받아온 값을 state에 동기화 시키는 용도로 사용하며, 컴포넌트가 마운트될 때랑 업데이트될 때 호출

  static getDerivedStateFromProps(nextProps, prevState) {
    // props에서 파생된 state를 동기화
    if (nextProps.value !== prevState.value) {
      return {
        value: nextProps.value,
      };
    }
    // 변경이 없으면 null 반환하여 state를 변경하지 않음
    return null;
  }

componentDidMount

componentDidMount는 컴포넌트가 마운트된 직후에 호출되는 라이프 사이클 메소드로, 주로 비동기 작업이나 이벤트 리스너 등록 등에 사용됩니다. 아래는 componentDidMount의 사용 예시

class ExampleComponent extends Component {
  // 초기 state를 설정합니다.
  state = {
    data: null,
    loading: true,
  };

  // 컴포넌트가 마운트된 직후에 호출됩니다.
  componentDidMount() {
    // 네트워크 요청을 통해 데이터를 가져옵니다.
    fetch('https://api.example.com/data')
      .then(response => response.json()) // 응답을 JSON으로 변환합니다.
      .then(data => this.setState({ data, loading: false })) // 데이터를 state에 저장하고 로딩 상태를 false로 변경합니다.
      .catch(() => this.setState({ loading: false })); // 오류가 발생하면 로딩 상태만 false로 변경합니다.

    // 윈도우 리사이즈 이벤트 리스너를 등록합니다.
    window.addEventListener('resize', this.handleResize);
  }

  // 컴포넌트가 언마운트되기 직전에 호출됩니다.
  componentWillUnmount() {
    // 리사이즈 이벤트 리스너를 제거합니다.
    window.removeEventListener('resize', this.handleResize);
  }

  // 리사이즈 이벤트 핸들러 함수입니다.
  handleResize = () => {
    console.log('Window resized');
  };

  // 컴포넌트를 렌더링합니다.
  render() {
    const { data, loading } = this.state;

    // 로딩 중에는 "Loading..."을, 로딩이 완료되면 데이터를 출력합니다.
    return loading ? <p>Loading...</p> : <div>Data: {JSON.stringify(data)}</div>;
  }
}

shouldComponentUpdate

shouldComponentUpdate는 컴포넌트가 업데이트되기 전에 호출되어 컴포넌트가 업데이트될지 여부를 결정합니다. 기본적으로 shouldComponentUpdatetrue를 반환하며, false를 반환하면 컴포넌트는 업데이트되지 않습니다. 이를 통해 불필요한 렌더링을 방지하고 성능을 최적화할 수 있습니다.

class ExampleComponent extends Component {
  state = {
    count: 0,
  };

  shouldComponentUpdate(nextProps, nextState) {
    // 현재 state의 count와 새로운 state의 count가 다를 때만 업데이트
    return this.state.count !== nextState.count;
  }

  increment = () => {
    this.setState((prevState) => ({ count: prevState.count + 1 }));
  };

  render() {
    console.log('Component re-rendered');
    return (
      <div>
        <p>Count: {this.state.count}</p>
        <button onClick={this.increment}>Increment</button>
      </div>
    );
  }
}

getSnpashotBeforeUpdate, componentDidUpdate

getSnapshotBeforeUpdate는 컴포넌트가 DOM에 업데이트되기 직전에 호출됩니다. 이 메소드는 DOM에서의 변경 사항을 반영하기 전에 특정 값을 캡처할 수 있게 합니다. 메소드가 반환한 값은 componentDidUpdate로 전달됩니다.

componentDidUpdate는 컴포넌트가 업데이트된 후에 호출됩니다. 이 메소드는 이전 propsstate를 참조하여 컴포넌트가 업데이트된 후에 실행해야 하는 작업을 수행하는 데 사용됩니다. 예를 들어, DOM 조작, 네트워크 요청, 스크롤 위치 복구 등이 가능합니다.

class ChatComponent extends Component {
  constructor(props) {
    super(props);
    this.chatContainerRef = React.createRef();
  }

  state = {
    messages: ['Hello', 'fffff', 'bye'],
  };

  // 컴포넌트가 DOM에 업데이트되기 직전에 호출됩니다.
  getSnapshotBeforeUpdate(prevProps, prevState) {
    // 스크롤 위치를 캡처합니다.
    if (prevState.messages.length < this.state.messages.length) {
      const chatContainer = this.chatContainerRef.current;
      return chatContainer.scrollHeight - chatContainer.scrollTop;
    }
    return null;
  }

  // 컴포넌트가 DOM에 업데이트된 후에 호출됩니다.
  componentDidUpdate(prevProps, prevState, snapshot) {
    // 캡처한 스크롤 위치를 복구합니다.
    if (snapshot !== null) {
      const chatContainer = this.chatContainerRef.current;
      chatContainer.scrollTop = chatContainer.scrollHeight - snapshot;
    }
  }

  render() {
    return (
      // ...
    );
  }
}

componentWillUnmount

componentWillUnmount는 컴포넌트가 DOM에서 제거되기 직전에 호출됩니다. 이 메소드는 타이머 제거, 이벤트 리스너 해제, 네트워크 요청 취소 등 정리 작업을 수행하는 데 사용됩니다.

class TimerComponent extends Component {
  state = {
    count: 0,
  };

  componentDidMount() {
    // 1초마다 count를 증가시키는 타이머 설정
    this.timerID = setInterval(() => {
      this.setState((prevState) => ({ count: prevState.count + 1 }));
    }, 1000);
  }

  componentWillUnmount() {
    // 타이머를 정리합니다.
    clearInterval(this.timerID);
  }

  render() {
    return <div>Count: {this.state.count}</div>;
  }

componentDidCatch

componentDidCatch는 컴포넌트의 하위 트리에 있는 컴포넌트에서 오류가 발생했을 때 호출됩니다. 이 메소드를 사용하여 오류를 잡고, 오류 메시지를 표시하거나 로깅하는 등의 작업을 수행할 수 있습니다.

class ErrorBoundary extends Component {
  state = {
    hasError: false,
    errorMessage: '',
  };

  // 하위 컴포넌트에서 오류가 발생했을 때 호출됩니다.
  componentDidCatch(error, errorInfo) {
    // 오류를 state에 저장합니다.
    this.setState({
      hasError: true,
      errorMessage: error.toString(),
    });
    // 오류 정보를 로깅하거나 외부 서비스에 전송할 수 있습니다.
    console.error("Error caught by ErrorBoundary:", error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      // 오류가 발생하면 사용자에게 표시할 내용을 정의합니다.
      return <h1>Something went wrong: {this.state.errorMessage}</h1>;
    }

    // 오류가 발생하지 않으면 하위 컴포넌트를 렌더링합니다.
    return this.props.children;
  }
}

class BuggyComponent extends Component {
  render() {
    // 일부러 오류를 발생시킵니다.
    throw new Error("I crashed!");
    return <div>Buggy Component</div>;
  }
}
function App() {
  return (
    <ErrorBoundary>
      <BuggyComponent />
    </ErrorBoundary>
  );
}

리액트의 라이프 사이클 메소드들은 컴포넌트의 생성, 업데이트, 제거 과정에서 다양한 작업을 수행할 수 있도록 도와줍니다. 이를 통해 상태 관리, DOM 조작, 이벤트 처리, 에러 처리 등을 효율적으로 수행할 수 있습니다. 클래스형 컴포넌트에서 이러한 메소드를 적절히 사용함으로써 더욱 안정적이고 성능 최적화된 애플리케이션을 개발할 수 있습니다.

Happy Coding!

0개의 댓글