React _ Component Lifecycle

연정·2021년 10월 31일
0

React

목록 보기
3/9
post-thumbnail

React의 Component Lifecycle 개념에 대해 알아보고,
그 내용을 코드 작성 시 어떻게 반영할 수 있는지에 대하여 정리해본다.

컴포넌트 생명주기

React 내의 모든 컴포넌트는 여러 종류의 “생명주기 메서드”를 가지며, 이 메서드를 활용하여 특정 시점에 코드가 실행되도록 설정할 수 있다. 즉, 컴포넌트 내의 특정 메소드는 각자의 생명주기를 가지며, 랜더링 시 지정된 순서에 맞추어 호출된다.

예를 들어, 아래의 메소드들은 컴포넌트의 인스턴스가 생성되어 DOM 상에 삽입될 때에 순서대로 호출된다.

  • constructor()
  • render()
  • componentDidMount()

자주 사용되는 생명주기 메소드

render()

class Login extends Component {
  render() {
  }
}

render() 메소드는 클래스형 컴포넌트에서 반드시 구현되어야하는 유일한 메서드이다. 해당 메소드 내부에는 페이지에 랜더링 되는 내용들이 정리되어 표현되어 있으며, state나 props에 변화*가 생길 때마다 새롭게 호출되어 변경을 반영한 새로운 버전을 화면에 랜더링 한다.

그러므로 render() 함수 내부에서는 state를 변화시키지 않고 브라우저와 직접적으로 상호작용을 하지 않아야 안정적인 코드를 만들 수 있다. 즉 render() 함수는 순수해야 한다!

*setState()와 같은 메소드는 컴포넌트가 가지고 있는 state값에 영향을 미치고,
render() 함수를 재실행 시킨다.


constructor()

class Login extends Component {
  constructor() {
    super();
    this.state = {
      id: '',
      password: '',
    };
  }
  
  render() {
  }
}

constructor()는 컴포넌트 내에서 이벤트 처리 메소드를 바인딩하거나 state를 초기화하는 경우 구현해야 하는 메소드이다. 생성자 메소드가 포함된 경우 생성자 메소드는 해당 컴포넌트가 마운트되기 전에 호출된다.


componentDidMount()

class Main extends Component {
  constructor() {
    super();
    this.state = {
      recommendUsersData: [],
    };
  }
  
  componentDidMount() {
      fetch('data/yeonjeong/recommendUsersData.json', {
      method: 'GET',
      })
      .then(res => res.json())
      .then(data =>
        this.setState({
          recommendUsersData: data,
        })
      );
  }
  
  render() {
  }
}

componentDidMount()는 컴포넌트가 마운트된 직후, 즉 트리에 삽입된 직후에 호출된다. 처음에 정리해놓은대로 render() 메소드 이후 호출된다고 생각하면 쉽다. 그러므로 외부에서 데이터를 불러와야 한다면, 네트워크 요청을 보내기 적절한 위치이다.
(아마도 페이지에 대한 전체적인 틀이 render() 함수를 통해 우선적으로 그려지고, 그 후 외부 데이터를 추가하는 방식으로 진행되는 게 효율적이기 때문에 일반적으로 componentDidMount() 내부에서 외부 데이터를 호출하는 것일거다..)

componentDidMount() 메소드를 활용하여 외부 데이터를 불러오는 경우는, 페이지에 랜더링 되어야 하는 데이터가 외부에 있을 경우이다. 만약 특정 조건을 성립할 경우에만 외부 데이터가 실행되어야 한다면 해당 메소드 내에 넣는 것은 적합한 선택이 아니다. 이럴 때는 별도의 함수를 생성하여 fetch()를 통해 외부 데이터를 연결하는 것이 더 적절하다.

예시)
Login 페이지의 경우, 페이지가 랜더링될 때가 아닌 로그인 조건이 성립할 경우에만
데이터가 불러와져야 하므로 componentDidMount() 메소드를 쓰는 건 부적절하다.

class Login extends Component {
  constructor() {
    super();

    this.state = {
      id: '',
      password: '',
    };
  }

  loginFormInput = e => {
    const { value, name } = e.target;
    this.setState({ [name]: value });
  };

  handleLogin = () => {
    const { id, password } = this.state;
    fetch('https://westagram-signup.herokuapp.com/signup', {
      method: 'POST',
      body: JSON.stringify({
        id: id,
        password: password,
      }),
    })
      .then(res => res.json())
      .then(result => console.log(result));
  };
  
  render() {
  }
}

컴포넌트 생명주기를 활용한 예

아래의 코드는 컴포넌트 생명주기를 활용하여 문제 상황을 해결한 예시이다.
구현하고자 했던 내용을 명확하게 보여주기 위해 필요 없는 코드는 삭제하였다.

구현하고 싶었던 사항은, 로그인 조건(ID에 '@'가 포함되고 Password가 5자 이상)이 충족되는 경우에만 로그인 버튼이 활성화되며 색상이 변화하는 것이었다. 최초에는 render() 바깥에 별도의 함수를 만들어 state 값이 변화한 뒤, 변화한 state값을 바탕으로 조건 충족 여부를 검사하였다. 하지만 그 방법을 쓸 경우에는 state값이 새롭게 업데이트 되기 전에 페이지가 랜더링되며 결과값이 1회씩 밀려 나타나는 오류가 발생하였다.

오류 해결을 위해 코드를 수정하면서는 컴포넌트 lifecycle의 특성을 활용, state값이 변화할 때마다 자연스럽게 호출되는 render() 함수 내에서 조건 충족 여부를 검사함으로써, 변화된 값이 바로 반영되어 랜더링 될 수 있도록 하였다.

class Login extends Component {
  constructor() {
    super();

    this.state = {
      id: '',
      password: '',
    };
  }

  loginFormInput = e => {
  };

  handleLogin = () => {
  };

  goToMain = e => {
  };

  render() {
    const { id, password } = this.state;

    const isIdValid = id.indexOf('@') !== -1;
    const isPwValid = password.length >= 5;

    return (
            <button
              onClick={this.handleLogin}
              className={isIdValid && isPwValid ? 'btn' : 'btn deactivated'}
              type="button"
              disabled={!isIdValid || !isPwValid}
            >
              로그인
            </button>
    );
  }
}

위에 언급한 메소드 외에도 생명주기를 가진 다양한 메소드들이 존재하나, 일단 가장 자주 쓰이고 현재 나에게 익숙한 내용들을 우선적으로 정리해보았다. 소소한 부분이지만 각각의 메소드들이 언제 어떤 순서로 호출되는지를 알고 역할을 이해하고나니 왜 코드가 이렇게 구성되어야 하는지를 명확하게 파악할 수 있었다.

profile
성장형 프론트엔드 개발자

0개의 댓글