티스토리 뷰

 

리액트 개발자라면 반드시 이해해야 할 핵심 개념 중 하나가 바로 **생명주기(Lifecycle)**입니다. 컴포넌트가 브라우저에 나타나고, 업데이트되고, 사라지는 일련의 과정을 이해하면 보다 효율적인 앱을 설계할 수 있습니다.

과거에는 클래스형 컴포넌트의 메서드를 통해 이를 관리했지만, 현재는 React Hooks를 이용한 함수형 컴포넌트가 표준이 되었습니다. 이 글에서는 두 방식을 비교하며 생명주기를 완벽하게 정리해 보겠습니다.

1. 생명주기의 3단계 요약

리액트 컴포넌트의 생명주기는 크게 세 단계로 나뉩니다.

  1. Mounting (마운트): 컴포넌트가 DOM에 삽입될 때 (탄생)
  2. Updating (업데이트): Props나 State가 변경되어 재렌더링될 때 (성장)
  3. Unmounting (언마운트): 컴포넌트가 DOM에서 제거될 때 (소멸)

2. 클래스형 vs 함수형 비교 차트

단계클래스형 메서드함수형 Hook (useEffect)

마운트 componentDidMount useEffect(() => { ... }, [])
업데이트 componentDidUpdate useEffect(() => { ... }, [의존성])
언마운트 componentWillUnmount useEffect(() => { return () => { ... } })

3. 단계별 상세 설명 및 실무 예제

① Mounting: 컴포넌트가 화면에 나타날 때

이 단계는 주로 외부 API 호출, 라이브러리 연동(D3, Google Maps 등), 혹은 setTimeout 같은 타이머를 설정할 때 사용됩니다.

[함수형 예제: API 데이터 불러오기]

import React, { useState, useEffect } from 'react';

function UserProfile() {
  const [user, setUser] = useState(null);

  useEffect(() => {
    // 마운트 시점에 딱 한 번 실행됨
    console.log("컴포넌트가 마운트되었습니다.");
    
    fetch('[https://api.example.com/user/1](https://api.example.com/user/1)')
      .then(res => res.json())
      .then(data => setUser(data));

  }, []); // 빈 배열([])은 마운트 시에만 실행하라는 의미입니다.

  return (
{user ?

안녕하세요, {user.name}님!

:

로딩 중...

}
  );
}

② Updating: 데이터가 변경되어 다시 그려질 때

특정 상태(state)나 속성(props)이 변경될 때 동작을 수행하고 싶다면 이 단계를 활용합니다.

[함수형 예제: 검색어 변경에 따른 재조회]

useEffect(() => {
  if (searchTerm) {
    console.log(`검색어가 "${searchTerm}"으로 변경되어 결과를 업데이트합니다.`);
    // 검색 로직 실행
  }
}, [searchTerm]); // searchTerm이 바뀔 때마다 실행됩니다.

③ Unmounting: 컴포넌트가 사라질 때

메모리 누수를 방지하기 위해 사용했던 이벤트 리스너를 제거하거나 타이머를 멈추는 '정리(Cleanup)' 작업이 필요합니다.

[함수형 예제: 타이머 해제]

import React, { useState, useEffect } from 'react';

function Timer() {
  const [seconds, setSeconds] = useState(0);

  useEffect(() => {
    const intervalId = setInterval(() => {
      setSeconds(prev => prev + 1);
    }, 1000);

    // Cleanup 함수: 언마운트 직전에 실행됨
    return () => {
      console.log("타이머를 종료하고 메모리를 정리합니다.");
      clearInterval(intervalId);
    };
  }, []);

  return <div>경과 시간: {seconds}초</div>;
}

4. 복합적인 흐름 이해하기

실무에서는 하나의 useEffect 안에서 마운트와 언마운트 로직을 동시에 처리하는 경우가 많습니다.

[종합 예제: 윈도우 리사이즈 이벤트]

function WindowWidth() {
  const [width, setWidth] = useState(window.innerWidth);

  useEffect(() => {
    const handleResize = () => setWidth(window.innerWidth);

    // 1. 마운트 시: 이벤트 등록
    window.addEventListener('resize', handleResize);
    console.log("리스너 등록");

    // 2. 언마운트 시: 이벤트 제거
    return () => {
      window.removeEventListener('resize', handleResize);
      console.log("리스너 해제");
    };
  }, []); // 의존성 배열이 비어있으므로 생성/소멸 시에만 작동

  return <h2>현재 창 너비: {width}px</h2>;
}

5. 생명주기 관리 시 주의사항

  1. 의존성 배열 관리: useEffect의 두 번째 인자인 배열을 잘못 관리하면 무한 루프에 빠지거나 업데이트가 반영되지 않을 수 있습니다. 내부에 사용된 모든 상태값과 함수를 체크하세요.
  2. 불필요한 렌더링 방지: 매 렌더링마다 생명주기 로직이 실행되지 않도록 useMemo나 useCallback과 함께 사용하여 최적화하는 것이 좋습니다.
  3. Cleanup의 중요성: 외부 라이브러리를 연결했다면 반드시 언마운트 시점에 연결을 끊어주어야 성능 저하를 막을 수 있습니다.

리액트의 생명주기는 컴포넌트의 라이프 사이클을 개발자가 제어할 수 있게 해주는 강력한 도구입니다. 클래스형의 복잡한 메서드 이름보다는 useEffect 하나로 통합된 현대적인 리액트 방식을 익히는 것이 생산성 향상에 큰 도움이 됩니다.

반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/04   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30
글 보관함