FrontEnd Develop

Data Fetching : User의 능동성 여부와 useEffect의 올바른 공존

Frisbeen 2025. 1. 22. 19:03

서론

 

React 애플리케이션에서는 데이터를 서버에서 가져오는 작업(Fetching)이 필수적입니다. 하지만 데이터를 언제, 어떻게 가져올지는 사용자와 애플리케이션의 동작 방식에 따라 다릅니다.

 

이를 크게 두 가지로 나눌 수 있습니다:

1. 수동적 데이터 Fetching: 페이지가 로드되거나 특정 상태가 변경될 때, 애플리케이션이 자동으로 데이터를 서버에서 가져옵니다.

2. 능동적 데이터 Fetching: 사용자가 버튼 클릭이나 특정 액션을 통해 데이터를 요청합니다.

 

이 두 가지 경우는 각각의 특성과 요구사항이 다르기 때문에 React의 useEffect와 비동기 통신 함수의 역할이 달라집니다. 이번 포스팅에서는 상황에 맞는 useEffect와 비동기 함수의 활용법을 알아보고, 능동성과 수동성에 따라 useEffect를 사용할지 여부를 정리해보겠습니다.

 

1. 수동적 데이터 Fetching: useEffect가 적합한 경우

 

특징

 

수동적 데이터 Fetching은 페이지가 로드되거나 특정 상태가 변경될 때 데이터를 자동으로 서버에서 가져오는 방식입니다. 이 방식에서는 컴포넌트 라이프사이클과 상태 변화에 따라 데이터 Fetching이 이루어져야 하므로 useEffect가 적합합니다.

 

서버와의 통신은 axios를 선택했습니다.

 

쉽게 말하면 사용자의 의지없이 자동적으로 fetching이 되어야하는 구조입니다.

예를들면 로그인을 해서 메인페이지로 진입을 했을때, 자동적으로 자신의 이름 혹은 이메일이 보이는 경우가 이에 해당합니다.

 

코드 예제

 

다음은 사용자가 로그인한 후, 자동으로 프로필 데이터를 서버에서 가져오는 예제입니다:

 

흐름을 정리하면

1. AuthContext에서 주입해놓은 상태 isLoggedIn, user 그리고 상태변경함수 setUser을 가져옵니다. (이때 context 내부의 커스텀 훅useAuth()를 활용했습니다.

 

2. useEffect를 활용해, 진입 초기에 바로 Fetching이 가능하게끔 하였고, 이땐 불가피하게 비동기 함수인 axios.get을 활용해야합니다.

 

3. 서버에서 받아오는 데이터 구조는 JSON 형태의 객체 배열이기에 find method로 많은 JSON 객체중 내가 원하는 객체를 find합니다.

 

4. 따라서 response.data 라는 전체의 객체 배열에서 user.id ( 로그인 시 사용자가 입력한 데이터(객체)의 id와 u.id( 전체 데이터에서 변수 id)를 비교해서 올바른것을 찾습니다.

 

5. 해당 값이 존재하면 preceed합니다,

 

6. 찾고나서 해당하는 데이터의 필요한 속성들을 프로필에 표시합니다. 

 

import { useEffect } from 'react';
import { useAuth } from '../context/AuthContext';
import axios from 'axios';

const Profile = () => {
  const { isLoggedIn, user, setUser } = useAuth();

  useEffect(() => {
    const fetchUserData = async () => {
      if (isLoggedIn) {
        try {
          const response = await axios.get('http://localhost:4000/users');
          const currentUser = response.data.find((u) => u.id === user.id);
          if (currentUser) {
            setUser(currentUser);
          }
        } catch (error) {
          console.error('Error fetching user data:', error);
        }
      }
    };

    fetchUserData();
  }, [isLoggedIn, user?.id, setUser]); // 상태 변경 시 자동으로 실행

  return (
    <div>
      <h1>{user?.name || '이름 없음'}</h1>
      <p>{user?.email || '이메일 없음'}</p>
    </div>
  );
};

 

useEffect를 사용하는 이유 , 그리고 전역 상태 관리

1. 자동화된 Fetching: 컴포넌트가 로드되거나 상태가 변경될 때 자동으로 실행됩니다.

2. 라이프사이클 관리: React 컴포넌트의 마운트/언마운트 시점을 활용하여 데이터를 가져오고 정리(cleanup)할 수 있습니다.

 

-> 의존성 배열을 현재 isLoggedIn 상태와 user?.id, setUser라는 글로벌 state (Context)를 가져왔기에 글로벌적으로 변화가 일어나면 자동 최신화가 가능합니다.

 

 

useEffect 의존성 배열에 전역 상태를 넣었을 때의 장점

 

1. 자동 최신화

의존성 배열에 있는 상태가 변경될 때 useEffect가 다시 실행됩니다.

예를 들어, setUseruser 상태를 다른 컴포넌트에서 변경하면, 현재 컴포넌트의 useEffect가 재실행되어 새로운 user 데이터를 반영할 수 있습니다.

 

2. 데이터 일관성 유지

Context나 전역 상태는 여러 컴포넌트에서 공유되므로, 한 곳에서 상태가 변경되었을 때 의존성 배열 덕분에 다른 곳에서도 최신 상태를 반영할 수 있습니다.

이로 인해 React 컴포넌트 트리 전체에서 동기화된 상태를 유지할 수 있습니다.

 

3. React의 상태 관리 생태계 활용

useEffect는 React의 상태 관리와 잘 통합되어 있어, 전역 상태 관리 라이브러리(Context, Redux 등)와 함께 사용할 때 강력한 동작을 보입니다.

예를 들어, user가 Context로 관리되고 있고, 다른 컴포넌트에서 setUser를 호출하면, 이 컴포넌트는 그 변경 사항을 자동으로 반영합니다.

 

예시: 전역 상태 변경에 따른 최신화

 

1. Context 상태 정의

import React, { createContext, useContext, useState } from 'react';

const AuthContext = createContext();

export const AuthProvider = ({ children }) => {
  const [isLoggedIn, setIsLoggedIn] = useState(false);
  const [user, setUser] = useState(null);

  return (
    <AuthContext.Provider value={{ isLoggedIn, setIsLoggedIn, user, setUser }}>
      {children}
    </AuthContext.Provider>
  );
};

export const useAuth = () => useContext(AuthContext);

 

 

2. 프로필 컴포넌트에서 useEffect 활용

import { useEffect } from 'react';
import { useAuth } from '../context/AuthContext';

const Profile = () => {
  const { isLoggedIn, user, setUser } = useAuth();

  useEffect(() => {
    console.log('프로필이 자동으로 최신화되었습니다!', user);
  }, [isLoggedIn, user?.id, setUser]); // 전역 상태 의존성 배열

  return (
    <div>
      <h1>{user?.name || '이름 없음'}</h1>
      <p>{user?.email || '이메일 없음'}</p>
    </div>
  );
};

 

3. 상태 변경을 트리거하는 다른 컴포넌트

import { useAuth } from '../context/AuthContext';

const UpdateUser = () => {
  const { setUser } = useAuth();

  const changeUser = () => {
    setUser({ id: 2, name: 'Alice', email: 'alice@example.com' });
  };

  return <button onClick={changeUser}>사용자 변경</button>;
};

 

 

결과

1. 초기 상태

usernull로 설정되어 있고, 프로필 컴포넌트는 “이름 없음”과 “이메일 없음”을 표시합니다.

2. 사용자 변경 버튼 클릭

UpdateUser 컴포넌트에서 setUser를 호출하여 새로운 사용자 데이터를 설정합니다.

3. 자동 최신화

user 상태가 변경되면서 useEffect가 다시 실행됩니다.

프로필 컴포넌트는 새로운 사용자 데이터를 즉시 반영하여 화면을 업데이트합니다.

 

주의사항

 

1. 의존성 배열 최적화

의존성 배열에 넣은 상태나 함수가 변경되면 useEffect가 실행됩니다. 따라서 의존성을 정확히 설정해야 불필요한 재실행을 방지할 수 있습니다.

예: setUser는 일반적으로 의존성 배열에 추가하지 않아도 됩니다. 이유는 React가 제공하는 setState 함수는 변하지 않기 때문입니다.

 

2. 불필요한 의존성 추가 방지

의존성 배열에 불필요한 값을 추가하면 성능 저하가 발생할 수 있습니다.

eslint-plugin-react-hooks를 사용하면 의존성 배열의 정확성을 자동으로 검사해줍니다.

 

3. Context 상태 관리의 복잡성

여러 컴포넌트에서 전역 상태를 관리할 경우, 의존성 배열이 너무 많아질 수 있습니다. 이런 경우, 상태 관리 라이브러리(Redux 등)를 고려할 수도 있습니다.

 

 

2. 능동적 데이터 Fetching: useEffect가 필요 없는 경우

 

특징

 

능동적 데이터 Fetching은 사용자가 특정 버튼을 클릭하거나 액션을 취했을 때 데이터를 서버에서 요청합니다. 이 경우, Fetching의 트리거가 사용자 행동이기 때문에 useEffect는 불필요합니다.

 

코드 예제

 

다음은 사용자가 “새로고침” 버튼을 클릭했을 때 데이터를 가져오는 예제입니다:

import { useState } from 'react';
import axios from 'axios';

const FetchOnButtonClick = () => {
  const [data, setData] = useState(null);

  const handleFetchData = async () => {
    try {
      const response = await axios.get('http://localhost:4000/data');
      setData(response.data);
    } catch (error) {
      console.error('Error fetching data:', error);
    }
  };

  return (
    <div>
      <button onClick={handleFetchData}>데이터 가져오기</button>
      {data && <div>{JSON.stringify(data)}</div>}
    </div>
  );
};

 

useEffect를 사용하지 않는 이유

1. 사용자 행동이 트리거: 버튼 클릭과 같은 명확한 이벤트가 있으므로, 굳이 useEffect로 관리할 필요가 없습니다.

2. 불필요한 호출 방지: 사용자가 원할 때만 데이터를 가져오기 때문에 불필요한 데이터 요청이 없습니다.

 

예시

버튼 클릭 시 최신 데이터 가져오기 (e.g., 새로고침)

검색 폼에서 검색 버튼 클릭 후 데이터 요청

파일 업로드 후 결과 확인

 

-> 이런것들을 사용자가 직접 서버에서 가져와야하니 useEffect가 필요없습니다.

 

3. 능동성과 수동성에 따른 useEffect 활용 요약

THANKS, GPT

 

 

참고!  Context API와의 결합

 

Context를 활용한 데이터 관리

 

Context는 전역 상태를 관리하며, useEffect를 사용한 수동적 Fetching과 능동적 Fetching 모두를 지원합니다.

1. 수동적 Fetching 예제

로그인 상태를 Context로 관리하며, useEffect에서 사용자 데이터를 가져옵니다.

 

2. 능동적 Fetching 예제

버튼 클릭 시, Context의 상태를 업데이트하여 데이터 Fetching 결과를 저장합니다.

 

const ProfileWithManualFetch = () => {
  const { isLoggedIn, user, setUser } = useAuth();

  const fetchUserData = async () => {
    try {
      const response = await axios.get('http://localhost:4000/users');
      const currentUser = response.data.find((u) => u.id === user.id);
      if (currentUser) {
        setUser(currentUser);
      }
    } catch (error) {
      console.error('Error fetching user data:', error);
    }
  };

  return (
    <div>
      {isLoggedIn ? (
        <>
          <h1>{user?.name || '이름 없음'}</h1>
          <p>{user?.email || '이메일 없음'}</p>
          <button onClick={fetchUserData}>프로필 새로고침</button>
        </>
      ) : (
        <p>로그인이 필요합니다.</p>
      )}
    </div>
  );
};

 

5. 배운 점과 정리

useEffect수동적 데이터 Fetching에 적합하며, 컴포넌트 라이프사이클 또는 상태 변경에 따라 데이터를 가져옵니다.

능동적 데이터 Fetching에서는 useEffect 대신, 명시적인 이벤트 핸들러를 통해 데이터를 가져오는 것이 더 효율적입니다.

Context API를 활용하여 전역 상태를 관리하면 수동적/능동적 Fetching 모두 더 쉽게 통합할 수 있습니다.

상황에 맞는 패턴을 선택하여 데이터 Fetching의 효율성을 높이는 것이 중요합니다.

 

 

결론

 

React에서 데이터 Fetching은 상황에 따라 다른 접근 방식이 필요합니다. 능동성과 수동성을 구분하고 useEffect와 비동기 함수의 역할을 명확히 이해하면 더 나은 코드를 작성할 수 있습니다.