React-useEffect
React-useEffect
useEffect ( ) 함수는 컴포넌트가 렌더링 될 때마다 특정 작업 (Side effect) 을 실행할 수 있도록 하는 리액트 Hook 이다.
Side effect 는 component 가 렌더링 된 이후에 비동기로 처리되어야 하는 부수적인 효과들을 뜻한다.
이러한 기능으로 인해 함수형 컴포넌트에서도 클래스형 컴포넌트에서 사용했던 생명주기 메서드를 사용할 수 있게 되었다.
useEfect는 함수이고, 매개변수로 콜백 함수를 가진다.
useEffect 에 인자로 전달하는 콜백 함수에는 특정한 side effect 를 수행 시킬 수 있다.
const App = () => {
doSideEffect();
return <h1>hello</h1>
}위 코드에서 side effect 를 렌더링 전에 발생시키고 있다.
이러한 코드는 렌더링을 blocking 하기에 좋지 않다는 측면을 알고 있다.
그렇다면 이 문제를 해결하기 위해서 useEffect 를 사용핼 볼 수 있다.
import { useEffect } from 'react';
const App = () => {
doSideEffect();
}side effect 를 발생시키는 함수를 바로 호출 하는것이 아니라 useEffect 의 인자로 전달 했다. 위와 같이 useEffect 의 인자로 전달된 콜백 함수는 곧바로 호출되는 것이 아니라 모든 렌더링이 완료된 후에 호출된다.
즉, 렌더링을 blocking 하지않고 side effect 를 발생시킬 수 있게 되는 것이다.
useEffect 를 통해서 렌더링이 모두 다 완료되고 난 후 side effect 를 실행한다는 요구사항은 충족되었지만 아직도 매 렌더링 마다 side effect 가 실행된다는 사실은 변함이 없다.
useEffect 는 콜백함수 외에 한가지 매개변수를 더 가지고 있다.
useEffect 는 콜백함수 외에 의존성 배열 (dpendencyArray) 이라는 두번째 매개변수를 가진다. 의존성 배열은 이름에서부터 알 수 있듯이 배열의 형태이다.
이 배열이 바로 side effect 의 발생 여부를 결정짓는 조건 이다.
useEffect 의 동작 방식은 간단하다.
첫 번재 렌더링 이후에는 무조건 useEffect 에 전달된 콜백함수를 호출하고 다은 렌더링부터는 아래의 조건에 따라 동작 한다.
- 의존성 배열이 전달되지 않았다면 매 렌더링마다 콜백 함수를 호출 한다.
- 의존성 배열이 전달되었다면 의존성 배열의 값을 감사한다.
- a. 의존성 배열에 있는 값 중 하나라도 이전 렌더링과 비교했을때 달라졌다면 콜백 함수를 호출 한다.
- b. 의존성 배열에 있는 값이 이전 렌더링과 비교했을 때 모두 다 같다면 콜백 함수를 호출하지 않는다.
즉, useEffect 에서 첫번째 인자인 콜백 함수는 실행시킬 동작을 결정하고 두 번째 인자인 의존성 배열은 실행시킬 타이밍을 결정짓는다고 할 수 있다.
Rendering & Effect Cycle
함수 컴포넌트의 렌더링과 useEffect 가 발생하는 과정을 풀어 설명 하면 아래와 같다.
- 컴포넌트가 렌더링 된다.
( 최초로 진행되는 렌더링은 브라우저에 처음으로 이 컴포넌트가 보였다는 의미로 mount 라고 표현한다. ) 2. useEffect 첫 번째 인자로 넘겨준 콜백 함수가 호출 된다. (side effect) 3. 컴포넌트의 state 또는 props 가 변경되었을 경우 리렌더링이 발생한다. 4. useEffect 는 두번째 인자에 들어있는 의존성 배열을 확인한다.
- 만약 의존성 배열이 전달되지 않았거나, 의존성 배열 내부의 값 중 이전 렌더링과 비교했을때 변경된 값이 하나라도 있다면 첫 번째 인자로 넘겨준 콜백 함수가 호출된다. ( sideEffect )
- 의존성 배열 내부의 값 중 이전 렌더링과 비교했을때 변경된 값이 없다면 콜백 함수를 호출 하지 않는다.
- state 또는 props 가 변경된다면 3~4 의 과정을 반복
- 컴포넌트가 더 이상 필요 없어지면 화면에서 사라진다.
( 컴포넌트가 브라우저의 화면에서 사라졌다는 ㅡ이미로 unmount 라고 표현한다. )
Clean up Effect
clean up 필요성 side effect 에는 여러 종류가 있을 수 있다.
그중에서 반드시 clean up 이 필요한 effect 들이 있을 수 있다.
useEffect (()=> {
const contTime = () => {
console.log('100ms 가 지났습니다. ');
};
setInterval ( countTime, 100);
}, []);위 side effect 는 clean up 이 필요 하다.
이 side effect 는 setInterval 함수를 이용해서 100ms 마다 countTime 함수가 호출 되도록 하고 있다.
useEffect 의 의존성 배열에 빈 배열이 전달되었으므로 첫 번째 렌더링 이후에 side effect 가 실행된다.
그런데 side effect 를 clean up 해주지 않는다면 컴포넌트가 unmount 되는 경우 등 setInterval 을 통한 구독이 필요 없어진 상황에서도 계속해서 console 이 출력되고 있을 것이다.
clean up 하는 방법 useEffect 는 side effect 를 clean up 할 수 있는 방법을 제공해 준다. 결론부터 말하자면 useEffect 에서 side effect 를 clean up 하기 위해서는 useEffect에 전달한 콜백 함수에서 clean up 을 하는 함수를 return 하면 된다.
useEffect(()=> {
const button = document.getElementById ('consoleButton');
const printConsole = ()=> {
console.log('button clicked ');
};
button.addEventListener ('click', printConsole);
// side effect 를 clean up 하기 위한 함수를 선언한다.
const removeEventListener = () => {
button.removeEventListener ('click', printConsole);
};
// clean up 함수를 return 한다.
return removeEventListener;
});위와 같이 발생시킨 side effect 를 상쇄하기 위한 함수를 만든 뒤 그 함수를 return 해주면 된다.
addEventListener 로 등록한 eventListner 는 removeEventListener 함수를 통해서 제거할 수 있기 때문에 해당 동작을 하는 함수 (clean up)를 만든 뒤 콜백 함수 내에서 clean up 함수를 return 해주었다.
clean up 함수를 return 만 해준다면 clean up 함수를 적절한 시점에 호출해 주는 일은 useEffect 가 알아서 처리 해준다.
useEffect 에 의존성 배열을 전달하지 않았기 때문에 해당 effect 는 매 렌더링마다 실행 된다.
리렌더링이 발생해서 useEffect가 다시 호출되는 상황이 발생한다.
a. clean up 함수를 리턴해주었기 때문에 clean up 함수가 호출된다.
b. clean up 함수가 호출된 뒤 effect 가 발생한다.
c. 리렌더링이 발생하면 a ~ b 의 과정이 반복 된다. 컴포넌트가 unmount 되면 clean up 함수가 호출 된다.
정리
- React 에서 side effect 를 발생시킬 때는 조건을 충족 시켜야 한다.
- 렌더링 이후에 발생시켜야 한다.
- 매 렌더링 이후가 아니라 조건부로 원하는 순간에만 실행할 수 있어야 한다.
- React 에서는 위의 조건을 충족시키면서 side effect 를 발생시킬 수 있는 useEffect 라는 hook 을 제공해 주며 이를 통해서 손쉽게 side effect 를 발생 시킬 수 있다.
- 일부 side effect는 발생시킨 후 다시 clean up 하는 과정이 필요할 수 있다.
- useEffect 에 인자로 전달한 콜백 함수에서 함수를 return 하면 다음 effect 가 실행되기 전과 컴포넌트가 unmount 될 때 return 된 함수를 호출해 주며 이를 통해 기존의 side effect 를 clean up 할 수 있다.