준영이의 성장일기(FrontEnd)
React 공식문서 도장깨기(2)-1 : useEffect 본문
저번 글에 이어서 이번 공식문서를 공부하고 정리하는 내용은 useEffect이다. 오늘 블로그는 공식문서 기준으로 반응형 의존성 지정 카테고리까지 정리하고자 한다. 또한 useEffect에 대해서 정리하면서 동시에 최근에 프로젝트에서 사용한 이미지 프리로딩도 정리하고자 한다.
<참고 레퍼런스>
https://react-ko.dev/reference/react/useEffect
useEffect – React
The library for web and native user interfaces
react-ko.dev
<useEffect>
개념: useEffect는 컴포넌트가 렌더링 될 때마다 특정 작업(side effect)을 실행할 수 있도록 하는 React Hook이다. 즉 컴포넌트를 외부 시스템과 동기화 할 수 있는 Hook 이다. 컴포넌트의 최상위 레벨에서 useEffect를 선언한다. 또한 useEffect는 서버 렌더링 중에는 실행되지 않으며 오로지 클라이언트 단에서 실행된다.
중요한 특징 중 하나는 클래스형 컴포넌트에서만 사용할 수 있었던 componentDidMount, componentDidUpdate, componentWillUnmount와 같은 라이프사이클 메서드의 역할을 함수형 컴포넌트에서 할 수 있도록 도와준다.
useEffect(setup, dependencies?)
useEffect(() => {
console.log("처음 렌더링될 때 한번만 실행")
}, [])
✅ setup함수 쪽은 Effect의 로직이 포함된 함수이고 선택적으로 클린업 함수를 적용할 수 있다. React는 (클린업 함수가 있는 경우) 먼저 이전 값으로 클린업 함수를 실행한 다음, 새 값으로 셋업 함수를 실행한다. 그리고 컴포넌트가 DOM에서 제거되면, React는 마지막으로 클린업 함수를 실행한다.
✅ dependencies는 useEffect의 의존값으로 React는 각 의존성에 대해 Object.is로 이전 값과 비교하여 의존값이 변화한다면 변화 할 때마다 useEffect를 호출한다. 무조건 넣어야 되는 값은 아니고 아예 생략하거나 빈 배열만 두거나 할 수 있다.
아예 생략할 경우 렌더링 될 때마다 호출하게 되고 빈 배열로 설정한다면 처음 렌더링될때, 딱 한번만 실행되도록 한다.
✅ Effect를 사용하면 컴포넌트를 외부 시스템(예: 채팅 서비스)과 동기화를 유지할 수 있다. 여기서 외부 시스템이란 React로 제어되지 않는 코드 조각을 의미한다. 나 같은 경우 setInterval를 사용하여 프리로딩 해놓은 이미지들이 5초마다 차례대로 렌더링 되도록 이용하였다.
const images = [
'/src/assets/studying.jpg',
'/src/assets/studying2.jpg',
'/src/assets/studying3.jpg',
'/src/assets/studying4.jpg',
]
function preloadImages(imageArray) {
imageArray.forEach((image) => {
const img = new Image()
img.src = image
})
}
const [currentImageIndex, setCurrentImageIndex] = useState(0)
useEffect(() => {
preloadImages(images)
const intervalId = setInterval(() => {
setCurrentImageIndex((prevIndex) => (prevIndex + 1) % images.length)
}, 5000)
return () => clearInterval(intervalId)
}, [])
이 코드에서 useEffect는 컴포넌트가 처음 렌더링될 때 실행된다.(왜냐면 빈 배열이니까..!) 그리고 이미지들을 미리 로드하기 위해 preloadImages 함수를 사용하였고, 이 과정을 통해 사용자 경험을 개선하였다.
이미지 프리로딩이란?
개념: 사용자가 이미지를 실제로 보기 전에 미리 이미지를 로드하는 과정을 말한다. 이미지 프리로딩을 적용하기 전에는 이미지가 5초마다 바뀔 때 이미지가 로드되는 과정에서 지연이 발생하는 경우 화면이 깜박거리는 현상이 존재했다. 그래서 이미지 프리로딩을 적용해보았고 이것을 개발자 도구의 네트워크 탭에서 확인할 수 있었다.
❗ 이 과정에서 새로운 이미지의 로드가 지연될 경우 화면이 깜박거리는 현상이 발생했다..! 이후 이미지 프리로딩을 적용하였고 밑에 첨부된 사진 처럼 이미지 로드 지연을 줄이고 사용자 경험을 개선함과 동시에 부드러운 전환이 되도록 하였다.
커스텀 훅과 useEffect의 조합
공식문서에 나와있는 코드이다. 커스텀 훅을 적용하지 않은 코드이며 채팅방과의 연결 로직이 직접 컴포넌트 내부에 작성 되어있다.
function ChatRoom({ roomId }) {
const [serverUrl, setServerUrl] = useState('https://localhost:1234');
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
connection.disconnect();
};
}, [roomId, serverUrl]);
return (
<>
<label>
Server URL:{' '}
<input
value={serverUrl}
onChange={e => setServerUrl(e.target.value)}
/>
</label>
<h1>Welcome to the {roomId} room!</h1>
</>
);
}
연결 로직을 필요한 컴포넌트에서 재 사용 할 수 있도록 커스텀 훅으로 분리하면 다음과 같다. 이와 같이 분리를 하면 채팅방 연결 로직을 한 곳에서 관리할 수 있어 코드 중복이 없고, 유지보수 또한 용이하다.
import { useEffect } from 'react';
import { createConnection } from './chat.js';
export function useChatRoom({ serverUrl, roomId }) {
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
connection.disconnect();
};
}, [roomId, serverUrl]);
}
useEffect와 반응형 의존성
function ChatRoom({ roomId }) { // This is a reactive value
const [serverUrl, setServerUrl] = useState('https://localhost:1234'); // This is a reactive value too
useEffect(() => {
const connection = createConnection(serverUrl, roomId); // This Effect reads these reactive values
connection.connect();
return () => connection.disconnect();
}, [serverUrl, roomId]); // ✅ So you must specify them as dependencies of your Effect
// ...
}
의존성 배열은 useEffect 안에서 사용된 모든 반응형 값에 의해 결정된다. 만약 어떤 반응형 값을 사용하고도 의존성 배열에 포함시키지 않는다면, 그 값이 변경되었을 때 useEffect가 다시 실행되지 않을 수 있으며, 이는 버그를 유발 할 수 있다. 여기서 반응형 값 이란 state나 props처럼 컴포넌트의 상태나 속성, 혹은 외부에서 전달된 값을 의미한다.
해당 코드를 보면 serverUrl은 state로 사용되고 있으므로 만약 의존성 배열에 생략하게 된다면 해당 경고 메시지가 출력된다. 만약 빈 배열로 두고 싶다면 serverUrl을 ChatRoom 컴포넌트 밖에 선언 및 초기화 해야한다. 즉, 의존성이어야 할 필요가 없음을 알려줘야 한다.( Effect의 코드가 반응형 값을 사용하지 않는다면 의존성 목록은 비어 있어야 정답)
'프론트엔드 > React.js' 카테고리의 다른 글
React 공식문서 도장깨기(3) - useRef (1) | 2024.08.29 |
---|---|
React 공식문서 도장깨기(2)-2 : useEffect, useLayoutEffect (3) | 2024.08.27 |
React 공식문서 도장깨기(1) - useState (0) | 2024.08.21 |
React useEffect, 상태 업데이트의 비동기성 (2) | 2024.08.17 |
Rendered more hooks than during the previous render (0) | 2024.08.03 |