렌더링 : html 요소(element), 또는 React 요소 등의 코드가 눈으로 볼 수 있도록 그려지는 것을 렌더링(rendering) 이라고 말합니다. 전체 HTML이 업데이트 되는 것이 아니라 변화하는 부분의 엘리먼트만 바뀌는 것을 알 수 있습니다.
리액트에서 가장 핵심이라고 볼 수 있는 컴포넌트는 생명 주기(=라이프사이클=Life cycle)를 가집니다.
컴포넌트는
생성 (mount)
업데이트 (update)
제거 (unmount)
라는 과정을 거치는데 각각의 과정마다 특정한 함수(메서드)를 실행하며 이 함수들을 생명주기함수(생명주기 메서드)라고 합니다.
내용에 들어가기에 앞서 생명주기함수는 클래스컴포넌트에서 적용이 가능하고 함수 컴포넌트에서의 생명주기는 훅(hooks)를 이용해 적용이 가능하다.
이번 포스팅에서는 class컴포넌트에서의 생명주기에 대해 알아보겠습니다.
출처 : https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
생명주기 함수는 총 9종의 함수가 있으며 위 이미지에서 보는데로 생명주기 함수가 호출됩니다.
그럼 각 단계별로 어떤 함수들이 호출되는지 알아봅시다.
: 컴포넌트의 인스턴스가 생성되어 DOM 상에 삽입될 때에 순서대로 호출된다
constructor()
getDerivedStateFromProps()
render()
componentDidMount()
: 해당 함수는 '맨 처음 생성될 때 한 번만 호출'되며 . this.state에 객체를 할당하여 지역 state를 초기화
. 인스턴스에 이벤트 처리 메서드를 바인딩 하는데 사용됩니다. 따로 상태를 초기화하거나 메서드를 바인딩하는 작업이 없다면 생성자를 구현하지 않아도 됩니다.
constructor()함수를 정의할 떄는 super()함수를 가장 위에 호출해야 합니다.
super()함수는 프로퍼티와 생명 주기 상태 등을 초기화합니다.
또 constructor()내부에서만 this.state를 직접 할당할 수 있으며 그 외의 메서드에서는 this.setState()를 사용해야합니다.
: 최초 마운트 시,업데이트 시 모두에서 render()를 호출하기 직전에 호출되는 함수입니다.
이 함수는 정적함수이기 떄문에 this.props나 this.state 같은 방법으로 props,state에 접근할 수 없습니다. 접근하기 위해서는 인자로 전달받은 props,state를 이용해야 하며, 여기서의 props는 상위컴포넌트에서 전달된 값, state는 현재 컴포넌트의 state값입니다.
따라서 상위컴포넌트에서 전달받은 프로퍼티로 state값을 연동할 때 주로 사용되며, 반환값으로 state를 변경합니다.
class컴포넌트에서 반드시 구현돼야하는 유일한 메서드로 새 화면을 그릴 떄 자동으로 호출되는 함수 입니다.
render()함수가 반환하는 JSX를 화면에 나타내줍니다.
render()함수가 JSX를 화면에 나타낸 후 호출되는 함수로 컴포넌트가 화면에 모두 표현된 이후 필요한 작업들은 이 메서드에서 진행합니다.
: props나 state가 변경되면 렌더(업데이트)가 진행되며 아래 순서대로 호출된다.
getDerivedStateFromProps()
shouldComponentUpdate()
render()
getSnapshotBeforeUpdate()
componentDidUpdate()
(생성단계에서 설명한 함수와 중복되는 함수에 대한 설명은 생략하겠습니다.)
: 프로퍼티를 변경하거나 setState()함수를 호출하여 state값을 변경하면 '화면을 새로 출력해야 하는지' 판단하는 함수입니다
이 메서드는 화면을 출력할지 말지 판단해 데이터 변화를 비교하는 작업을 포함하므로 리액트 성능에 영향을 줍니다.
화면 변경을 위한 검증 작업을 해야 하는 경우 이 함수를 사용하면 됩니다.
: 컴포넌트에서 업데이트 된 내용이 가상 화면에 출력된 이후에 호출되는 함수입니다. 가상 화면에 출력했기 때문에 화면에 실제로 출력되기 전에 호출되는 함수이며, 출력될 요소(element)의 크기나 스크롤 위치 등의 DOM 정보에 접근할 때 사용됩니다.
: 컴포넌트가 실제 화면에 출력된 후에 호출되는 함수입니다.
이 함수는 부모 컴포넌트에서 전달된 이전 props와 이전 state값, getSnapshotBeforeUpdate()함수에서 반환된 값을 인자로 전달받아 이 인자들을 이용해 스크롤 위치 변경, 커서 이동 등의 DOM 정보 변경 시 사용됩니다.
: 컴포넌트가 DOM에서 제거될 떄 호출된다.
: 컴포넌트가 소멸되지 직전 이 함수가 호출되며 타이머 제거, componentDidMount()에서 생성된 작업 등을 정리 할 때 사용됩니다.
이 메소드를 사용하여 해제 작업을 하지 않으면 메모리 누수 현상이 발생할 수 있습니다.
React.PureComponent는 사실 React.Component와 비교해서 딱 한가지만 다르다! 그것은 바로 shouldComponentUpdate를 어떻게 쓰는가 하는 부분이다.
자 그러면 이제, React.Component와 React.PureComponent가 각각 어떻게 컴포넌트 업데이트 여부를 결정하고 컴포넌트를 업데이트하는지 확인해보도록 하자.
import React, { Component } from 'react';
class BlahBlah extends Component {
state = {
count: 0
}
counter () {
this.setState({
count: this.state.count // 주목! 값이 변경되지는 않으나, setState는 써줌!!
})
}
componentDidUpdate () {
console.log(this.state.count); // 컴포넌트가 업데이트되면 값을 출력시킨다.
}
render () {
return (
<div>
{ this.state.count }
<button onClick={this.counter.bind(this)}>counterButton</button>
</div>
)
}
}
export default BlahBlah;
이렇게 작성한 뒤 브라우저에서 콘솔창을 열고 counterButton을 광클해보자.

React.Component는 결론적으로 shouldComponentUpdate를 따로 설정해주지 않은 경우, 항상 true를 반환한다. setState가 실행되면 state, props의 변경 여부를 신경쓰지 않고 무조건적으로 컴포넌트를 업데이트시킨다는 것이다.
그렇기 때문에 위의 코드에서는 count의 값은 변경되지 않았지만, setState가 실행되었기 때문에 컴포넌트 업데이트가 일어나 componentDidUpdate가 실행된 모습이다.
그렇다면 React.PureComponent에서는 어떨까? 위 코드에서 Component만 PureComponent로 바꾸어주면 된다.
import React, { PureComponent } from 'react';
class BlahBlah extends PureComponent {
state = {
count: 0
}
counter () {
this.setState({
count: this.state.count // 주목! 값이 변경되지는 않으나, setState는 써줌!!
})
}
componentDidUpdate () {
console.log(this.state.count); // 컴포넌트가 업데이트되면 값을 출력시킨다.
}
render () {
return (
<div>
{ this.state.count }
<button onClick={this.counter.bind(this)}>counterButton</button>
</div>
)
}
}
export default BlahBlah;
위와 같이 Component를 PureComponent로 변경해준 후 다시 한 번 콘솔창을 열고 counterButton을 광클해보자. 콘솔창에 아무 것도 나타나지 않을 것이다. 값이 변경되지 않았으므로, 컴포넌트 업데이트는 일어나지 않는다.
그렇다면 PureComponent는 정확히 언제 어떻게 컴포넌트 업데이트를 수행하는 것일까?
PureComponent는 shouldComponentUpdate에서 얕은 비교를 통해 업데이트 여부를 결정한다.
얕은 비교란 무엇일까? 얕은 비교의 특징은 다음과 같다.
PureComponent는 위의 조건으로 현재 state, props와 바뀔 state, props를 비교하여 업데이트 여부를 결정하게 되는 것이다. 그렇기 때문에 위의 코드에서 setState는 실행되었지만, count의 값이 바뀌지 않아 컴포넌트가 업데이트되지 않았다!
다음은 내 개인적인 Thinking이다.
shouldComponentUpdate를 만지지 않아도 되거나 ~만지고 싶지 않을때~완전히 동일한 객체라도 새로 선언하면 다른 참조 값을 가짐
this.state={
{StateStringObj:['react',{'react':200}]}
}
...
this.setState({StateStringObj:['react',{'react':200}]})
shallowEqualArrays() : 문자열과 배열의 값과 참조를 비교함
책에는 PureComponent에서 state 값의 변경을 비교하는 함수라 나와있으나 검색결과 Redux에서 useSelector를 최적화하기 위해 주로 사용되는 듯
state가 없고 생명주기 함수를 사용할 수 없음
상위 컴포넌트에서 props와 context를 파라미터로 전달받아 사용
render 말고 return 사용
componenetDidMount와 같이 렌더링 이후 실행
최초로 페이지가 로딩될 때 한 번 실행되고 setStatus() 함수로 state값이 변경될 때 한 번 더 실행됨.
array에 저장된 element들을 순서대로 꺼냄
class R033_ReturnMap extends Component {
render() {
const element_Array = [
<li>react</li>
, <li>200</li>
, <li>Array map</li>
]
return (
<ul>
{element_Array.map((array_val) => array_val)}
</ul>
)
}
}