[SeSACx코딩온] 컴포넌트 생명 주기 (Class vs Function 컴포넌트)
React에서의 Ref와 컴포넌트 생명 주기
1. 컴포넌트 생명 주기(Lifecycle)란?
컴포넌트 생명 주기(Lifecycle)는 React 컴포넌트가 생성되고, 업데이트되고, 제거되는 과정에서 발생하는 단계를 의미합니다. 각 단계마다 특정 메서드나 훅을 통해 작업을 수행할 수 있습니다.
-
마운트(Mount): 컴포넌트가 처음으로 DOM(Document Object Model)에 삽입될 때 발생합니다. 여기서 DOM은 브라우저가 웹 페이지를 이해하고 구조화하기 위해 사용하는 문서 구조입니다. 컴포넌트가 마운트된다는 것은, DOM 트리에 컴포넌트가 처음으로 추가되어 화면에 보이게 됩니다.
-
업데이트(Update): 컴포넌트가 리랜더링될 때 발생합니다. 리랜더링은 컴포넌트의 상태(state)나 부모로부터 전달받은 속성(props)이 변경될 때 일어나며, 이를 통해 화면에 보여지는 내용이 새롭게 갱신됩니다.
-
언마운트(Unmount): 컴포넌트가 DOM에서 제거될 때 발생합니다. 이는 컴포넌트가 더 이상 필요하지 않아 브라우저 화면에서 사라지는 시점을 의미합니다. 이때, 컴포넌트가 사용했던 자원을 해제하거나, 타이머나 네트워크 요청을 정리하는 작업이 필요할 수 있습니다.
이러한 단계에서 원하는 작업을 수행하려면
- 클래스형 컴포넌트에서는 생명 주기 메서드를
- 함수형 컴포넌트에서는
useEffect
훅을 사용합니다.
2. 단축 평가(Short-circuit Evaluation)와 조건부 랜더링
단축 평가는 논리 연산에서 좌항의 값에 따라 우항의 평가를 건너뛸 수 있는 특성을 의미합니다.
-
논리곱(
&&
): 좌항이false
이면, 전체 표현식이false
가 되기 때문에 우항은 평가되지 않습니다. 그러나 좌항이true
이면, 표현식의 결과는 우항의 값에 따라 달라지므로 우항이 평가되고 그 결과가 반환됩니다.true && "Hello"
의 경우, 좌항이true
이므로 우항"Hello"
가 평가되고, 이 값이 반환됩니다.-
false && "Hello"
의 경우, 좌항이false
이므로 우항은 평가되지 않고,false
가 반환됩니다. &&
연산자는 두 값이 모두true
일 때만true
를 반환합니다. 따라서 좌항이false
일 경우, 더 이상 결과가true
가 될 수 없으므로 우항을 평가할 필요가 없어서 건너뛰고, 좌항인false
를 그대로 반환합니다. 반면, 좌항이true
일 경우, 결과는 우항에 의해 결정되므로 우항을 평가하고, 그 결과가 반환됩니다.
-
논리합(
||
): 좌항이true
이면, 전체 표현식이true
가 되기 때문에 우항은 평가되지 않습니다. 좌항이false
이면, 표현식의 결과는 우항의 값에 따라 달라지므로 우항이 평가되고 그 결과가 반환됩니다.true || "Hello"
의 경우, 좌항이true
이므로 우항은 평가되지 않고,true
가 반환됩니다.-
false || "Hello"
의 경우, 좌항이false
이므로 우항"Hello"
가 평가되고, 이 값이 반환됩니다. ||
연산자는 두 값 중 하나만true
이면true
를 반환합니다. 따라서 좌항이true
일 경우, 전체 표현식이true
가 되므로 우항을 평가할 필요가 없고, 좌항인true
가 그대로 반환됩니다. 반면, 좌항이false
일 경우, 결과는 우항에 의해 결정되므로 우항을 평가하고, 그 결과가 반환됩니다.
3. 클래스형 컴포넌트에서의 생명 주기와 조건부 랜더링
1) LifeCycleClass 컴포넌트
import React, { Component } from 'react';
import LifeCycleClassChild from './LifeCycleClassChild';
export default class LifeCycleClass extends Component {
constructor(props) {
super(props);
this.state = {
number: 0,
visible: true, // 자식 컴포넌트의 표시 여부를 제어
};
}
// number 상태를 1 증가시키는 함수
changeNumberState = () => this.setState({ number: this.state.number + 1 });
// 자식 컴포넌트의 표시 여부를 토글하는 함수
changeVisibleState = () => this.setState({ visible: !this.state.visible });
render() {
return (
<>
<button onClick={this.changeNumberState}>Plus</button>
<button onClick={this.changeVisibleState}>On / Off</button>
{/* visible 상태에 따라 자식 컴포넌트를 랜더링 */}
{this.state.visible && (
<LifeCycleClassChild number={this.state.number} />
)}
</>
);
}
}
Plus
버튼:number
상태를 1씩 증가시킵니다.On / Off
버튼: 자식 컴포넌트의 표시 여부를 제어합니다. 자식 컴포넌트가 화면에 나타나거나 사라지도록 하는 것을 의미합니다.visible
이라는 상태를 통해 이루어지며,visible
이true
일 때는 자식 컴포넌트가 화면에 보이고,false
일 때는 자식 컴포넌트가 화면에서 사라지게 됩니다.
React에서의 조건부 랜더링(단축평가 사용)
클래스형 컴포넌트에서 &&
연산자를 사용한 조건부 랜더링을 사용합니다. {this.state.visible && <LifeCycleClassChild number={this.state.number} />}
의 경우, this.state.visible
이 true
일 때만 <LifeCycleClassChild />
컴포넌트가 랜더링됩니다.
this.state.visible
이 true
이면, &&
연산자의 특성에 따라 우항의 컴포넌트가 평가됩니다. 이때 평가된 컴포넌트는 랜더링 결과로 반환됩니다. 만약 this.state.visible
이 false
라면, &&
연산자의 좌항이 false
이기 때문에 우항의 컴포넌트는 평가되지 않고, 전체 표현식은 false
로 평가되어 아무것도 랜더링되지 않습니다.
2) LifeCycleClassChild 컴포넌트
import React, { Component } from 'react';
export default class LifeCycleClassChild extends Component {
// 컴포넌트가 처음 DOM에 삽입될 때 실행
componentDidMount() {
console.log('컴포넌트 마운트!! 🔵');
}
// 컴포넌트가 업데이트될 때 실행
componentDidUpdate() {
console.log('컴포넌트 업데이트!! 🟢');
}
// 컴포넌트가 DOM에서 제거될 때 실행
componentWillUnmount() {
console.log('컴포넌트 언마운트!! 🔴');
}
render() {
return (
<div>
자식 컴포넌트
<p>현재 Number 값
은 {this.props.number} 입니다.</p>
</div>
);
}
}
-
componentDidMount
: 컴포넌트가 처음으로 마운트(Mount)될 때, 즉 DOM에 삽입될 때 호출됩니다. 이 시점에서 초기 데이터 로드, 외부 API 호출 등의 작업을 할 수 있습니다. -
componentDidUpdate
: 컴포넌트가 업데이트(Update)될 때, 즉 상태나 props가 변경되어 리랜더링될 때 호출됩니다. 이 메서드는 이전 상태 또는 props와 비교하여 조건부로 작업을 수행할 때 유용합니다. -
componentWillUnmount
: 컴포넌트가 언마운트(Unmount)될 때, 즉 DOM에서 제거될 때 호출됩니다. 이 시점에서는 타이머 정리, 네트워크 요청 취소 등의 정리 작업을 수행할 수 있습니다.
4. 함수형 컴포넌트에서의 생명 주기와 조건부 랜더링
1) LifeCycleFunc 컴포넌트
import React, { useState } from 'react';
import LifeCycleFuncChild from './LifeCycleFuncChild';
export default function LifeCycleFunc() {
const [number, setNumber] = useState(0);
const [visible, setVisible] = useState(true); // 자식 컴포넌트의 표시 여부를 제어
// number 상태를 1 증가시키는 함수
const changeNumber = () => {
setNumber(number + 1);
};
// 자식 컴포넌트의 표시 여부를 토글하는 함수
const changeVisible = () => {
setVisible(!visible);
};
return (
<div style=>
<button onClick={changeNumber}>Plus</button>
<button onClick={changeVisible}>On / Off</button>
{/* visible 상태에 따라 자식 컴포넌트를 랜더링 */}
{visible && <LifeCycleFuncChild number={number} />}
</div>
);
}
Plus
버튼:number
상태를 1씩 증가시킵니다.On / Off
버튼:visible
상태가true
일 때만 자식 컴포넌트가 랜더링되고,false
일 때는 사라집니다.
React에서의 조건부 랜더링(단축평가 사용)
함수형 컴포넌트에서도 &&
연산자를 사용한 조건부 랜더링을 자주 사용합니다. {visible && <LifeCycleFuncChild number={number} />}
의 경우, visible
이 true
일 때만 <LifeCycleFuncChild />
컴포넌트가 랜더링됩니다.
visible
이 true
이면, &&
연산자의 특성에 따라 우항의 컴포넌트가 평가됩니다. 이때 평가된 컴포넌트는 랜더링 결과로 반환됩니다. 만약 visible
이 false
라면, &&
연산자의 좌항이 false
이기 때문에 우항의 컴포넌트는 평가되지 않고, 전체 표현식은 false
로 평가되어 아무것도 랜더링되지 않습니다.
2) LifeCycleFuncChild 컴포넌트
import React, { useEffect, useState } from 'react';
export default function LifeCycleFuncChild({ number }) {
const [input, setInput] = useState('');
// 컴포넌트가 처음 DOM에 삽입될 때 실행
useEffect(() => {
console.log('컴포넌트 마운트!! 🔵');
}, []);
// 컴포넌트가 마운트될 때와 언마운트될 때 실행
useEffect(() => {
console.log('컴포넌트 마운트!! 🔵');
return () => {
console.log('컴포넌트 언마운트!! 🔴');
};
}, []);
// 상태나 props가 변경될 때마다 실행
useEffect(() => {
console.log('컴포넌트 마운트!! 🔵 or 업데이트 🟢');
});
// 특정 상태(input)가 변경될 때 실행
useEffect(() => {
console.log(
'마운트 🔵 or input 상태가 변경됨에 따라 컴포넌트 업데이트 🟠'
);
}, [input]);
return (
<div style=>
자식 컴포넌트
<div>현재 number 값은 {number} 입니다.</div>
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
/>
</div>
);
}
(1) useState
useState
는 React에서 함수형 컴포넌트의 상태를 관리하는 훅입니다. 여기서는input
이라는 상태를 선언하고, 이 상태는 사용자가 입력 필드에 입력한 값을 저장합니다.useState
는 초기값으로 빈 문자열''
을 설정하고, 상태를 업데이트하는 함수인setInput
을 반환합니다. 이 함수는 사용자가 입력 필드에 값을 입력할 때 호출되어 상태를 업데이트합니다.
(2) useEffect
-
useEffect
는 함수형 컴포넌트에서 사이드 이펙트를 처리하는 훅입니다. 여기서는 네 가지useEffect
훅이 사용되고 있으며, 각각 컴포넌트의 생명 주기 동안 특정 시점에 실행됩니다.-
빈 배열을 의존성으로 가지는
useEffect
: 컴포넌트가 처음 마운트될 때 한 번 실행됩니다. 이때"컴포넌트 마운트!! 🔵"
라는 메시지를 콘솔에 출력합니다. -
언마운트 시 실행되는
useEffect
: 컴포넌트가 마운트될 때 실행되며, 반환하는 함수는 컴포넌트가 언마운트될 때 호출됩니다. 이 훅은"컴포넌트 마운트!! 🔵"
와"컴포넌트 언마운트!! 🔴"
메시지를 각각 출력합니다. -
의존성 배열이 없는
useEffect
: 의존성 배열이 없기 때문에 컴포넌트가 리랜더링될 때마다 실행됩니다."컴포넌트 마운트!! 🔵 or 업데이트 🟢"
라는 메시지를 콘솔에 출력합니다. -
특정 상태를 의존성으로 가지는
useEffect
:input
상태가 변경될 때마다 실행됩니다."마운트 🔵 or input 상태가 변경됨에 따라 컴포넌트 업데이트 🟠"
라는 메시지를 콘솔에 출력합니다.
-
3) 의존성 배열과 useEffect
useEffect
훅은 React 함수형 컴포넌트에서 생명 주기 메서드의 역할을 합니다. 이 훅은 컴포넌트가 랜더링될 때마다 특정 작업을 수행할 수 있게 해주며, 이 작업이 언제 실행될지를 제어하기 위해 의존성 배열(Dependency Array)을 사용합니다.
(1) 의존성 배열의 역할
-
빈 배열
[]
: 의존성 배열이 빈 배열이면useEffect
는 컴포넌트가 처음 마운트될 때 한 번만 실행됩니다. 마운트 이후에는 다시 실행되지 않습니다. 이 방법은 컴포넌트가 처음 생성될 때 한 번만 실행하고자 하는 초기화 작업에 유용합니다. -
의존성 배열 생략: 의존성 배열을 생략하면,
useEffect
는 컴포넌트가 리랜더링될 때마다 실행됩니다. 이는 상태나 props가 변경될 때마다 특정 작업을 수행해야 할 때 사용됩니다. -
특정 값이 포함된 배열: 특정 상태나 props를 의존성 배열에 포함시키면, 해당 값이 변경될 때마다
useEffect
가 실행됩니다. 예를 들어, 배열에[count]
가 있다면count
값이 변경될 때마다useEffect
가 실행됩니다.
(2) 빈 배열일 때 useEffect
가 한 번만 실행되는 이유 (특징)
빈 배열이 의존성 배열로 전달되면 useEffect
훅은 컴포넌트가 마운트될 때 한 번만 실행되고, 이후에는 다시 실행되지 않습니다. 이는 React가 의존성 배열을 통해 useEffect
의 실행 여부를 결정하기 때문입니다.
-
컴포넌트 마운트 시점: 컴포넌트가 처음 DOM에 삽입될 때, React는
useEffect
를 실행합니다. 이 시점에서 의존성 배열이 빈 배열인 경우, React는 의존할 값이 없다고 판단합니다. -
의존성 배열 검사: 의존성 배열이 비어 있으므로, React는 더 이상 추적할 상태나 props가 없다고 인식합니다. 따라서 이후의 업데이트나 리랜더링 시점에서는
useEffect
가 실행되지 않습니다. -
업데이트 시점: 컴포넌트가 리랜더링될 때, React는 의존성 배열에 변화가 없음을 확인하고
useEffect
를 다시 실행하지 않습니다.