리액트의 반응형 효과 생명주기

안현희·2024년 11월 6일
6

React를 배워보자!

목록 보기
13/20

리액트의 반응형 효과(reactive effects) 생명주기

React컴포넌트의 라이프사이클은 컴포넌트가 생성되고 화면에 렌더링된 후, 사용자의 상호작용이나 상태 변경에 따라 업데이트되며, 마지막으로 제거되는 일련의 과정입니다. 이를 이해하면 컴포넌트가 언제, 어떻게 작동하는지 예측 가능하며, 필요한 시점에 적절한 로직을 추가할 수 있어 최적화와 유지 관리에 큰 도움이 됩니다.


1. 컴포넌트 라이프사이클의 단계

React 컴포넌트는 크게 다음 세 가지 단계로 나뉩니다.

1. 마운트(Mount)

  • 컴포넌트가 생성되어 초기 렌더링을 수행하는 단계입니다.
    이 시점에는 컴포넌트가 DOM에 삽입되며, 컴포넌트의 초기화 작업을 수행할 수 있습니다.
    보통 useEffect Hook을 사용해 컴포넌트가 처음 화면에 렌더링될 때만 실행되는 코드를 넣습니다. 예를 들어, API로 데이터를 가져오는 작업이 여기에 해당합니다.

2. 업데이트(Update)

  • 상태(state)속성(props)이 변경되면 컴포넌트가 다시 렌더링되는 단계입니다.
    React는 변경된 값에 반응하여 컴포넌트를 재랜더링하고, 사용자가 기대하는 대로 최신 화면을 보여줍니다.
    이때도 useEffect를 통해 상태나 속성에 따라 특정 로직을 실행할 수 있습니다. 이 기능은 반응형 웹 애플리케이션을 만들 때 매우 유용합니다.

3. 언마운트(Unmount)

  • 컴포넌트가 DOM에서 제거되는 단계입니다.
  • 이 과정에서 메모리 누수를 방지하기 위해 정리 작업(clean-up)이 필요할 수 있습니다.
  • useEffect에서는 클린업 함수를 통해 타이머나 이벤트 리스너 등을 정리해 효율적인 리소스 관리를 할 수 있습니다.

2. 리액티브 효과의 활용

React에서 useEffect와 같은 훅(Hook)을 통해 라이프사이클을 다루는 것은 반응형 웹 개발에서 핵심적인 역할을 합니다. 기존 클래스형 컴포넌트에서는 componentDidMount, componentDidUpdate, componentWillUnmount와 같은 메서드를 활용해 각 단계별로 처리했으나, 함수형 컴포넌트useEffect의 등장으로 단일 API로 관리가 가능해졌습니다.

이 구조를 이해하는 것은 더 나은 코드 품질과 애플리케이션 성능 최적화로 이어집니다. 특히, 필요할 때만 효과가 실행되고, 불필요한 렌더링을 방지할 수 있기 때문에 효율적인 웹 애플리케이션을 구축할 수 있습니다.


3. useEffect가 필요한 이유

React는 기본적으로 컴포넌트 렌더링과 UI업데이트를 위한 라이브러리이지만, 실제 애플리케이션에서는 단순 렌더링 외에도 다양한 작업들이 필요합니다.

  • 데이터 패칭(Data Fetching): 서버에서 데이터를 불러와야 하는 경우가 많습니다. 이 과정에서 데이터를 요청하고, 받아온 데이터를 상태에 저장해 화면에 반영해야 합니다.

  • 구독 및 이벤트 리스너 등록(Subscriptions & Event Listeners): 웹소켓 연결, 타이머 설정, 스크롤 이벤트 등의 작업은 컴포넌트가 마운트될 때 시작되고, 언마운트될 때 정리되어야 합니다.

  • DOM 조작 및 애니메이션: React가 DOM을 관리하지만, 특정 작업에서는 직접 DOM 조작이나 애니메이션 설정이 필요할 수 있습니다.


3-1. useEffect의 역할과 주요 기능

  • 마운트 시 효과: 컴포넌트가 처음 렌더링될 때만 실행되는 효과를 추가할 수 있습니다. 예를 들어, 페이지에 들어가자마자 API에서 데이터를 가져오는 로직을 포함할 수 있습니다.

  • 업데이트 시 효과: 특정 상태나 속성이 변경될 때마다 자동으로 재실행되도록 할 수 있습니다. 이를 통해 특정 상태 변화에 반응하여 부수 효과를 관리할 수 있습니다.

  • 클린업(Cleanup) 기능: 컴포넌트가 언마운트되기 직전에 호출되는 클린업 함수를 포함할 수 있습니다. 이를 통해 메모리 누수나 불필요한 리소스 점유를 방지할 수 있습니다.


3-2. useEffect가 성능에 미치는 영향

useEffect는 적절한 의존성 관리로 불필요한 렌더링을 줄여 성능을 최적화할 수 있습니다. 특정 상태나 속성의 변화에 따라 원하는 시점에만 효과를 실행할 수 있어, 기존 클래스형 컴포넌트에서 자주 발생하던 무한 루프나 불필요한 연산을 방지하는 데에도 큰 도움이 됩니다.


3-3. useEffect의 기본 사용법과 원리

기본 사용법

useEffect(() => {
  // 실행할 부수 효과 코드
  return () => {
    // 클린업(clean-up) 코드 (선택적)
  };
}, [의존성1, 의존성2, ...]);
  • 첫 번째 인자는 실행할 함수입니다. 이 함수는 컴포넌트가 처음 렌더링될 때 실행되며, 특정 조건에 따라 컴포넌트가 다시 렌더링될 때 재실행됩니다.

  • 두 번째 인자는 의존성 배열입니다. 이 배열에 있는 값이 변경될 때만 useEffect가 다시 실행됩니다. 이 배열을 통해 useEffect가 언제 실행될지를 제어할 수 있습니다.


의존성 배열의 역할과 작동 원리

의존성 배열useEffect의 재실행을 제어하는 중요한 요소입니다. 의존성 배열에 따라 useEffect의 실행 빈도와 시점이 달라지며, 크게 세 가지 경우로 나눌 수 있습니다.

1. 빈 배열 []: 초기 마운트 시 한 번만 실행

useEffect(() => {
  console.log("이 코드는 마운트될 때 한 번만 실행됩니다.");
}, []);
  • 배열을 빈 배열 []로 두면, 이 useEffect는 컴포넌트가 마운트될 때 한 번만 실행됩니다.

2. 특정 상태나 속성에 의존하는 배열 [state1, prop1]

useEffect(() => {
  console.log("이 코드는 state1이나 prop1이 변경될 때마다 실행됩니다.");
}, [state1, prop1]);
  • 배열에 특정 상태나 속성을 넣으면, 해당 값이 변경될 때만 useEffect가 다시 실행됩니다.

3. 의존성 배열을 생략한 경우 (매번 렌더링 시 실행)

useEffect(() => {
  console.log("이 코드는 매 렌더링마다 실행됩니다.");
});
  • 의존성 배열을 생략하면 컴포넌트가 렌더링될 때마다 useEffect가 실행됩니다.

3-4. useEffect의 클린업 함수

useEffect(() => {
  const intervalId = setInterval(() => {
    console.log("매 초마다 실행되는 타이머");
  }, 1000);

  return () => {
    clearInterval(intervalId); // 타이머 정리
    console.log("타이머가 정리되었습니다.");
  };
}, []);
  • useEffect의 첫 번째 인자로 전달하는 함수 내부에서 클린업 함수를 반환하면, 컴포넌트가 언마운트되거나, 의존성 배열의 값이 변경되어 useEffect가 재실행되기 전에 클린업 함수가 실행됩니다. 클린업 함수는 이벤트 리스너 제거, 타이머 정리, 구독 해제 등을 처리하는 데 유용합니다.

실전예제

useEffect로 API 데이터 페칭 및 실시간 업데이트 관리하기

1) 초기 데이터 로드

useEffect를 사용해 컴포넌트가 마운트될 때 API로 데이터를 한 번만 가져오는 패턴입니다.

import { useEffect, useState } from 'react';

function DataFetchingComponent() {
  const [data, setData] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch('https://api.example.com/data');
        const jsonData = await response.json();
        setData(jsonData);
      } catch (error) {
        console.error("Error fetching data:", error);
      }
    };
    fetchData();
  }, []); // 빈 배열로 마운트 시 한 번만 실행

  return <div>{data ? `Data: ${data}` : "Loading..."}</div>;
}
  • 이 코드는 데이터가 처음 로드될 때만 fetchData를 실행하여 불필요한 API 호출을 방지합니다.
    에러 핸들링도 포함해 실무에서 유용한 패턴입니다.

2) 실시간 업데이트 및 구독 관리

다음 예제는 useEffect를 통해 실시간 데이터를 구독하는 패턴입니다. 예를 들어, 웹소켓을 이용해 서버에서 실시간으로 데이터를 받아오는 상황을 가정할 수 있습니다.

import { useEffect, useState } from 'react';

function RealTimeDataComponent() {
  const [messages, setMessages] = useState([]);

  useEffect(() => {
    const ws = new WebSocket('wss://example.com/realtime');

    ws.onmessage = (event) => {
      setMessages((prevMessages) => [...prevMessages, event.data]);
    };

    // 클린업 함수로 웹소켓 연결 해제
    return () => {
      ws.close();
      console.log("WebSocket connection closed");
    };
  }, []); // 빈 배열로 마운트 시 한 번만 실행

  return (
    <div>
      <h3>Real-Time Messages</h3>
      <ul>
        {messages.map((msg, index) => (
          <li key={index}>{msg}</li>
        ))}
      </ul>
    </div>
  );
}
  • 이 코드는 웹소켓을 통해 실시간 메시지를 받아와 화면에 표시합니다. 컴포넌트가 언마운트될 때 ws.close()를 호출해 웹소켓 연결을 해제하므로, 리소스를 효율적으로 관리할 수 있습니다.

3) 사용자 입력에 따라 실시간으로 데이터 필터링

마지막으로, 상태 값이 변경될 때마다 useEffect를 사용해 데이터를 필터링하는 예제입니다.

import { useEffect, useState } from 'react';

function FilteredListComponent({ items }) {
  const [query, setQuery] = useState('');
  const [filteredItems, setFilteredItems] = useState([]);

  useEffect(() => {
    const filtered = items.filter((item) => item.includes(query));
    setFilteredItems(filtered);
  }, [query, items]); // query나 items가 변경될 때마다 실행

  return (
    <div>
      <input
        type="text"
        value={query}
        onChange={(e) => setQuery(e.target.value)}
        placeholder="Search..."
      />
      <ul>
        {filteredItems.map((item, index) => (
          <li key={index}>{item}</li>
        ))}
      </ul>
    </div>
  );
}
  • 여기서 query가 변경될 때마다 useEffect가 실행되어 filteredItems가 업데이트됩니다. itemsquery의존성 배열에 추가하여, 입력 값이 변경될 때만 데이터가 필터링되도록 설정했습니다.

회고

  • 챌린지반 과제로 반응형 이펙트의 생명주기에 대해 공부해봤다.

  • 어떻게든지 개발을 해나가는것과 이런 개념들에 대해 확립하고 해나가는것은 정말 하늘과 땅 차이인것 같다.

  • 원리에 대해 알게되니까 어떻게 써야할지 느낌이 온다고 해야하나? 매우 좋은시간이다.

  • 리액트 숙련주차 과제로 포켓몬 도감 만들기가 주어졌다.
    이번에도 잘해봅시다 화이팅!!!

그럼이만

0개의 댓글