React 공식 문서 해석하며 공부하기 : Using the Effect Hook

배지로·2021년 9월 13일
0
post-thumbnail

해석하며 공부하는 것을 목적으로 하기 때문에 다수의 의역, 오역이 있음을 미리 밝힙니다.
원본 : https://reactjs.org/docs/hooks-effect.html

Effect Hook 사용하기

Hooks는 리액트 16.8 버전에 새롭게 추가되었습니다. 이로 인해 클래스를 사용하지 않고도 state나 다른 리액트 기능들을 사용할 수 있게 되었죠.

Effect Hook은 함수 컴포넌트에서 side effect를 수행하도록 합니다.

import React, {useState,useEffect} from 'react';

function Example(){
  const [count,setCount]=useState(0);
  
  //componentDidMount와 componentDidUpdate와 유사함
  useEffect(()=>{
    //브라우저 API를 사용하여 문서의 제목을 업데이트함
    document.title=`You clicked ${count} times`;
  });
  
  return(
    <div>
      <p>You clicked {count} times</p>
      <button onClick={()=>setCount(count+1)}>
        click me
      </button>
    </div>
  );
}

이전 페이지의 counter 예시에 기반한 스니펫이지만 새로운 기능이 추가되었습니다: 클릭 수를 포함한 커스텀 메세지에 문서의 제목을 설정하였습니다.

데이터 가져오기, 구독 설정하기, 리액트 컴포넌트 DOM을 직접 바꾸는 것, 이 모든 것이 side effect의 예시입니다. 이러한 동작들을 "side effects"(혹은 "effects")이라고 부르는지 여부와 관계없이, 이전에 컴포넌트에서 수행해본 적 있을 것입니다.

Tip
리액트 클래스 생명주기 메소드에 친숙하다면, useEffect Hook을 componentDidMount,componentDidUpdate, 그리고 componentWillUnmount가 합쳐진 것이라고 생각하면 됩니다.

리액트 컴포넌트에 2가지 종류의 일반적인 side effect가 있습니다: 정리(cleanup)가 필요한 것과 그렇지 않은 것. 둘의 차이를 더 자세히 알아봅시다.


정리(Cleanup)가 필요하지 않은 Effects

때때로, 리액트가 DOM을 업데이트한 이후에 추가적인 코드를 실행하기를 원하는 경우가 있습니다. 네트워크 요청이나 직접 DOM을 조작해야 할 때, 그리고 로깅 과정은 정리 과정을 필요로 하지 않는 effect 예시입니다. 이것들은 실행시킨 이후에 신경 쓸 필요가 없기 때문에 정리과정을 필요로 하지 않습니다. 어떻게 클래스와 Hooks가 이러한 side effect들을 구현하도록 하는지 비교해봅시다.

클래스를 사용한 예시

리액트 클래스 컴포넌트에서는 render 메소드가 side effect를 직접 발생시키지 못합니다. 리액트가 DOM을 업데이트한 이후에 effect가 수행되기 때문에 render함수에서 구현하는 것은 너무 이릅니다.

이것이 리액트 클래스에서 componentDidMountcomponentDidUpdate에서 side-effect를 구현하는지에 대한 이유입니다. 예시로 돌아와서, 여기 리액트 counter 클래스 컴포넌트가 있습니다. 리액트가 DOM을 조작한 직후 문서의 제목을 업데이트한 상태입니다:

class Example extends React.Component{
  constructor(props){
    super(props);
    this.state={
      count:0
    };
  }
  
  componentDidMount(){
    document.title=`You clicked ${this.state.count} times`;
  }
  componentDidUpdate(){
    document.title=`You clicked ${this.state.count} times`;
  }
  
  render(){
    return(
      <div>
      <p>You clicked {this.state.count} times</p>
      <button onClick={()=>this.setState({count:this.state.count+1})}>
        click me
      </button>
    </div>
    );
  }
}

클래스에서 두 개의 생명주기 메소드에 코드가 중복되는 부분을 살펴봅시다.

코드가 중복될 수밖에 없는 이유는 컴포넌트가 마운트만 된 상태인건지, 혹은 업데이트된 상황인 건지에 관계없이 모든 상황에서 같은 side effect가 수행되어야 하기 때문입니다. 개념상으로는, 렌더링 이후에는 항상 코드가 실행되었으면 하지만, 리액트 클래스 컴포넌트는 그런 기능을 하는 메소드가 없습니다. 코드를 별개의 메소드로 추출한다고 하더라도, 여전히 두 장소 모두에서 호출해야만 합니다.

이제, useEffect Hook을 이용해서 똑같이 실행해봅시다.

Hooks를 사용한 예시

이미 페이지 상단에서 이 예제를 확인하셨을 거지만, 자세히 한 번 더 살펴봅시다:

import React, { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

useEffect가 어떤 것을 하나요? 이 Hook을 사용함으로서, 컴포넌트가 렌더링 이후에 수행할 것이 있다고 리액트에게 전달할 수 있습니다. 리액트는 당신이 전달한 함수를 기억하며(이 함수는 "effect"라고 불리죠), DOM 업데이트를 수행한 이후에 그 함수를 수행합니다. 이 effect에서는, 문서 제목을 설정할 뿐만 아니라 데이터 불러오기나 또다른 중요한 API를 호출하는 것 또한 수행하는 것도 가능합니다.

왜 useEffect는 컴포넌트 안에서 호출되어야 하나요? useHook을 컴포넌트 안에 위치하게 하는 것은 count state 변수(혹은 props)가 effect에 직접 접근할 수 있게 합니다. state가 이미 함수 스코프 안에 있기 때문에 그것을 읽어올 특별한 API가 필요하지 않죠. Hooks는 자바스크립트의 클로저를 수용하지만 자바스크립트가 해결책이 될 수 있는 리액트의 몇몇 특정한 API 도입은 피합니다.

렌더링 후에 매번 useEffect가 실행되나요? 네! 기본적으로, 첫번째 렌더링 이후와 업데이트 이후 매번 실행됩니다. (이것을 어떻게 커스터마이징하는지에 대해서는 조금 있다가 얘기해보도록 합시다.) "마운트"과 "업데이트"라는 단어를 떠올리기보다는, "렌더링 이후에" effect가 실행된다고 생각하면 조금 더 이해하기 쉽겠네요. 리액트는 effect가 실행되기 전까지 DOM이 업데이트되었음을 보장합니다.

자세한 설명

이제 effects에 대해서 알았으니, 이 코드라인이 이해가 되겠네요:

function Example(){
  const [count,setCount]=useState(0);
  
  useEffect(()=>{
    document.title=`You clicked ${count} times`;
  });
}

count state 변수를 선언하고 나서, 리액트에게 effect를 사용해야 한다고 리액트에게 전달해야 합니다. useEffect Hook에 함수를 전달합니다. 전달한 함수는 effect입니다. effect 안에서 브라우저 API인 document.title 을 이용하여 문서 제목을 설정합니다. 같은 함수 컴포넌트 스코프 안에 있기 때문에 effect 안에서 가장 최신의 count를 읽을 수 있습니다. 리액트가 컴포넌트를 렌더링할 때, 우리가 사용한 effect를 기억하여 DOM을 업데이트한 이후에 실행시킵니다. 이것은 첫번째 렌더링을 포함해서 매번 렌더링이 일어날 때마다 발생합니다.

숙련된 자바스크립트 개발자라면 useEffect에 전달된 함수는 매 렌더링때마다 다르다는 것을 눈치채셨을 것 같은데요. 이것은 의도된 것입니다. count값이 제대로 업데이트 되는지에 대한 걱정 없이 effect 내부에서 count 값을 읽을 수 있게 합니다. 다시 렌더링을 할 때마다, 이전의 것을 대체하는 다른 effect를 전달합니다. 이 방법대로 하면, 렌더링의 결과로서 effect가 행동할 수 있게 하며, 각 effect는 특정 렌더링에 "포함됩니다". 왜 이것이 유용한지 좀 더 명확히 이 페이지의 후반부에서 살펴봅시다.

Tip
componentDidMountcomponentDidUpdate와 다르게, useEffect에서 사용되는 effect는 브라우저가 화면을 업데이트하는 것을 막지 않습니다. 이것은 app이 더 반응적으로 보이게 합니다. effect의 대부분은 동기적으로 작동할 필요가 없습니다. 레이아웃을 측정하는 것과 같은 흔하지 않은 상황에서는 useEffect와 동일한 기능을 하는 API인 별도의 Hook, useLayoutEffect을 대신 사용합니다.


정리(Cleanup)를 하는 Effect

바로 전에 정리를 필요로 하지 않는 side effect의 구현 방법에 대해 살펴봤습니다. 하지만 몇몇의 effect는 정리를 합니다. 예를 들어, 외부 데이터 소스에 대해 구독을 설정하고 싶을 때 사용합니다. 이러한 경우, 메모리 낭비를 막기 위해서 정리를 하는 것은 매우 중요합니다. 어떻게 구현하는지 클래스와 훅을 통해 비교하며 알아봅시다.

클래스를 사용한 예시

리액트 클래스에서는 전형적으로 componentDidMount에서 구독을 설정하고, componentWillUnmount에서 정리를 합니다. 예를 들어, 친구의 온라인 상태를 구독할 수 있는 ChatAPI 모듈을 사용해봅시다. 아래 예시에서 구독을 어떻게 하는지, 클래스를 사용하여 상태를 어떻게 보여주는지 볼 수 있습니다:

class FriendStatus extends React.Component{
  constructor(props){
    super(props);
    this.state={isOnlie:null};
    this.handleStatusChange=this.handleStatusChange.bind(this);
  }
  
  componentDidMount(){
    ChatAPI.subscribeToFriendStatus(
      this.props.friend.id,
      this.handleStatusChange
    );
  }
  
  componentWillUnmount(){
    ChatAPI.unsubscribeFromFriendStatus(
      this.props.friend.id,
      this.handleStatusChange
    );
  }
  
  handleStatusChange(status){
    this.setState({
      isOnline:status.isOnline
    });
  }
  
  render(){
    if(this.state.isOnline===null){
      return 'Loading...';
    }
    return this.state.isOnline?'Online':'Offline';
  }
}

componentDidMount와 componentWillUnmout가 어떻게 대칭을 이루고 있는지 보세요. 생명주기 메소드는 개념적으로 두 메소드 안의 코드가 같은 effect와 관련되어 있음에도 로직을 나눌 수밖에 없습니다.

참고
예리한 독자는 이 예시가 componentDidUpdate 메소드 또한 필요할 것이라고 예상하는데 정확합니다. 지금은 일단 무시하고 지나가지만 페이지의 다음 섹션에서는 돌아와서 살펴볼 것입니다.

Hooks를 사용한 예시

Hooks를 사용하여 컴포넌트를 작성하는 방법을 알아봅시다.

정리를 하기 위해서 분리된 effect가 필요하다고 생각하실 수 있습니다. 그러나 구독을 하거나 취소하는 코드는 결합도가 너무 높기 때문에 useEffect가 그 기능들을 함께 다루도록 구현되었습니다. effect가 함수를 반환한다면, 리액트는 정리할 타이밍에 그 함수를 작동시킬 것입니다:

import React, { useState, useEffect } from 'react';

function FriendStatus(props){
  const [isOnline, setIsOnline]=useState(null);
  
  useEffect(()=>{
    function handleStatusChange(status){
      setIsOnline(status.isOnline);
    }
    
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    
    //이 effect 이후에 어떻게 정리할지 명시:
    return function cleanup(){
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id,handleStatusChange);
    };
  });
  
  if(isOnline===null){
    return 'Loading...';
  }
  return isOnline?'Online':'Offline';
}

왜 effect에서 함수를 반환하도록 하나요? 이것은 effect를 위한 선택적 정리 방법입니다. 모든 effect는 정리를 위한 함수를 반환할 수 있습니다. 이것은 서로 밀접한 관련이 있는 구독 기능과 구독 취소 기능의 로직을 유지할 수 있게 해줍니다. 그 기능들은 같은 effect의 일부분입니다.

리액트가 effect를 정리하는 정확한 시점이 언제인가요? 리액트는 컴포넌트가 unmount될 때 정리를 합니다. 그러나, 우리가 이전에 배운대로, effect는 모든 렌더링마다 실행되고, 한 번으로 끝나지 않죠. 이것은 리액트가 다음에 effect를 실행하기 전에 이전의 렌더링에서의 effect 또한 정리해줘야한다는 의미입니다. 왜 이것이 버그 발생을 피하게 도와주는지,, 성능 문제가 발생할 경우에 이 행동에서 벗어나게 하는 방법에 대해서는 이후에 이야기해 볼 것입니다.

참고
effect에서 이름이 있는 함수를 반환할 필요는 없습니다. 우리는 그 함수의 목적을 명확하게 하기 위해서 cleanup이라고 불렀지만, 화살표 함수나 혹은 다른 방법으로 호출하는 것도 가능합니다.


요약

컴포넌트 렌더링 이후에 여러 종류들의 side effects를 구현할 수 있게 해주는 useEffect를 지금까지 배워봤습니다. 몇몇 effect들은 정리를 요구할 것이고 그것들은 함수를 반환할 것 입니다:

useEffect(()=>{
  function handleStatusChange(status){
    setIsOnline(status.isOnline);
  }
  
  ChatAPI.subscribeToFriendStatus(props.friend.id.handleStatusChange);
  return ()=>{
    ChatAPI.unsubscribeFromFriendStatus(props.friend.id.handleStatusChange);
  };
});

다른 effect들은 정리 단계를 포함하지 않을 수 있으며, 이런 경우에는 아무것도 반환하지 않습니다.

useEffect(()=>{
  document.title=`You clicked ${count} times`;
});

Effect Hook은 모든 경우들을 하나의 API로 통합합니다.


Effect Hook이 어떻게 작동하는지 충분히 파악했거나, 내용이 어렵다고 느끼신다면, Hooks의 규칙에 대한 내용을 다루는 다음 페이지로 넘어가셔도 좋습니다.



Effect를 사용하는 데 필요한 팁

리액트 사용자들이 궁금해 할 만한 useEffect에 대한 깊이있는 관점을 계속해서 살펴보도록 하겠습니다. 그것들을 파헤치는 것을 의무로 느낄 필요는 없어요. Effect Hook에 대한 세부적인 설명을 보고 싶으시다면 언제든지 이 페이지로 돌아와서 살펴보실 수 있습니다.

팁: 관심사를 구분하기 위해 여러 개의 Effect 사용하기

Hooks가 탄생한 동기가 된 문제들 중 한가지가 있었습니다. 바로 클래스 생명주기 메소드는 관련 없는 로직을 포함하지만 관련된 로직은 여러 메소드로 쪼개진다는 것이었습니다. 이전 예시에서 보았던 counter와 친구의 상태 지표 로직을 합친 컴포넌트를 보겠습니다:

class FriendStatusWithCounter extneds React.Component{
  constructor(props){
    super(props);
    this.state={ count: 0, isOnline:null };
    this.handleStatusChange=this.handleStatusChange.bind(this);
  }
  
  componentDidMount(){
    document.title=`You clicked ${this.state.count} times`;
    ChatAPI.subscribeToFriendStatus(
      this.props.friend.id, 
      this.handleStatusChange
    );
  }
  
  componentDidUpdate(){
    document.title=`You clicked ${this.state.count} times`;
  }
  
  componentWillUnmount(){
    ChatAPI.unsubscribeFromFriendStatus(
      this.props.friend.id, 
      this.handleStatusChange
    );
  }
  
  handleStatusChange(status){
    this.setState({
      isOnline:status.isOnline
    });
  }
  //...

document.title가 설정된 로직이 componentDidMountcomponentDidUpdate에서 쪼개져서 어떻게 작성되었는지 살펴봅시다. 구독 로직 또한 componentDidMountcomponentWillUnmount에 분산되어 작성되었죠. 그리고 compoentDidMount는 두 가지 일에 대한 코드를 모두 포함합니다.

그래서, Hooks는 어떻게 이 문제를 해결할 수 있을까요? State Hook을 한 번 이상 사용하는 것처럼, effects 또한 여러 번 사용할 수 있습니다. 이것은 관련없는 로직을 다른 종류의 effects로 분리할 수 있도록 합니다:

function FriendStatusWithCounter(props){
  const [count,setCount]=useState(0);
  useEffect(()=>{
    document.title=`You clicked ${count} times`;
  });
  
  const [isOnline,SetIsOnline]=useState(null);
  useEffect(()=>{
    function handleStatusChange(status){
      setIsOnline(status.isOnline);
    }
    
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return()=>{
    	ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    }
  });
  //...
}

Hooks는 생명주기 메소드 이름보다는 코드가 무엇을 하는지에 기반해서 분리될 수 있게 합니다. 리액트는 컴포넌트에서 쓰이는 모든 effect를 지정된 순서에 맞춰 적용합니다.

설명 : 왜 Effect는 업데이트될 때마다 실행되어야 하나요?

만약 클래스에 익숙하시다면, 왜 effect 정리 단계는 언마운트될 때 한번 발생하는 것이 아니라 렌더링된 이후에 항상 발생하는지 궁금해하실 수 있습니다. 왜 이러한 디자인이 컴포넌트에 버그를 더 적게 만들도록 돕는지에 대한 예시를 살펴봅시다.

이 페이지의 앞쪽에서, 친구가 온라인 상태에 있는지 아닌지 표시해주는 FriendStatus 컴포넌트에 대한 예시를 살펴봤습니다. 해당 클래스는 this.props로부터 friend.id를 읽어오고, 컴포넌트가 마운트된 이후에 친구의 상태를 구독하고, 언마운트될 때 구독을 취소합니다:

componentDidMount(){
  ChatAPI.subscribeToFriendStatus(this.props.friend.id, this.handleStatusChange);
}

componentWillUnmount(){
  ChatAPI.unsubscribeFromFriendStatus(this.props.friend.id, this.handleStatusChange);
}

그러나, 컴포넌트가 화면 상에 보여지는 동안에 friend prop이 변화한다면 어떤 일이 발생할까요? 우리의 컴포넌트는 다른 친구의 온라인 상태를 계속해서 보여줄 것입니다. 이것은 버그입니다. 이것은 또한 언마운트될 때 구독 취소 요청이 잘못된 친구의 id를 사용하여 메모리 낭비나 충돌을 발생시킬 수도 있습니다.

클래스 컴포넌트에서는, 버그를 방지하기 위해서 componentDidUpdate를 추가해야 합니다:

componentDidMount(){
  ChatAPI.subscribeToFriendStatus(this.props.friend.id, this.handleStatusChange);
}

componentDidUpdate(prevProps){
  //이전 friend.id를 구독 취소
  ChatAPI.unsubscribeFromFriendStatus(prevProps.friend.id, this.handleStatusChange);
  
  //다음 friend.id를 구독
  ChatAPI.subscribeToFriendStatus(this.props.friend.id, this.handleStatusChange);
}

componentWillUnmount(){
  ChatAPI.unsubscribeFromFriendStatus(this.props.friend.id, this.handleStatusChange);
}

componentDidUpdate를 사용하는 것을 잊는 것은 리액트 어플리케이션에서 버그를 발생시키는 흔한 이유가 됩니다.

이제 Hooks를 사용하는 컴포넌트의 경우를 살펴봅시다:

function FriendStatus(props){
//...
  useEffect(()=>{
    //...
    
  	ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return()=>{
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    }
  });

버그로 고통받을 일은 없습니다.

useEffect는 기본적으로 업데이트에 관여하기 때문에 엡데이트를 위한 특별한 코드가 필요하지 않습니다. 다음 effect를 적용하기 전에 이전의 effect를 정리합니다. 이것을 설명하기 위해, 컴포넌트가 이 시간 동안 만들어내는 구독과 구독 취소 호출에 대한 일련의 나열이 있습니다:

//{friend:{id:100}} props로 마운트
ChatAPI.subscribeToFriendStatus(100, handleStatusChange);

//{friend:{id:200}} props로 업데이트
ChatAPI.unsubscribeFromFriendStatus(100, handleStatusChange); //이전 effect 정리
ChatAPI.subscribeToFriendStatus(200, handleStatusChange); //다음 effect 실행

//{friend:{id:300}} props로 업데이트
ChatAPI.unsubscribeFromFriendStatus(200, handleStatusChange); //이전 effect 정리
ChatAPI.subscribeToFriendStatus(300, handleStatusChange); //다음 effect 실행

//언마운트
ChatAPI.unsubscribeFromFriendStatus(300, handleStatusChange); //이전 effect 정리

이러한 행동은 기본적으로 일관성을 보장해주고, 업데이트 로직을 잊은 바람에 클래스 컴포넌트에서 흔하게 발생하는 버그를 방지할 수 있게합니다.

팁: Effect을 건너뛰어 성능 최적화하기

어떤 경우에서는, 렌더링 이후에 매번 effect를 정리하거나 적용하는 것이 성능 문제를 가져오기도 합니다. 클래스 컴포넌트에서는 componentDidUpdateprevPropsprevState를 비교를 통해서 문제를 해결할 수 있습니다:

componentDidUpdate(prevProps, prevState){
  if(prevState.count!==this.state.count){
    document.title=`You clicked ${this.state.count} times`;
  }
}

이러한 요구는 useEffect Hook API에서 너무 흔합니다. 렌더링 과정 사이에 값의 변화가 없었다면 effect를 적용하는 것을 건너뛰자고 리액트에게 전달할 수 있습니다. 이렇게 하려면, useEffect에 추가적인 두번째 변수로 배열을 전달해야만 합니다:

useEffect(()=>{
  document.title=`You clicked ${count} times`;
},[count]); //count가 변화했을 때만 effect를 실행

위의 예시에서, [count]를 두번째 매개변수로 전달합니다. 이것이 무슨 의미일까요? count가 5일 때, 다시 컴포넌트를 렌더링할 때 count가 여전히 5라면, 리액트는 다음 렌더링에서 [5]와 이전 렌더링의 [5]를 비교합니다. 배열의 모든 아이템이 같기 때문에(5===5), 리액트는 effect를 건너 뜁니다. 이것이 최적화 방식입니다.

count가 6으로 업데이트되었을 때 렌더링을 하면, 리액트는 이전 렌더링된 값 [5]를 다음 렌더링될 때의 값 [6]과 비교합니다. 이번에는 5!==6이기 때문에 effect를 다시 적용합니다. 만약 배열에 여러 개의 값이 있다면, 리액트는 그 중의 하나만 달라져도 effect를 다시 실행합니다.

이것은 정리 단계를 가진 effect에도 적용됩니다:

useEffect(()=>{
  function handleStatusChange(status){
    setIsOnline(status.isOnline);
  }
  ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
  return()=>{
    ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
  };
},[props.friend.id]); //props.friend.id가 변화할 때만 다시 구독

두번째 매개변수는 빌드 시 변환에 의해 자동적으로 추가될 수도 있습니다.

참고
위의 최적화 기술을 사용하신다면, 배열이 계속해서 변화하고 effect에 의해 사용되는 컴포넌트 스코프 안의 모든 값(props나 state와 같은)을 포함하도록 해야합니다. 그렇지 않으면, 당신의 코드는 이전의 렌더링 때의 값을 참고하게 될 것입니다. 함수를 다루는 방법배열에 변화가 너무 자주 일어날 때 해야하는 것에 대해서는 링크를 타고 가시면 더 배워볼 수 있습니다.
당신이 effect를 실행시키고 정리하는 것을 한 번만 하길 원한다면(마운트와 언마운트 시), 빈 배열([])을 두번째 매개변수로 전달하면 됩니다. 이렇게 하면 effect가 props나 state로부터 받은 어떤 값에도 의존하지 않아서 다시 실행할 필요가 없다는 것이 리액트에게 전달됩니다. 이것은 의존성 배열의 작동방법을 그대로 따라서 사용하는 것일 뿐이며 특별한 방법인 것은 아닙니다.
빈 배열을 전달한다면([]), effect 내의 props와 state는 항상 초기값을 가집니다. []을 두번째 인자로 전달하는 것이 componentDidMountcomponentWillUnmount 모델에 더 가깝지만, 너무 자주 effects를 다시 실행하는 것을 피하기 위한 더 나은 방법은 보통 존재합니다. 또한, 리액트가 useEffect를 실행시키는 것을 브라우저가 다 그려질때까지 연기하기 때문에 추가 작업은 큰 문제가 되지 않는다는 것은 잊지 마세요.
exhuastive-deps규칙을 eslint-plugin-react-hooks 패키지에 포함시켜서 사용하는 것을 추천드립니다. 이것은 의존성이 잘못 명시되었을 때 경고해주고, 고치는 방법을 제안해줍니다.


다음 단계

축하합니다! 매우 긴 페이지였지만, effect에 대한 대부분의 질문이 마지막에는 대답되었길 바랍니다. State Hook과 Effect Hook 모두에 대해서 배워왔고, 그것들을 모두 결합하면 많은 것을 할 수 있습니다. 클래스에 대한 많은 예시들도 다뤄봤죠. 클래스가 사용되지 않는 곳에서는 추가적인 Hooks가 도움이 될 수 있습니다.

Hooks가 문제를 해결하는 방법을 Hooks를 배우는 동기에서 개략적으로 살펴보면서 시작했죠. componentDidUpdatecomponentWillUnmount에서 effect 정리가 중복을 피하는 방법, 관련된 코드가 함께 있는 방법, 버그를 피하게 하는 방법을 배워봤습니다. 목적에 따라 effects를 분리하는 방법에 대해서도 알아봤습니다. 이는 클래스에서는 구현할 수 없는 방법이었죠.

이 지점에서, Hooks는 어떻게 동작하는지에 대해서 질문하실 수 있을 것 같습니다. 리액트는 어떻게 어떤 useState 호출이 렌더링 과정 사이에서 어떤 state 변수와 일치하는지 알 수 있나요? 리액트는 어떻게 업데이트마다 이전과 이후의 effect가 "일치"하는지 알 수 있나요? 다음 페이지에서 Hooks를 작동시키는 데 중요한 Hooks의 규칙들에 대해서 알아볼 예정이고,

profile
웹 프론트엔드 새싹🌱

0개의 댓글