이 글의 코드는 저자의 Github에서 확인할 수 있습니다.
지난글에서 useState
를 이미 살펴 보았습니다. 간단히 정리하자면 "함수형 컴포넌트에서도 state를 사용할 수 있다! 개꿀!" 이었죠? 하지만 사용만 한다고 다가 아닌것이 우리내 코딩 인생이고, 복잡한 로직을 위한 다양한 기능들을 살펴볼 필요가 당연히 있습니다. useEffect
는 그러한 기능들 중 하나로 useState
가 기존의 방식을 대체하는 것 처럼 해당 기능 역시 componentDidUpdate
와 componentDidMount
를 대신한다고 봐도 좋을 것 같습니다. 정확한 기능을 먼저 말해보자면, 리엑트 컴포넌트가 렌더링될때 자동으로 해당 함수를 호출하게 되는데, 지난글에서 설명한 Hook을 이를 통해 이해할 수 있을 것 같네요. 특정 동작이 발생하면 특정 행동을 자동으로 실행한다. 네, Hook입니다.
const UseEffectExample = () => {
const [test, setTest] = useState('initial value');
useEffect(() => {
console.log('렌더링!');
});
return (
<div>
<p>{test}</p>
<input onChange={(e) => {setTest(e.target.value)}} />
</div>
)
}
위의 코드를 봅시다. 지난 글의 코드에서 크게 바뀐건 없고, 아주아주 간단한 useEffect
를 추가했을 뿐입니다. 해당 컴포넌트가 렌더링 될 때마다 useEffect
함수가 실행되면서 콘솔에 로그를 남깁니다. 바로 이렇게 말이죠!
기존의 state처럼, state의 값이 변경될 경우 렌더링을 새로 시도하기 때문에, input에 타자를 칠때마다 로그가 찍히게 됩니다.
흠, 그런데 만약 state가 여러개이고 useEffect
함수가 그 state들이 변경될 때 마다 호출된다면 어떨까요? 딱 들어봐도 비효율 적일 것 같은 느낌이 나지 않나요? 이런 상황을 위해서 useEffect
는 두번째 매개변수 인자를 받을 수 있습니다.
useEffect(() => {
console.log('렌더링!');
},[test]);
위의 코드와 같이 배열 형태로 특정 state를 넘기게 되면, 해당 state의 값이 변경될 때만 useEffect
함수가 호출되게 됩니다. 흠, 그런데 만약 여러개의 state에 대해서 여러개의 개별적인 동작을 실행시키고 싶다면 어떨까요?
useEffect(() => {
console.log('test state에 대해서만 호출!')
}, [test])
useEffect(() => {
console.log('test2 state에 대해서만 호출!')
}, [test2])
간단합니다. 여러개의 useEffect
를 설정하면 됩니다. 간단하죠? 실제 동작을 살펴보면 아래와 같이 실행됩니다.
흠, 그런데 만약 렌더링 될 때마다 호출되는 것이 아니라 처음에 렌더링 될 때 한번만 실행하고 싶은 경우에는 어떨까요? 이 역시 방법이 있습니다. 매개변수를 넘기되, 빈 배열로 넘기는 겁니다. 바로 이렇게요.
useEffect(() => {
console.log('첫 렌더링에만 호출')
}, [])
이렇게 할 경우 처음 렌더링 되는 한번만 호출되고, 이후의 어떠한 렌더링에도 재호출 되지 않습니다. 이렇게요.
자 여기까지 해서 useEffect
에 대해서 알아 보았습니다.
컴포넌트가 렌더링 될 때마다 useEffect
함수를 호출하는건 이제 모두 알았을 것 같습니다. 그럼 컴포넌트가 unmount될때는 어떻게 해야 할까요? 사실 그런 경우에도 같은 구문을 사용하면 되지만 약간 변경해서 useEffect
함수에서 return을 해주면 됩니다.
useEffect(() => {
console.log('state가 변경될 때 마다 호출!');
return () => {
console.log('언마운트 시 호출!')
}
})
원래 없던 return이 추가됐습니다. 역시나 간단하게 콘솔에서 로그를 찍을뿐입니다. 해당 결과를 쉽게 예상이 되겠죠?
위의 이미지를 보면 state가 변경될 때 마다 기존의 컴포넌트가 unmount되면서 로그를 찍고 다시 렌더링 되면서 로그를 찍습니다. 눈치가 빠른 분들이면 이미 눈치 챘겠지만, 두번째 인자에 state를 줬을 경우에는 해당 state의 변화에 대한 unmount에만 반응하게 됩니다. 이 경우의 코드는 이미 너무 자명하기 때문에 생략합니다.
첫 렌더링에서만 useEffect
를 호출하는 방법이 있었습니다. 그럼 당연히 마지막 unmount에서만 호출하는 방법도 있겠죠? 있고말고요. 게다가 방식도 똑같습니다. 두번째 인자를 빈 배열로 주면 됩니다.
const UseEffectExample = () => {
const [test, setTest] = useState('initial state');
useEffect(() => {
console.log('첫 렌더링에만 호출');
return () => {
console.log('마지막 언마운트 시 호출')
}
}, [])
return (
<div>
<p>{test}</p>
<input onChange={(e) => {setTest(e.target.value)}} />
</div>
)
}
function App() {
const [useEffectRender, setUseEffectRender] = useState(true);
return (
<div className="App">
{useEffectRender && <UseEffectExample />}
<button onClick={() => {setUseEffectRender(!useEffectRender)}}>마지막 언마운트 호출하기!</button>
</div>
);
}
export default App;
자, 위의 코드는 마지막 언마운트 호출하기!
버튼을 누르면 useEffect
가 설정된 UseEffectExample
컴포넌트가 unmount되도록 설정되어 있습니다. 로그를 확인하면 알겠지만, 아무리 test state의 값을 변경해서 unmount/mount하더라도 로그는 찍히지 않습니다. 하지만 버튼을 눌러서 해당 컴포넌트를 완전히 unmount시키면 로그가 찍히게 됩니다.
위의 이미지와 같이 말이죠.
자 이렇게 해서 useEffect
에 대한 것을 알아 봤습니다. 원래는 useContext
에 대해서도 설명하려고 했는데 글이 너무 길어졌네요. 다음 시리즈에서 계속 하겠습니다.
설명이 이해가 쏙쏙 되네요~ 감사합니다!