useEffect 정리하기 (1)

    반응형

    useEffect

     

    React를 공부하면서 useEffect에 대해 알고 있다고 생각했는데 간단한 프로젝트를 진행하면서 막혔던 부분 중 하나가 바로 useEffect였다. 그래서 다시 한번 정리하고 정확히 알아보려고 한다. 

     

    문제


    그렇다면 왜 useEffect를 다시 공부하는가? 어떤 부분에서 막혔는가?에 대해 말하자면 react-router-domcreateBrowserRouter를 사용하면서 막혔다. 사용자 인증 여부를 검사하는 AuthenticatedRoute를 만들면서 사용자 인증이 있다면 outlet을 통해 children에 접근이 가능해야 하는데 계속 Navigate 로 리다이렉션 되는 문제가 발생했다.

     

    import { Navigate, Outlet } from "react-router-dom";
    import { getAuth, onAuthStateChanged } from 'firebase/auth';
    import { app } from '../firebaseApp';
    import { useState, useEffect } from "react";
    
    const AuthenticatedRoute = () => {
      const auth = getAuth(app);
    
      const [isAuthenticated, setAuthenticated] = useState<boolean>(!!auth.currentUser);
    
      useEffect(() => {
      	onAuthStateChanged(auth, () => {
        	if(user) {
            	setAuthenticated(true)
            } else {
            	setAuthenticated(false)
            }
        })
      }, [auth]);
    
      return (
        isAuthenticated ? <Outlet /> : <Navigate to='users/login' replace />
      );
    }
    
    export default AuthenticatedRoute;

     

    const router = createBrowserRouter([
      {
        path: '/',
        element: <App />,
        errorElement: <ErrorPage />,
        children: [
          { path: '/', element: <HomePage /> },
          {
            path: '',
            element: <AuthenticatedRoute />,
            children: [
              { path: 'posts', element: <PostList /> },
              { path: 'posts/:id', element: <PostDetail /> },
              { path: 'posts/edit/:id', element: <PostEdit /> },
              { path: 'posts/new', element: <NewPost /> },
              { path: 'profile', element: <ProfilePage /> },
              { path: 'profile/edit', element: <ProfileEdit /> },
              { path: 'notification', element: <NotificationPage /> },
              { path: 'search', element: <SearchPage /> },
            ]
          },
          {
            path: 'users',
            children: [
              { path: 'login', element: <Login /> },
              { path: 'signup', element: <SignUp /> }
            ]
          }
        ]
      }
    ])
    
    export default router

     

    createBrowserRouter를 사용해 경로를 구성하고 사용자 인증 유무에 따라 보이는 페이지를 다르게 하기 위해 AuthenticatedRoute를 만들어 인증 유무를 검사했다. 정상적으로 로그인을 성공하고 사용자 인증정보를 가진 채 페이지를 이동해도 계속해서 '/users/login'으로 리다이렉션 되는 문제가 있었는데 이 문제 때문에 다시한번 useEffect를 정리하고 공부하기로 결정했다. (위 문제의 해결은 아래의 링크를 통해 확인할 수 있습니다.)

     

     

    createBrowserRouter 사용자 인증 유무에 따른 페이지 렌더링 방법

    React 프로젝트를 진행하면서 React-Router-Dom을 사용하게 되었는데, 기존에 사용했던 대신 를 사용해 보기로 결정했다. 기존 에서는 사용자 인증 여부를 useState에 담아 일부분만 보여주는 식으로 페

    dev.hoostory.com

     

    useEffect 란?


    useEffect를 좀 더 쉽게 표현하면 컴포넌트가 렌더링 될 때 특정 작업을 수행할 수 있도록 설정하는 React Hooks중 하나이다. 즉 클래스형 컴포넌트의 생명주기 메소드들의 기능을 합쳐 함수형 컴포넌트에서도 사용할 수 있도록 만든 것이 useEffect이다. 데이터 fetching, 직접적인 DOM 조작, 타이머 설정과 같은 작업들을 수행할 때 사용한다. 

     

    useEffect 기본적인 사용법


    useEffect는 첫 번째 인자와, 두 번째 인자를 가진다. 첫 번째 인자에서는 특정 작업을 수행할 함수와 필요에 따라 정리 함수가 사용될 수 있으며, 두 번째 인자로는 의존성 배열을 가진다. 의존성 배열에는 여러 개의 의존성을 포함할 수 있다.

     

    useEffect(() => {
    	//특정 작업(side effects)을 수행할 함수
    }, [의존성 배열])

     

    첫 번째 인자에 들어가는 sideEffects를 수행 할 함수는 비동기적으로 수행될 수 있으며, 필요에 따라 clean-up 함수가 반환될 수 있다. 두 번째 인자에 들어가는 의존성 배열의 값에 따라 useEffect hook의 실행 횟수를 결정한다.

     

    • 컴포넌트가 마운트 될 때만 사용하기
    useEffect(() => {
      console.log("컴포넌트가 마운트됐습니다.");
    }, []);

     

    두 번째 인자인 의존성 배열이 비어있기 때문에 해당 컴포넌트가 마운트 될 때 한번 sideEffectsconsole.log("컴포넌트가 마운트됐습니다.") 가 실행된다.

     

    • 의존성 배열이 변경될 때마다 실행하기
    function App() {
      const [count, setCount] = useState(0)
    
      useEffect(() => {
        document.title = `클릭횟수 ${count}`
      }, [count])
    
      return (
        <div>
          <button onClick={() => setCount(count + 1)}>
            +
          </button>
        </div>
      );
    }
    
    export default App;

     

    의존성 배열에 count 상태 값을 입력하고 버튼을 클릭할 때마다 브라우저 탭의 title이 변경되는 것을 확인할 수 있다. 즉 의존성 배열에 있는 상태 count가 변할 때마다 sideEffects documnet.title 의 값을 변화하는 함수가 실행되는 것이다. 여러개의 의존성을 포함시켰다면 의존성 배열의 값중 하나라도 변경되면 실행된다.

     

    • clean-up 함수를 사용하는 경우
      useEffect(() => {
        const timer = setTimeout(() => {
          console.log("타이머 실행");
        }, 1000);
    
        return () => {
          clearTimeout(timer); // 컴포넌트가 언마운트되거나, 의존성이 변경되어 재실행되기 전에 타이머를 취소
          console.log("정리 함수 실행");
        };
      })

     

    clean-up 함수의 사용 예시이다. 해당 컴포넌트가 마운트 되면 useEffect 내부의 sideEffects 함수가 등록되고, clean-up 함수가 실행된다. 즉 위 코드의 실행 순서는 컴포넌트가 마운트 되면 콘솔에 "정리 함수 실행" 이 먼저 출력되고 1초 뒤에 "타이머 실행" 이 실행된다.  그 이유는 useEffect 내부의 sideEffects 함수들은 비동기적으로 실행되기 때문이다.

     

    반응형

     

    clean-up 함수를 사용할 때 의존성 배열이 없다면 컴포넌트가 리렌더링 될 때마다 실행되고, 빈 의존성 배열이 있는 경우 컴포넌트가 언마운트 될 때 clean-up 함수가 실행된다. 의존성 배열에 값이 있는 경우 컴포넌트가 언마운트 될 때, 의존성 배열의 값이 변하는 경우에 clean-up 함수가 실행된다.

     

    function App() {
    
      useEffect(() => {
        const timer = setTimeout(() => {
          console.log("타이머 실행");
        }, 1000);
    
        return () => {
          clearTimeout(timer); 
          console.log("정리 함수 실행");
        };
      }, [])
    
    
      return (
        <div>
          App 컴포넌트!
        </div>
      );
    }
    
    export default App;

     

    예를 들어 이러한 useEffect를 사용한 컴포넌트가 있다고 가정하면 이 코드의 동작 순서는 다음과 같다.

    • App 컴포넌트의 렌더링
    • useEffect hook 이 task queue에 저장
    • useEffect 내부의 clean-up 함수 리턴 실행
    • 콘솔창에 "정리 함수 실행"
    • task-queue 저장된 useEffect 함수 실행
    • 1초 뒤 timer 콜백 함수 실행
    • 콘솔창에 "타이머 실행" 출력
    • 컴포넌트 언마운트시 clean-up 함수 실행
    • 콘솔창에 "정리 함수 실행" 출력

     

    2편에 계속..

    반응형

    'React' 카테고리의 다른 글

    React, ErrorBoundary 사용해서 에러 처리하는 방법  (0) 2024.07.09
    React 웹 폰트 최적화하기(2)  (0) 2024.05.02
    React 웹 폰트 최적화하기(1)  (0) 2024.05.02
    React 이해하기  (0) 2024.04.14
    useEffect 정리하기(2)  (0) 2024.03.22

    댓글