Library & Framework/React.js

[React.js] 리액트 useEffect 기본 사용

개발하는 사막여우 2021. 10. 14. 21:58
반응형

useEffect는 리액트의 함수형 컴포넌트에서 컴포넌트의 마운트 시(생성시), 마운트 해제 시(삭제시), 업데이트 시특정 로직을 실행될 수 있도록 하는데 사용한다. 클래스형 컴포넌트의 componentDidMount, componentWillUnmount, componentDidUpdate를 한번에 사용가능하도록 해주는 것이다.

useEffect의 경우 다음과 같은 형태로 구현된다.

useEffect(() => { 내부 로직 return () => {cleanup 함수} }, deps)


내부 로직은 말 그대로 조건에 맞는 상황에서 실행될 로직을 의미하며, return의 경우 cleanup 함수이다. deps의 경우 useEffect가 실행되는 기준이 될 값들의 배열을 의미한다. 조건문을 사용하여 좀 더 단순하게 설명하면 아래와 같다.

1. deps가 빈 배열일 경우( [] )
a) 내부 로직: 컴포넌트가 처음 생성될 때만 실행됨(componentDidMount)
b) cleanup 함수: 컴포넌트가 삭제되기 직전에만 실행됨(componentWillUnmount)
2. deps내부에 값이 있을 경우( [value] )
a) 내부 로직: 컴포넌트 생성 시 + value 값이 업데이트 된 직후 실행됨(componentDidMount + componentDidUpdate)
b) cleanup 함수: 컴포넌트 삭제 시 + value 값이 업데이트 되기 직전 실행됨(componentWillUnmount + componentWillUpdate)
3. deps 자체를 생략했을 경우
a) 내부 로직: 컴포넌트가 (재)렌더링 된 직후 항상 실행됨(after rendering)
b) cleanup 함수: 컴포넌트가 (재)렌더링 되기 직전 항상 실행됨(before rendering)


위와 같이 useEffect의 경우 deps에 따라 내부 로직과 cleanup 함수가 작용하는 시기가 다르다. 예제를 통해 설명해보면 다음과 같다.

import React, { useRef, useState, useEffect } from 'react'; function App() { const [people, setPeople] = useState([ { name: 'kim' }, { name: 'lee' }, { name: 'tester' }, ]); const input = useRef(); const addPerson = () => { setPeople([...people, { name: input.current.value }]) input.current.value = ''; console.log('--------------\n', '새로운 사람이 나타났다!'); } const deletePerson = name => { setPeople(people.filter(person => person.name !== name)); console.log(name, '은 사망하였습니다...'); } const makeNameLonger = name => { setPeople(people.map(person => name === person.name ? { name: person.name + person.name[person.name.length - 1] } : person)); console.log('--------------\n', name, '의 이름이 길어졌다!'); } return ( <> <div> <input ref={input} /> <button onClick={addPerson}>등록</button> </div> <div> {people.map((person, idx) => <Person name={person.name} key={idx} makeNameLonger={makeNameLonger} deletePerson={deletePerson} />)} </div> </> ) } function Person({ name, makeNameLonger, deletePerson }) { // 1. deps = [] useEffect(() => { console.log(name, '마운트 됨!'); return () => { console.log(name, '마운트 해제됨!'); } }, []) // 2. deps = [name] useEffect(() => { console.log(name, '마운트 or 업데이트 됨!'); return () => { console.log(name, '마운트 해제 or 업데이트 직전!'); } }, [name]) // 3. deps = undefined useEffect(() => { console.log(name, '(재)렌더링 됨!'); return () => { console.log(name, '(재)렌더링 직전!'); } }) return ( <div> <b>내 이름은 {name}</b> <button onClick={() => makeNameLonger(name)}>이름 늘리기</button> <button onClick={() => deletePerson(name)}>삭제</button> </div> ); } export default App;

people 객체에 있는 모든 사람 이름이 Person 컴포넌트를 통해 웹에 전부 나타나고, 이름을 늘리거나, 추가, 삭제할 수 있는 간단한 코드이다. 이번에 중요한 부분은, Person 컴포넌트 내부의 3개의 useEffect()이다. 위에서 설명한 deps값에 따른 분기를 표현해주기 위해 3개의 useEffect()를 구현해주었다. 이제 위에서 설명한 경우에 따라 어떻게 useEffect가 동작했는지 확인해보겠다. (원래 useEffect() 내부에서 사용해주는 prop은 항상 deps 안에 속해있는 것이 권장된다고 하나, 예시를 위해 임의로 name값을 모든 useEffect() 함수에 넣어주었다.)

1. 최초 실행시

최초 실행시

최초 실행시, 3개의 Person 컴포넌트들이 모두 마운트 된다. 그에 따라 각각 1,2,3번 경우의 useEffect()가 모두 실행된다.

2. 새로운 사람(컴포넌트) 추가시

new가 나타났다!

새로운 사람이 추가되면서, App 전체가 재렌더링이 이루어지고, 그에 따라 기존에 있던 모든 Person 컴포넌트들이 다 재렌더링된다. 이 경우, 각 Person은 재렌더링에 해당하는 useEffect()인 3번 useEffect()만 실행된다. 새로 추가된 new의 경우, 당연히 3가지 useEffect()가 모두 실행된다.

3. 새로운 사람(컴포넌트) 삭제시

new는 떠났습니다..ㅠㅠ

new가 마운트 해제 되면서, new의 1,2 번 useEffect()의 cleanup 함수가 실행되고, App의 재렌더링 직전이 되어 모든 컴포넌트의 3번 useEffect()의 cleanup 함수가 실행되었다. App의 재렌더링 이후엔, new는 없어졌기 때문에 원래 3명의 사람(컴포넌트) 만 재렌더링 되었다.

4. 특정 컴포넌트 값 변경시(kim의 이름을 늘렸을 때)

킴 -&gt; 킴ㅁ

kim의 name값이 업데이트 되면서, kim의 2번 useEffect()의 cleanup이 먼저 실행되었고, 전부 다시 렌더링 된 이후 2번 useEffect()의 내부 로직이 다시 실행되었다.

반응형