React를 공부하면서 useEffect에 대해 알고 있다고 생각했는데 간단한 프로젝트를 진행하면서 막혔던 부분 중 하나가 바로 useEffect였다. 그래서 다시 한번 정리하고 정확히 알아보려고 한다.
문제
그렇다면 왜 useEffect를 다시 공부하는가? 어떤 부분에서 막혔는가?에 대해 말하자면 react-router-dom의 createBrowserRouter를 사용하면서 막혔다. 사용자 인증 여부를 검사하는 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를 정리하고 공부하기로 결정했다. (위 문제의 해결은 아래의 링크를 통해 확인할 수 있습니다.)
useEffect 란?
useEffect를 좀 더 쉽게 표현하면 컴포넌트가 렌더링 될 때 특정 작업을 수행할 수 있도록 설정하는 React Hooks중 하나이다. 즉 클래스형 컴포넌트의 생명주기 메소드들의 기능을 합쳐 함수형 컴포넌트에서도 사용할 수 있도록 만든 것이 useEffect이다. 데이터 fetching, 직접적인 DOM 조작, 타이머 설정과 같은 작업들을 수행할 때 사용한다.
useEffect 기본적인 사용법
useEffect는 첫 번째 인자와, 두 번째 인자를 가진다. 첫 번째 인자에서는 특정 작업을 수행할 함수와 필요에 따라 정리 함수가 사용될 수 있으며, 두 번째 인자로는 의존성 배열을 가진다. 의존성 배열에는 여러 개의 의존성을 포함할 수 있다.
useEffect(() => {
//특정 작업(side effects)을 수행할 함수
}, [의존성 배열])
첫 번째 인자에 들어가는 sideEffects를 수행 할 함수는 비동기적으로 수행될 수 있으며, 필요에 따라 clean-up 함수가 반환될 수 있다. 두 번째 인자에 들어가는 의존성 배열의 값에 따라 useEffect hook의 실행 횟수를 결정한다.
- 컴포넌트가 마운트 될 때만 사용하기
useEffect(() => {
console.log("컴포넌트가 마운트됐습니다.");
}, []);
두 번째 인자인 의존성 배열이 비어있기 때문에 해당 컴포넌트가 마운트 될 때 한번 sideEffects인 console.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 |
댓글