React Lifecycle
- React component의 생명 주기
- Component가 생성될 때, 업데이트할 때, 제거될 때 어떻게 작동할지 정의할 수 있다.
- 자주 사용되는 메서드
- componentDidMount
- componentDidUpdate
- componentWillUnmount

Lifecycle 용어
- Mount (마운트): DOM이 생성되고 웹 브라우저 상에 나타남
- Update (업데이트): props 및 state가 바뀌었을 때 업데이트함
- Unmount (언마운트): componenet가 화면에서 사라질 때(제거될 때)
마운트(mount) 될 때 호출되는 메서드
- construction
- render
- getDerivedStateFromProps
- componentDidMount
업데이트(update) 될 때 호출되는 메서드
- getDerivedStateFromProps
- shouldComponentUpdate
- componentDidUpdate
언마운트(unmount) 될 때 호출되는 메서드
- componentWillUnmount
메서드 사용 예제
import { Component } from 'react';
// 자식 클래스 component 정의
class MyComponent extends Component {
// 본 component가 생길 때 호출되는 함수
componentDidMount() {
console.log('Mount! - component가 생김')
};
// 본 component가 다시 렌더링될 때 호출되는 함수
componentDidUpdate() {
console.log('Update! - component가 다시 렌더됨')
};
// 본 component가 제거될 때 호출되는 함수
componendWillUmount() {
console.log('Unmount! - component가 제거됨')
};
render() {
return(
<>
<div>My Component {this.props.number}</div>
</>
);
}
}
// 부모 클래스 component 정의
class LifeCycleClass extends Component {
state = {
number: 0,
visible: true
};
changeNumberState = () => {
this.setState({ number: this.state.number + 1 });
};
changeVisibleState = () => {
this.setState({ visible: !this.state.visible });
};
render() {
return(
<>
<button type='button' onClick={this.changeNumberState}>증가</button>
<button type='button' onClick={this.changeVisibleState}>ON/OFF</button>
{this.state.visible && <MyComponent number={this.state.number}></MyComponent>}
</>
);
}
}
export default LifeCycleClass;
부모 component에서 number와 visible이라는 state를 생성하고 증가 버튼이 클릭되면 기존 number값에 1일 증감하다. visible값이 true일 경우에만 자식 component를 렌더링 하게 되며 ON/OFF 버튼이 클릭되면 자식 component의 렌더링 상태를 바꿔줄 수 있다.
- 자식 component가 렌더링 될 경우, 바로 componentDidMount() 함수가 호출된다.
- 자식 component는 부모 component의 number state값을 props로 받기 때문에, 증가 버튼이 클릭되면 props값이 바뀌는 관계로 자식 component가 다시 렌더링 된다. 이때, componentDidUpdate() 함수가 호출된다.
- 부모 component에서 ON/OFF 버튼이 클릭되어 자식 component가 렌더링 되지 않는 상태로 바뀔 경우, componentWillUnmount() 함수가 호출된다.
해당 함수들은 클래스형 component에서만 사용이 가능하다.
함수형 component에서 Lifecycle은?
- 제공되는 여러 Hook들 중, useEffect() 함수로 함수형 component의 lifecycle을 관리할 수 있다.
useEffect()
- Side Effect (부수효과)를 수행한다.
- Side Effect: component가 렌더링 된 이후 비동기로 처리되어야 하는 부수적인 효과
- mount / unmount / update
- 클래스형 component의 componentDidMount 함수와 componentDidUpdate 함수가 합쳐진 형태다.
// component가 재렌더링 여부 상관 없이 렌더링이 될때마다 호출된다.
useEffect(() => {
console.log('렌더링이 완료되었습니다.');
})
// useEffect함수의 두번째 인자로 빈 배열 []을 추가할 경우,
// component가 최초로 렌더링 될때만 함수가 실행된다.
useEffect(() => {
console.log('처음 렌더링될 때만 실행됨');
}, []);
// 특정 값이 바뀔 때만 호출하고 싶을 경우, 두번째 인자에 component의 특정 state(들)을 넣어준다.
useEffect(() => {
console.log('state가 바뀌었습니다');
}, [상태])
// component가 unmount될 때 작업을 수행하고 싶다면,
// useEffect함수의 두번째 인자 (의존성 배열)을 빈 배열로 설정해주고,
// useEffect함수의 첫번째 인자 (콜백 함수) 안에 수행하고 싶은 코드를 함수형으로 return 해준다.
// 만약 component가 렌더링되기 전(재렌더 포함)에 어떤 작업을 수행하고 싶다면 useEffect함수의 두번째 인자 없이 return 해줄 수 있다.
useEffect(() => {
console.log('effect');
return () => {
console.log('component가 제거됨')
}
}, []);


React에서 axios
만약 특정 component가 처음 렌더될 경우(mount) 벡엔드에서 데이터를 받아와 state에 저장하고 해당 state를 요소에 보여주고 싶을 경우, useEffect() 안에서 axios를 사용할 수 있다.
다만, useEffect()의 호출 함수는 promise를 반환하지 않는 관계로 해당 호출 함수 안에서 비동기 코드를 작성해야 한다.
아래와 같이 코드를 작성하면 제대로 동작하지 않는다.
// 잘못된 코드 예제
import { useState, useEffect } from 'react';
import axios from 'axios';
export default function UserList() {
const [users, setUsers] = useState([]);
useEffect(async() => {
const result = await axios({
method: 'GET',
url: 'https://jsonplaceholder.typicode.com/users',
});
console.log(result)
}, [])
}
올바른 코드 예시
import { useState, useEffect } from 'react';
import axios from 'axios';
export default function UserList() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
//useEffect는 비동기함수를 직접적으로 지원하지 않음.
//비동기 함수를 사용하려면 내부에 비동기함수를 정의하고 바로 호출
const axiosUser = async () => {
try {
const result = await axios({
method: 'GET',
url: 'https://jsonplaceholder.typicode.com/users',
});
setUsers(result.data);
setLoading(true);
} catch (error) {
console.log(error);
}
};
axiosUser();
return () => {
console.log('연결 해제 완료');
};
}, []);
useEffect(() => {
console.log('유저 정보 업데이트', users.length);
}, [users]);
return (
<div>
{loading ? (
<ul>
{users.map((user) => {
return (
<li key={user.id}>
{user.name} - {user.email}
</li>
);
})}
</ul>
) : (
<div>Loading...</div>
)}
</div>
);
}
느낀 점들
useEffect()를 사용함으로 예전 프로젝트에서 가끔 사용할 수밖에 없었던 즉시 실행 함수를 사용할 필요가 없어진 것 같다. 추가적으로 useEffect()의 clean up (return) 기능으로 component가 unmount 될 때의 경우도 다룰 수 있다는 점도 매우 편리한 것 같다.