react

컴포넌트 생명주기

React의 컴포넌트 생명주기(Lifecycle)는 컴포넌트가 생성(mount), 업데이트(update), 제거(unmount)될 때 React가 호출하는 일련의 메서드입니다.

컴포넌트 생명주기를 이해하면 컴포넌트가 언제 렌더링되는지, 언제 데이터를 불러올지, 언제 자원을 정리할지 등을 명확하게 이해하고 관리할 수 있습니다.

✅ 생명주기(Lifecycle)란?

컴포넌트가 DOM에 나타나고(mount), 업데이트되고(update), 사라질 때(unmount), React는 특정 메서드를 호출합니다.

크게 세 가지 단계로 나뉩니다:

  • Mounting (생성)
  • Updating (업데이트)
  • Unmounting (제거)

📌 클래스 컴포넌트에서의 생명주기 메서드

함수 컴포넌트는 Hooks(useEffect)로 관리하며, 클래스 컴포넌트에서만 lifecycle 메서드를 직접 사용합니다.

React 컴포넌트의 대표적인 생명주기 메서드는 다음과 같습니다:

  • Mounting(생성 및 추가) : constructor(), render(), componentDidMount()
  • Updating(업데이트) : shouldComponentUpdate(), componentDidUpdate()
  • Unmounting(제거) : componentWillUnmount()

🔷 각 생명주기 단계의 동작 원리

📌 1. Mounting (생성 단계)

컴포넌트가 처음 DOM에 추가될 때 실행되는 단계입니다.

순서는 다음과 같습니다:

  1. constructor
  2. getDerivedStateFromProps
  3. render
  4. componentDidMount
  • 보통 초기 상태 설정, API 호출, 외부 데이터 가져오기를 합니다.
1class MyComponent extends React.Component {
2 constructor(props) {
3 super(props);
4 this.state = { count: 0 }; // 상태 초기화
5 }
6
7 static getDerivedStateFromProps(props, state) {
8 // 상태 초기화 또는 변경 시 props에 따라 업데이트
9 return null; // 변경 없으면 null 반환
10 }
11
12 componentDidMount() {
13 // DOM 생성 후 호출되는 메서드 (API 요청 등 초기화 작업)
14 console.log('Component mounted!');
15 }
16
17 render() {
18 return <h1>{this.state.count}</h1>;
19 }
20}

📌 2. Updating (업데이트 단계)

컴포넌트의 상태(state)나 props가 변경될 때 실행됩니다.

순서는 다음과 같습니다:

  1. getDerivedStateFromProps
  2. shouldComponentUpdate
  3. render
  4. getSnapshotBeforeUpdate
  5. componentDidUpdate
1shouldComponentUpdate(nextProps, nextState) {
2 // 업데이트 여부 결정
3 return nextState.count !== this.state.count;
4}
5
6componentDidUpdate(prevProps, prevState, snapshot) {
7 // DOM 업데이트 이후 호출됨
8 console.log('Component updated!');
9}
  • shouldComponentUpdate에서 false를 리턴하면 업데이트를 막을 수 있습니다.
  • componentDidUpdate는 DOM 업데이트 후 호출되어, 업데이트 후 처리(예: 데이터 재요청)에 사용됩니다.

📌 3. Unmounting (제거 단계)

컴포넌트가 DOM에서 제거될 때 호출되는 메서드입니다.

이 단계는 메서드가 하나뿐입니다:

  • componentWillUnmount
1componentWillUnmount() {
2 // 이벤트 리스너 정리, 타이머 제거 등 리소스 정리
3 console.log('Component unmounted!');
4}

📌 생명주기 메서드 사용 예시 코드

다음은 각 단계를 명확하게 이해할 수 있도록 작성한 클래스형 컴포넌트 예시입니다.

1import React from 'react';
2
3class LifecycleExample extends React.Component {
4 constructor(props) {
5 super(props);
6 this.state = { count: 0 };
7 console.log('constructor');
8 }
9
10 static getDerivedStateFromProps(props, state) {
11 console.log('getDerivedStateFromProps');
12 return null;
13 }
14
15 componentDidMount() {
16 console.log('componentDidMount');
17 }
18
19 shouldComponentUpdate(nextProps, nextState) {
20 console.log('shouldComponentUpdate');
21 return true; // 항상 업데이트
22 }
23
24 getSnapshotBeforeUpdate(prevProps, prevState) {
25 console.log('getSnapshotBeforeUpdate');
26 return null; // 스냅샷 값 반환 가능
27 }
28
29 componentDidUpdate(prevProps, prevState, snapshot) {
30 console.log('componentDidUpdate');
31 }
32
33 componentWillUnmount() {
34 console.log('componentWillUnmount');
35 }
36
37 increment = () => {
38 this.setState({ count: this.state.count + 1 });
39 };
40
41 render() {
42 console.log('render');
43 return (
44 <div>
45 <p>Count: {this.state.count}</p>
46 <button onClick={() => this.setState({ count: this.state.count + 1 })}>
47 증가
48 </button>
49 </div>
50 );
51 }
52}
53
54export default LifecycleExample;

로그 순서 예시:

constructor getDerivedStateFromProps render componentDidMount (상태 변경 시) getDerivedStateFromProps shouldComponentUpdate render getSnapshotBeforeUpdate componentDidUpdate

📌 Hooks로 구현 시 (함수형 컴포넌트)

최근 React는 함수형 컴포넌트와 훅(useEffect)을 사용합니다.

클래스 컴포넌트의 생명주기는 함수 컴포넌트에서 다음과 같이 표현됩니다:

1import React, { useState, useEffect } from 'react';
2
3function LifecycleExample() {
4 const [count, setCount] = useState(0);
5
6 // Mount (componentDidMount)
7 useEffect(() => {
8 console.log('Component Mounted');
9
10 // Unmount 단계에서 실행될 정리함수 반환
11 return () => {
12 console.log('Component will unmount');
13 };
14 }, []);
15
16 // count가 변경될 때마다 실행 (componentDidUpdate와 유사)
17 useEffect(() => {
18 console.log('Component updated:', count);
19 }, [count]);
20
21 return (
22 <div>
23 <h1>Count: {count}</h1>
24 <button onClick={() => setCount(count + 1)}>증가</button>
25 </div>
26 );
27}

📌 정리

React 컴포넌트는 Mount, Update, Unmount 단계에 따라 특정 메서드를 호출하여 동작합니다.

  • Mount 단계에서는 초기화 작업과 데이터 로드를 수행합니다.
  • Update 단계에서는 UI가 최신 상태로 유지되도록 업데이트 여부를 결정합니다.
  • Unmount 단계에서는 리소스를 정리하고 메모리 누수를 방지합니다.

생명주기 메서드 및 훅(useEffect)을 적절히 활용하면 React 컴포넌트가 효율적으로 관리됩니다.


SNS 서비스를 예로 들어 React의 컴포넌트 생명주기(Lifecycle) 원리를 쉽게 설명해 보겠습니다.

아래는 SNS에서 흔히 볼 수 있는 피드(Feed) 컴포넌트를 예로 들어 생명주기 메서드를 설명한 것입니다.


✅ SNS 앱의 피드 컴포넌트 생명주기 예시

SNS 앱을 생각하면, 다음과 같은 과정이 일어납니다:

  1. 처음 피드를 로딩할 때 (Mount)
  2. 사용자가 새 게시물을 작성하거나 스크롤하여 데이터를 더 불러올 때 (Update)
  3. 다른 화면으로 이동하거나 앱을 종료할 때 (Unmount)

🔷 1단계: Mount (피드 컴포넌트 생성)

피드 컴포넌트가 처음 화면에 나타나는 시점입니다.

이때 필요한 작업:

  • 서버에서 최신 게시물 목록 가져오기 (API 호출)
  • 초기 화면 설정 (데이터가 오기 전 로딩 화면)

📌 Mount 단계에서의 주요 메서드

  • constructor
  • 상태(state) 초기 설정 (게시물 목록, 로딩 상태 등)
  • render
  • 컴포넌트의 초기 UI 렌더링 (로딩 화면 등)
  • componentDidMount
  • 서버에서 실제 게시물 데이터를 받아옴 (API 요청)

▶️ 예제 코드

1class FeedComponent extends React.Component {
2 constructor(props) {
3 super(props);
4 this.state = {
5 posts: [], // 게시물 목록 초기화
6 loading: true, // 로딩 상태 관리
7 };
8 }
9
10 componentDidMount() {
11 // 컴포넌트가 화면에 등장한 후 데이터 요청
12 fetch('/api/posts')
13 .then(res => res.json())
14 .then(data => this.setState({ posts: data, loading: false }));
15 }
16
17 render() {
18 if (this.state.loading) {
19 return <div>Loading...</div>;
20 }
21 return (
22 <div>
23 {this.state.posts.map(post => (
24 <Post key={post.id} data={post} />
25 ))}
26 </div>
27 );
28 }
29}

🔷 2단계: Update (피드 컴포넌트 업데이트)

사용자가 피드에서 새로운 게시물을 작성하거나, 스크롤하여 다음 게시물 목록을 불러올 때 이 단계가 발생합니다.

이때 필요한 작업:

  • 업데이트된 상태(state)에 따라 다시 렌더링
  • 필요 시 서버에서 추가 데이터를 요청

📌 Update 단계에서의 주요 메서드

  • shouldComponentUpdate
  • 불필요한 렌더링을 막기 위해, 실제 변경이 있을 때만 업데이트 결정
  • render
  • 업데이트된 상태를 화면에 다시 그립니다.
  • componentDidUpdate
  • 데이터 변경이 발생하면 추가 작업(API 재요청 등)을 수행합니다.

▶️ 예제 코드 (새로운 게시물 추가)

1componentDidUpdate(prevProps, prevState) {
2 if (prevState.posts.length !== this.state.posts.length) {
3 console.log('새로운 게시물이 추가되었습니다.');
4 }
5}
6
7handleNewPost = (newPost) => {
8 this.setState(prevState => ({
9 posts: [newPost, ...prevState.posts],
10 }));
11};

이 과정에서 사용자가 새 게시물을 올리면 상태(state.posts)가 변경되고,

컴포넌트는 자동으로 **render() → componentDidUpdate()**를 실행하여 UI를 갱신합니다.


🔷 3단계: Unmount (피드 컴포넌트 제거)

사용자가 다른 화면(예: 프로필, 설정)으로 이동하거나 앱을 종료할 때, 피드 컴포넌트가 화면에서 제거되는 단계입니다.

이때 필요한 작업:

  • 타이머, 이벤트 리스너 제거
  • 불필요한 요청 취소 (메모리 누수 방지)

📌 Unmount 단계에서의 주요 메서드

  • componentWillUnmount
  • 이벤트 리스너 및 리소스 정리

▶️ 예제 코드 (이벤트 리스너 제거)

1componentDidMount() {
2 window.addEventListener('scroll', this.handleScroll);
3}
4
5componentWillUnmount() {
6 window.removeEventListener('scroll', this.handleScroll);
7 console.log('피드 컴포넌트가 화면에서 제거됨.');
8}
9
10handleScroll = () => {
11 // 무한스크롤 처리 로직
12};
  • 피드의 무한 스크롤 기능을 구현할 때, 컴포넌트가 제거되면 반드시 이벤트 리스너를 정리하여 메모리 누수를 막아야 합니다.

📌 함수형 컴포넌트에서의 구현 (useEffect 활용)

최근에는 함수형 컴포넌트를 사용하는 경우가 많습니다.

함수형 컴포넌트의 생명주기 관리에는 useEffect 훅을 사용합니다.

▶️ 함수형 예제 (Mount, Update, Unmount 모두 처리)

1import React, { useState, useEffect } from 'react';
2
3function FeedComponent() {
4 const [posts, setPosts] = useState([]);
5 const [loading, setLoading] = useState(true);
6
7 useEffect(() => {
8 // Mount (생성 단계)
9 fetch('/api/posts')
10 .then(res => res.json())
11 .then(data => {
12 setPosts(data);
13 setLoading(false);
14 });
15
16 // Unmount (제거 단계)
17 return () => {
18 console.log('Feed 컴포넌트 제거됨');
19 };
20 }, []); // 빈 배열([])은 마운트와 언마운트 때만 실행됨
21
22 useEffect(() => {
23 // Update (업데이트 단계)
24 console.log('게시물 목록이 업데이트됨:', posts.length);
25 }, [posts]); // posts 상태가 변경될 때만 실행됨
26
27 if (loading) {
28 return <div>Loading...</div>;
29 }
30
31 return (
32 <div>
33 {posts.map(post => (
34 <Post key={post.id} data={post} />
35 ))}
36 </div>
37 );
38}
  • Mount 시 데이터 로딩
  • 게시물 목록(posts)이 변경될 때마다 업데이트 로직 실행
  • 컴포넌트 제거 시(Unmount) 리소스 정리