클래스형 컴포넌트

박은정·2022년 5월 13일
1

TIL

목록 보기
50/70
post-thumbnail

지금은 함수형 컴포넌트를 주로 사용하지만 레거시 프로젝트 즉, npm 패키지나 stackoverflow에서 코드를 볼 때 과거의 클래스형 컴포넌트를 사용한 코드를 봐야하는 경우가 있기 때문에 클래스형 컴포넌트에 대해 알아보려 합니다.

또한 기존 클래스형 컴포넌트를 훅으로 변환하는 방법도 같이 알아보겠습니다.

클래스형 vs 함수형 컴포넌트의 차이

클래스형 컴포넌트

  • state 기능 및 lifecycle 기능을 사용할 수 있고
  • 임의 메서드를 정의할 수 있습니다.
  • 화면에 보여줄 JSX를 반환하는 render() 함수가 필수적으로 있어야 합니다.
import React, {Component } from 'react';

class App extends Component {
  render() {
    const name = 'react';
    return <div className='react'>{name}</div>;
  }
}

export default App;

이러한 클래스도 생성자라는 개념은 있었지만, ES5 이전에는 없었기 때문에 prototype 이라는 문법을 사용해서 작업을 했습니다. 이러한 내용은 추후에 다루려 합니다.

함수형 컴포넌트

  • 클래스형 컴포넌트보다 선언하기 훨씬 편합니다.
  • 메모리 자원을 클래스형 컴포넌트보다 덜 사용합니다.
  • 프로젝트를 완성해서 빌드한 후 배포할 때, 함수형 컴포넌트를 사용하는 것이 결과물의 파일크기가 더 작긴하지만, 함수형과 클래스형 컴포넌트는 성능과 파일 크기 면에서 사실상 별 차이가 없긴 합니다.
  • state와 lifecycle API의 사용이 불가능합니다만, 이러한 문제는 16.8버전 업데이트 이후 Hooks의 기능이 도입되면서 해결되었습니다.

LifeCycle 메서드

모든 리액트의 컴포넌트에는 라이프사이클 (=수명주기)이 존재합니다.
컴포넌트의 수명은 페이지에 렌더링되기 전인 준비 과정에 시작해서 페이지가 사라질 때 끝납니다.

  • Will 접두사가 붙은 메서드: 어떤 작업을 작동하기 에 실행되는 메서드
  • Did 접두사가 붙은 메서드: 어떤 작업을 작동한 에 실행되는 메서드

이러한 메서드들은 컴포넌트 클래스에 덮어 써 선언하면서 사용이 가능합니다.

마운트: 초기화단계

DOM이 생성되고 웹 브라우저 상에 나타나는 것을 마운트라고 합니다.
마운트일 때 호출하는 메서드는 아래와 순서대로 호출됩니다.

constructor → getDerivedStateFromProps → render → componentDidMount 

1. constructor

constructor(props) { 
  super(props);
  // 이하 내용 생략
}

컴포넌트를 새로 만들 때마다 호출되는 클래스 생성자 메서드입니다.
초기 상탯값 state를 정할 수 있습니다.

super()

props 매개변수는 컴포넌트의 기본속성값 defaultProps이 적용된 상태로 호출됩니다.
constructor 메서드 내부에서 반드시 super 함수를 호출해야 합니다.
super함수를 호출해야 React.Component 클래스의 constrouctor 메서드가 호출되기 때문에 super 함수를 호출하지 않으면 컴포넌트가 제대로 작동되지 않을 겁니다.

상탯값 할당

상탯값을 직접적으로 할당하는 것은 constructor 메서드에서만 허용되기 때문에 초기 속성값을 이용해서 상탯값을 정의할 때 사용됩니다.
다른 생명주기 메서드에서 상탯값을 변경할 때는 setState 메서드를 사용해야 합니다.

클래스 필드 (class fields)

한편, 자바스크립트의 표준이 되는게 거의 확실한 클래스필드를 사용하면 constructor 메서드를 사용하지 않고 아래와 같이 표현할 수 있습니다.

// 만약 상탯값이 항상 props의 age속성값에 의존적이라면 다른 방법을 사용해야 합니다.
class App extends React.Componet {
  state = {
    currentMovie: this.props.age < 10 ? '뽀로로" : "어벤져스";
  };
}

클래스 필드를 사용하면 constructor 메서드 없이 초기 속성값으로부터 상태값을 정의할 수있습니다. 이러한 클래스 필드 문법은 create-react-app에서도 지원합니다.

이후에 클래스 필드에 대해 다뤄보려 합니다.

작성중... 197p

2. getDerivedStateFromProps

static getDerivedStateFropProps(nextProps, prevState) {
  // 조건에 따라 특정 값 동기화
  if (nextProps.value !== preState.value) { value: nextProps.value };
  // if (nextProps.value !== preState.value) {
  //   return { value: nextProps.value };
  // }
  
  return null; // state를 변경할 필요가 없다면 null 반환
}

16.3버전 이후에 새로 생긴 라이프사이클 메서드입니다.
props로 받아온 값을 → state에 동기화시킵니다.
컴포넌트가 마운트되거나 업데이트될 때 호출됩니다.

3. ⭐ render

render() { ... }

UI를 렌더링합니다. 라이프사이클 메서드 중 유일한 필수 메서드입니다.
this.propsthis.state에 접근이 가능하고, 리액트 요소를 반환합니다.
일반적인 태그나 따로 선언한 컴포넌트 형태로 반환하거나
null이나 false를 반환하면 아무것도 보여지지 않습니다.

render 메서드 안에서는 이벤트 설정이 아닌기 때문에 setState를 사용하면 안되고, 브라우저의 DOM에 접근해서도 안됩니다.
따라서 DOM 정보를 가져오거나 state에 변화를 줄 때에는 componentDidMount에서 처리해야 합니다.

4. componentDidMount

componentDidMount() { ... }

컴포넌트를 만들고, 첫 렌더링을 다 마친 뒤 컴포넌트가 웹 브라우저 상에 나타난 후 호출합니다.
이 메서드 내부에 다른 자바스크립트 라이브러리나 프레임워크 함수를 호출하거나
이벤트 등록, setTimeout, setInterval, 네트워크 요청과 같은 비동기 작업을 처리하면 좋습니다.

업데이트

컴포넌트는 아래 4가지 경우에 의해 업데이트됩니다.

  • props 변경
  • state 변경
  • 부모 컴포넌트가 rerender될때
  • this.forceUpdate로 강제로 렌더링을 트리거할 때

props나 state가 변경되거나 부모컴포너트가 리렌더링되었을 때

1. getDerivedStateFromProps

마운트과정에서도 호출되며, 업데이트가 시작되기 전에도 호출됩니다.
props의 변화에 따라 state 값에 변화를 주고 싶을 때 사용합니다.

2. shouldComponentUpdate

sholudComponentUpdate(nextProps, nextState) { ... }

props 또는 state가 변경되었을 때, 컴포넌트가 리렌더링을 해야 할지 말지를 결정합니다.
이 메서드는 true 혹은 false 값을 반환해야 하며,
true를 반환하면 다음 라이프사이클 메서드를 계속 실행하고
false를 반환하면 작업을 중지하면서 컴포넌트가 리렌더링되지 않습니다.

컴포넌트를 만들 때 이 메서드를 따로 생성하지 않으면 기본적으로 언제나 true값을 반환합니다.
만약 특정함수에서 this.forceUpdate() 함수를 호출하면 이 과정을 생략하고 바로 render함수를 호출합니다.

현재 props와 state는 각각 this.props와 this.state로 접근하고
새로 선정될 props 또는 state는 각각 nextProps와 nextState로 접근이 가능합니다.

사용하는 경우

프로젝트 성능을 최적화할 때, 상황에 맞는 알고리즘을 작성해서 리렌더링을 방지할 때는 false값을 반환하게 합니다.

3. ⭐ render

컴포넌트를 리렌더링합니다.

4. getSnapBeforeUpdate

getSnapBeforeUpdate(prevProps, prevState) {
  if (prevState.array !== this.state.array) {
    const { scrollTop, scrollHeight } = this.list;
    return { scrollTop, scrollHeight };
  }
}

16.3버전 이후로 생긴 라이프사이클 메서드입니다.
render에서 만들어진 결과물(컴포넌트 변화)이 브라우저 DOM에 반영하기 바로 직전에 호출합니다.
이 메서드에서 반환하는 값은 componentDidUpdate에서 3번째 파라미터인 snapshot 값으로 전달받을 수 있습니다.
주로 스크롤바 위치 유지같이 업데이트하기 직전의 값을 참고할 때 활용됩니다.

5. componentDidUpdate

componentDidUpdate(prevProps, prevState, snapshot) { ... }

컴포넌트의 업데이트 작업(=리렌더링)이 끝난 후 호출합니다.
업데이트가 끝난 직후이므로, DOM 관련 처리를 해도 무방합니다.
prevProps 또는 prevState를 사용해서 컴포넌트가 이전에 가졌던 데이터에 접근이 가능합니다.
또 이전 단계인 getSnapshotBeforeUpdate에서 반환한 값이 있다면 snapshot값을 전달받을 수 있습니다.

언마운트: 소멸단계

마운트의 반대 개념으로, 컴포넌트를 DOM에서 제거하는 작업을 말합니다.

componentWillUnmount

componentWillUnmount () { ... }

컴포넌트를 DOM에서 제거해서 컴포넌트가 웹 브라우저 상에서 사라지기 전에 호출합니다.
이벤트, 타이머, 직접 생성한 DOM이 있다면 여기서 제거 작업을 해야 합니다.

예외발생시 호출되는 lifecycle 메서드

componentDidCatch

componentDidCatch(error, info) {
  this.setState({
    error: true
  });
  console.log({error, info});
}

16버전에서 새롭게 도입이 되었으며, 컴포넌트 렌더링 도중에 에러가 발생했을 때
애플리케이션이먹통이 되지 않고 오류 UI를 보여줄 수 있습니다.

여기서 error는 파라미터에 어떤 에러가 발생했는지 알려주며,
info 파라미터는 어디에 있는 코드에서 오류가 발생했는지에 대한 정보를 줍니다.

지금은 console.log()만 했지만, 실제로 사용할 땐 서버 API를 호출하여 따로 수집할 수도 있습니다.

하지만 이 메서드를 사용할 때는 컴포넌트 자신에게 발생하는 에러를 잡아낼 수 없고,
자신의 this.props.children으로 전달되는 컴포넌트에서 발생하는 에러만 잡을 수 있습니다.

// ErrorBoundary.js
class ErrorBoundary extends Components {
  state = {
    error: false
  };

  componentDidCatch(error, info) {
    this.setState({
      error: true
    });
    console.log({error, info});
  }

  render() {
    if (this.state.error) return <div>에러가 발생했습니다!</div>;
    return this.props.children;
  }
}

// App.js
function getRandomColor() {
  return '#' + Math.floor(Math.random() * 16777215).toString(16);
}

class App extends Component {
  state = {
    color:  "#000000"
  };
  handleClick = () => {
    this.setState({
      color: getRandomColor()
    });
  };
  render() {
    return (
      <div>
        <button onClick={this.handleClick}>랜덤색상</button>
		<ErrorBoundaty>
      		<LifeCycle color={this.state.color} />
        </ErrorBoundaty>
      </div>
    );
  }
}

에러가 발생하면 componentDidCatch 메서드가 호출되며, this.state.error값을 true로 업데이트합니다.
그리고 render 함수는 this.state.error 값이 true가 되었을 때 에러가 발생했다는 문구를 보여줍니다.

static getDerivedStateFromError

정리

라이프사이클 메서드는 컴포넌트 상태에 변화가 있을 때마다 실행하는 메서드입니다.
이 메서드들은 서드파티 라이브러리를 사용하거나 DOM을 직접 건드려야 하는 상황에 유용합니다.
추가로 컴포넌트 업데이트의 성능을 개선할 때는 shouldComponentUpdate가 중요하게 사용됩니다.

출처

profile
새로운 것을 도전하고 노력한다

0개의 댓글