본문 바로가기
[ Programming ] Basic/React

[리액트(React) 학습자를 위한 기초지식] 컴포넌트 생명주기(Lifecycle of Components) - #1 Mounting (1/3)

by the_little_coder 2020. 2. 10.

리액트(React) 학습자를 위한 기초지식

컴포넌트 생명주기(Lifecycle of Components) - #1 Mounting (1/3)



리액트 컴포넌트에는 생명주기가 있습니다. 컴포넌트의 생명주기라 함은 컴포넌트가 처음으로 import되어 DOM을 형성하는 단계, 형성된 컴포넌트 내부를 수정하는 단계, 마지막으로 컴포넌트를 사용하지 않겠다고 선언하는 단계로 말 그대로 컴포넌트가 탄생하여 소멸되기까지 일련의 과정입니다. 이러한 생명주기 안에서는 특정 시점에서 자동으로 호출되는 생명주기와 관련된 함수들이 있습니다. 그리고 컴포넌트가 실행될 때, 이 함수들은 정해진 순서에 따라 실행이 됩니다. 이를 통해 리액트 컴포넌트를 보다 더 심층적으로 다룰 수 있습니다. 생명주기는 크게 3단계로 나누어집니다.


  • Mounting
  • Updating
  • Unmounting



Mounting


먼저 영영사전에서 mount의 뜻을 찾아보았습니다. "시작하다", "증가하다", "올라가다" 등 의미가 있었습니다. 우리가 흔히 알고있는 Mountain(산)도 이 단어에서 나온거라 유추해 볼 수 있습니다. mount가 '올라가다(올라타다)' 라는 뜻이 있다는 걸 알고나니, 옆집 강아지 순돌이가 절 볼때마다 제 다리에 하는 짓이 생각났습니다. 네, 그 행위를 마운팅이라 하더군요. (어이 순돌이! 난 수컷이라고!) 리액트 컴포넌트와 관련된 마운팅의 의미는 위에 언급한 의미랑 관련성이 조금 결여된 듯 하여 다른 의미를 찾아봤습니다.



to attach something to something else so that it can be seen:


- Cambridge Dictionary



"무언가를 살펴보기 위해 다른 무언가에 끼우다[고정시키다]" 라고 하고 있습니다. 다시 말하자면, A를 보기 위해 B에 끼우다[고정시키다]는 단어가 mount라는 겁니다. 이를 리액트 컴포넌트에 적용해서 얘기하면, 컴포넌트에서 작성한 것들을 보기 위해 DOM에 끼운다라고 얘기할 수 있습니다. 즉, 컴포넌트에서 작성한 요소들을 DOM에 넣어서 확인한다고 생각하면 될 듯 합니다.


컴포넌트 생명주기 첫 단계인 Mounting에는 4가지 함수가 내장되어 있습니다. 이 4가지 함수는 밑에 있습니다.


  1. constructor()
  2. getDerivedStateFromProps()
  3. render()
  4. componentDidMount()


render() 함수는 컴포넌트 실행시 항상 작동되는 함수입니다. DOM에 뿌려주어야 하기 때문이죠. 그래서 render() 함수는 반드시 작성해야 합니다. 하지만 나머지 3개 함수들은 필요할 때만 작성해도 되며, 함수 내부를 작성자 마음대로 정의할 수 있습니다. 



constructor()



constructor() 함수는 컴포넌트가 실행될 때 가장 먼저 호출되는 함수입니다. 그리고 state를 정의하거나 state에 초기값을 넣고 싶을 때 이 함수 안에 작성하면 됩니다. 또한 이 함수는 파라미터값으로 props가 들어가며, 함수 내부에 super(props)를 먼저 작성해야 합니다. super(props)를 작성하는 이유는 Component를 상속받기 때문입니다.



App.js


import React from 'react';
import Header from './components/Header';

function App() {
return (
<div>
<Header />
</div>
);
}

export default App;



Header.jsx


import React, { Component } from 'react';

class Header extends Component{

constructor(props){
super(props);
this.state = {
name: '', //state만 정의
favoriteColor: 'red' //state 정의+초기값
}
console.log('constructor() 실행');
}
render(){
console.log('render() 실행');
return(
<div>
<h1>My Favorite Color is {this.state.favoriteColor}</h1>
</div>
);
}

}

export default Header;



실행화면



getDerivedStateFromProps()



getDerivedStateFromProps() 함수는 DOM에 컴포넌트에서 작성한 html 요소들을 렌더링하기 바로 직전에 호출되는 함수입니다. 이 함수 이름을 보면 "derived ~ from ~"이라는 관용어구가 나오는데, "~에서 ~을 얻다." 라는 뜻을 가지고 있습니다. 그러므로 이 함수는 props에서 state값을 가져오는 함수라고 할 수 있습니다. 또한 이 함수는 컴포넌트 내부에 있는 state와 props를 참조하기 위해 파라미터로 props와 state를 받습니다. state값을 변경하는 함수이기 때문에 함수 return값에다 state를 props값으로 해준다고 작성해주면 됩니다. 그러면 props를 통해 받은 값으로 state가 변경이 되어 출력됩니다. 이제 이 함수를 위에서 작성한 header.jsx에 추가하겠습니다.



header.jsx


import React, { Component } from 'react';

class Header extends Component{

constructor(props){
super(props);
this.state = {
name: '',
favoriteColor: 'red'
}
console.log('constructor() 실행');
}
static getDerivedStateFromProps(props, state){
console.log('getDrivedStateFromProps() 실행');
return{
favoriteColor: props.favCol
}
}

render(){
console.log('render() 실행');
return(
<div>
<h1>My Favorite Color is {this.state.favoriteColor}</h1>
</div>
);
}

}

export default Header;



실행화면



console창에 출력된 순서를 볼까요? constructor() → getDerivedStateFromProps() → render() 순으로 실행되는걸 확인했습니다. 그런데 문제는 "My Favorite Color is..." 다음에 내용이 없습니다. 어떻게 된 걸까요? 이 함수에 넣을 props를 favCol이라고 정의만 했지, props값을 주입하지는 않았습니다. 그렇기 때문에 state값이 props값으로 변경은 됐지만 들어온 props값이 없어서 빈 값으로 출력되었습니다. 이를 console.log()로 확인하면 "undefined"라고 나옵니다. 이제 props에 값을 주입하겠습니다.



App.js


import React from 'react';
import Header from './components/Header';

function App() {
return (
<div>
<Header favCol="Black" />
</div>
);
}

export default App;



실행화면



render



render() 함수는 반드시 작성해야 하는 함수이며, 이 함수 내부에서 작성한 HTML 태그들로 DOM을 형성합니다. (추가로, HTML태그로 보이지만 실제로는 JSX이며, JSX는 자바스크립트로 컴파일되어 HTML 태그로 변경되어 DOM을 형성합니다.) Header.jsx만 내용을 바꿔주어 출력해보겠습니다.



Header.jsx


import React, { Component } from 'react';

class Header extends Component{

render(){
return(
<div>
<h1>This is My Blog</h1>
<button>+</button><br/><br/>
이 블로그 메인으로 이동 -> <a href="https://corini.tistory.com">블로그메인</a>
</div>
);
}

}

export default Header;



화면



componentDidMount



componentDidMount() 함수는 컴포넌트가 렌더(render, 즉 DOM 형성)된 후에 실행되는 함수입니다. 즉, render() 함수가 실행되어 JSX 태그들이 DOM을 형성한 이후에 이 함수가 호출됩니다. 그래서 이 함수에는 DOM이 형성된 이후에 해야 할 작업을 작성합니다. 보통 Database에서 데이터를 불러올 때 사용합니다. 예시를 작성해보겠습니다.



Header.jsx


import React, { Component } from 'react';

class Header extends Component{

constructor(props){
super(props);
this.state = {
name: '',
favoriteColor: 'red'
}
console.log('constructor() 실행');
}
static getDerivedStateFromProps(props, state){
console.log('getDrivedStateFromProps() 실행');
return{
//favoriteColor: props.favCol
}
}

componentDidMount(){
console.log('componentDidMount() 실행');
setTimeout(() => {
this.setState({
favoriteColor: "White"
})
}, 2000);
}

render(){
console.log('render() 실행');
return(
<div>
<h1>My Favoriate Color is {this.state.favoriteColor}</h1>
</div>
);
}

}

export default Header;



실행화면1



실행화면2



콘솔창에 출력된 것 처럼, Mounting단계에서 생명주기와 관련된 함수들은 constructor() → getDrivedStateFromProps() → render() → componentDidMount() 순서로 실행되었습니다. componentDidMount() 함수에 setTimeout을 설정해 2초 뒤에 state값이 변경되도록 설정했습니다. 그리고 2초 뒤 state값이 red에서 white로 변경되었습니다.이렇듯 componentDidMount()는 DOM이 형성되고 나서해야 할 작업을 작성해주면 됩니다. 


실행화면2의 콘솔창을 볼까요. 2초 뒤 state값을 바꿔주었을 때 나온 콘솔창입니다. constuctor()부터 재실행되지 않고 특정 함수만 실행되었죠? 이게 바로 리액트의 장점입니다. 리액트는 DOM을 먼저 형성한 후, 이후 컴포넌트를 수정(updating)하게 되면 수정된 DOM을 다시 그리는 것이 아닌 이전의 DOM과 변경된 부분을 비교합니다. 비교한 후 변경된 부분만 다시 rendering해서 보여줍니다. 그래서 state값이 white로 변경되면서 변경된 값을 DOM에 나타내야 하기 때문에 render() 함수가 한 번 더 실행이 된 겁니다. 그렇다면 getDerivedStateFromProps() 함수는 왜 호출된걸까요? 이 함수는 Mounting 과정에서만 나오는 것이 아니라 Updating 과정에서도 실행되는 함수이기 때문입니다.


아래에 리액트 생명주기를 잘 표현한 그림을 가져왔습니다. 처음엔 이해가 잘 안될 수 있지만 코딩을 해 보면서 천천히 그려보세요.




댓글