개발

React useEffect와 빈 의존성 배열: 마운트/언마운트 생명주기 이해하기

효ㄷi 2025. 2. 19. 21:48

React에서 useEffect는 가장 많이 사용되는 Hook 중 하나다. 특히 의존성 배열을 빈 배열(`[]`)로 설정하는 경우가 자주 있는데, 이것이 정확히 어떤 의미를 가지는지, 언제 사용해야 하는지 자세히 알아보자.


useEffect와 빈 의존성 배열의 의미

 

`useEffect`에 빈 의존성 배열을 전달하면, 해당 효과는 컴포넌트가 처음 마운트될 때 단 한 번만 실행된다. 또한 cleanup 함수가 있다면, 이는 컴포넌트가 언마운트될 때 한 번 실행된다.

import { useEffect } from 'react';

function ExampleComponent() {
  useEffect(() => {
    console.log('컴포넌트가 마운트되었다!');

    // cleanup 함수
    return () => {
      console.log('컴포넌트가 언마운트되었다!');
    };
  }, []); // 빈 의존성 배열

  return <div>예제 컴포넌트</div>;
}
 

 

빈 의존성 배열을 사용하는 대표적인 경우들

 

1. 초기 데이터 로딩

 

API에서 초기 데이터를 가져올 때 가장 흔히 사용된다.

function UserProfile() {
  const [userData, setUserData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchUserData = async () => {
      try {
        const response = await fetch('https://api.example.com/user');
        const data = await response.json();
        setUserData(data);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    };

    fetchUserData();
  }, []); // 컴포넌트 마운트 시 한 번만 실행

  if (loading) return <div>로딩 중...</div>;
  if (error) return <div>에러 발생: {error}</div>;
  
  return (
    <div>
      <h1>{userData?.name}의 프로필</h1>
      <p>이메일: {userData?.email}</p>
    </div>
  );
}
 

 

2. 이벤트 리스너 등록 및 제거

 

window나 document 레벨의 이벤트 리스너를 관리할 때 유용하다.

function ScrollTracker() {
  const [scrollY, setScrollY] = useState(0);

  useEffect(() => {
    const handleScroll = () => {
      setScrollY(window.scrollY);
    };

    // 이벤트 리스너 등록
    window.addEventListener('scroll', handleScroll);

    // cleanup: 이벤트 리스너 제거
    return () => {
      window.removeEventListener('scroll', handleScroll);
    };
  }, []);

  return (
    <div>
      <p>현재 스크롤 위치: {scrollY}px</p>
    </div>
  );
}
 

 

3. WebSocket 연결 관리

 

실시간 통신을 위한 WebSocket 연결을 설정하고 정리할 때 사용된다.

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

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

    socket.onmessage = (event) => {
      const message = JSON.parse(event.data);
      setMessages(prev => [...prev, message]);
    };

    socket.onopen = () => {
      console.log('WebSocket 연결 성공!');
    };

    // cleanup: WebSocket 연결 종료
    return () => {
      socket.close();
    };
  }, []);

  return (
    <div>
      {messages.map((msg, index) => (
        <div key={index}>{msg.text}</div>
      ))}
    </div>
  );
}
 

 

4. 타이머 설정 및 정리

 

주기적으로 실행되어야 하는 작업을 관리할 때 사용한다.

function AutoRefresh() {
  const [lastUpdate, setLastUpdate] = useState(new Date());

  useEffect(() => {
    const interval = setInterval(() => {
      setLastUpdate(new Date());
    }, 1000);

    // cleanup: 타이머 정리
    return () => {
      clearInterval(interval);
    };
  }, []);

  return (
    <div>
      <p>마지막 업데이트: {lastUpdate.toLocaleTimeString()}</p>
    </div>
  );
}
 

 

주의사항과 고려할 점

 

1. 불필요한 빈 배열 사용 피하기

// ❌ 잘못된 사용
function UserGreeting({ name }) {
  useEffect(() => {
    console.log(`Hello, ${name}!`);
  }, []); // name이 변경되어도 실행되지 않음

  return <h1>Welcome, {name}!</h1>;
}

// ✅ 올바른 사용
function UserGreeting({ name }) {
  useEffect(() => {
    console.log(`Hello, ${name}!`);
  }, [name]); // name이 변경될 때마다 실행

  return <h1>Welcome, {name}!</h1>;
}
 

 

2. 비동기 작업 처리 시 주의사항

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

  useEffect(() => {
    let isSubscribed = true;

    const fetchData = async () => {
      const result = await fetch('https://api.example.com/data');
      const json = await result.json();
      
      // 컴포넌트가 언마운트되지 않았을 때만 상태 업데이트
      if (isSubscribed) {
        setData(json);
      }
    };

    fetchData();

    return () => {
      isSubscribed = false;
    };
  }, []);

  return <div>{data ? JSON.stringify(data) : '로딩 중...'}</div>;
}
 

 

빈 의존성 배열을 사용하는 useEffect는 다음과 같은 특징을 가진다

 

1. 컴포넌트 마운트 시 단 한 번만 실행된다

2. cleanup 함수는 컴포넌트 언마운트 시 실행된다

3. 주로 초기 설정, 구독, 이벤트 리스너 등을 관리할 때 사용한다

4. 불필요하게 사용하면 버그의 원인이 될 수 있다

 

이러한 특징을 잘 이해하고 적절한 상황에서 사용한다면, 컴포넌트의 생명주기를 효과적으로 관리할 수 있다. 하지만 무분별한 사용은 피하고, 실제로 의존성이 필요한 경우에는 해당 값들을 의존성 배열에 포함시키는 것이 좋다.

 

React 개발을 하면서 useEffect와 의존성 배열을 올바르게 사용하는 것은 매우 중요하다. 특히 빈 의존성 배열의 의미를 정확히 이해하고 적절한 상황에서 활용한다면, 더 안정적이고 예측 가능한 컴포넌트를 만들 수 있을 것이다.