useEffect

박재성·2022년 4월 16일
0
post-custom-banner

컴포넌트는 어려워

리액트 컴포넌트를 관리하고 꿰뚫어 보는 것은 매우 어려운 일이다. 공부를 하며 가장 어렵다고 느껴지는 부분은 새로운 것을 학습할 때가 아니라 그 자체에 대한 기본을 갈고 닦는 것 같다.

생명주기

리액트 훅을 이해하기 위해선 기본적으로 생명주기를 알아야 한다. 기본적으로 useEffect는 컴포넌트가 랜더링 된 후 어떤 일을 수행하는지 관리하는 역할을 합니다.

컴포넌트가 처음 실행 될 때를 mount라고 합니다. class 컴포넌트 방식으로 리액트를 구성할 때는 특정 시점마다 설정을 줘야했지만 함수현 컴포넌트의 훅이 나오면서 복잡한 과정을 사용자가 쉽게 조정할 수 있게 바뀌었습니다.

그래도 리액트의 생명주기를 설명하기 위해서는 각각의 단계를 알아야 합니다.

componentWillMount

컴포넌트가 처음 호출 되었을 때, context, props, state가 저장이 되고 componentWillMount가 호출됩니다. 컴포넌트가 mount되기 바로 직전의 상태를 의미합니다. 여기서 주의할 점은 컴포넌트를 랜더 시키기 위한 상태이기 때문에 props나 state를 바꾸면 컴포넌트를 랜더 시킬 수 없습니다.

componentDidMount

componentWillMount가 호출 되면 컴포넌트가 랜더링 되고 componentDidMount가 호출됩니다. 리액트 컴포넌트가 랜더링 된 직후를 의미합니다.

만약에 랜더링 된 컴포넌트의 값을 바구고 싶을 때 새롭게 컴포넌트를 생성해서 다시 mount를 한다면 리액트는 지금의 리액트가 될 수 있었을까요? 애초에 이 질문은 리액트의 본질을 무시하는 질문입니다..ㅎ

componentDidUpdate

해당 컴포넌트의 값을 변경해서 리액트 페이지에서 해당 컴포넌트를 새롭게 업데이트 하는 상태입니다.

업데이트가 일어나기 전 값이 변경 되었음을을 감지하고, 여러 메소드를 호출하고 업데이트를 완료합니다. 새롭게 바뀐 컴포넌트가 랜더링이 완료되면 그 상활을 componentDidUpdate라고 합니다.

componentWillUnmount

마지막으로 컴포넌트가 제거되는 상태가 componentWillUnmount입니다.

리액트의 생명주기는 이렇게 4가지 과정을 반복합니다.

매번 상태를 관리할 때 모든 과정을써줘야 하는 번거로음을 함수형 컴포넌트의 hook이 완전히 대체하게 됩니다.

useEffect

useEffect는 엄청난 단순화로 리액트에 혁명을 주게 됩니다.

useEffect(() => {

}, [])

기본적인 구조는 인자로 콜백함수와, dependency를 받아옵니다. dependency를 받지 않는 경우도 있습니다.

dependency가 없이 콜백함수만 있다면 컴포넌트가 랜더링 될 때마다 실행하는 것을의미하고, 배열 형태로 받아온 dependency의 state가 업데이트 될 때와 처음 컴포넌트가 랜더링 됐을때 실행됩니다. 만약에 빈배열을 받아온다면 처음 컴포넌트가 생성 됐을 때만 실행하는 함수 입니다.

컴포넌트의 특정 값이 변경 되었을 때 컴포넌트를 업데이트하고, 처음 생성 됐을 때를 처리하는데 마지막 과정이 빠졌습니다.

맞습니다. componentWillUnmount을 처리하기 위해 Clean Up과정을 처리해야합니다.

예를 들면 타이머를 동작했다면, 타이머를 종료하는 코드를 작성해야 합니다.

useEffect(() => {

	rreturn () => {}

}, [])

함수 안에서 함수를 리턴해 주면 해당 함수를 실행하며 종료를 합니다.

정말 놀랍게 4가지의 메서드를 하나의 형태로 정리가 가능해졌습니다.

예시 코드를 보며 어떻게 쓰이는지 확인해봅시다!

예시코드

import React, {useState, useEffect} from 'react'
import Timer './TImer'
const App = () => {
	const [showTimer, setshowTimer] = useState(false)
	return (
    	<div>
        	{showTimer && <Timer/>}
        	<button onClick=(() => setShowTimer(!showTimer))>toggle</button>
        </div>
    )

}

export default App
---------------------
export default const Timer = () => {
	usseEffect(() => {
    	
    	const timer = setInterval(() => {
        	console.log('타이머가 작동 중')
        }, 1000)
        
        // clean up
        return () => {
        	clearInterval(timer)
        }
    }, [])
    // 처음 컴포넌트가 랜더링 되었을 때만
}

주의사항

useEffect는 정말 필요한 함수이다. 하지만 많이 사용하면 랜더링 시 컴포넌트 최적화에 무리를 주게 된다.

자식 컴포넌트에 useEffect가 있을 경우 부모 컴포넌트가 다시 랜더링 될 때 계속 호출 되기 때문이다. 그래서 최적화를 위해 리액트 구조를 present, container 구조로 적절하게 짜야한다.

이 부분이 가장 어렵다. 솔직하게 아직 리액트 패턴에 대한 이해가 많이 부족하다. useEffect 대신 useCallback을 사용해서 리랜더링을 막을 수 있지만 이거는 주먹구구식에 불과하다.

최적화를 위해 useEffect를 줄이는게 아니라, 얼마나 좋은 패턴을 가진 리액트 앱을 만들었는지 집중해야 한다.

얼른 리액트 패턴 공부하자!!

profile
개발, 정복
post-custom-banner

1개의 댓글

comment-user-thumbnail
2022년 4월 16일

마운트 단계까지는 몰랐는데..! 좋은 정보 감사합니다^^

답글 달기