💻 Frontend

4. React Hook, useEffect

category
💻 Frontend

useEffect

useEffect(setup, dependencies?)

문법

컴포넌트 최상위 레벨에서 useEffect를 호출하여 Effect를 선언합니다:
import { useEffect } from 'react'; import { createConnection } from './chat.js'; function ChatRoom({ roomId }) { const [serverUrl, setServerUrl] = useState('https://localhost:1234'); useEffect(() => { const connection = createConnection(serverUrl, roomId); connection.connect(); // cleanup 함수 return () => { connection.disconnect(); }; }, [serverUrl, roomId]); // ... }

파라미터

  • setup: Effect 로직이 포함된 함수. cleanup return 함수는 선택적으로 반환할 수 있습니다. 컴포넌트가 DOM에 추가되면 React가 setup 함수를 실행합니다. 변경된 dependencies를 다시 렌더링할 때마다, React는 먼저 cleanup 함수(해당 함수를 선언한 경우)를 실행한 다음 새로운 values로 setup 함수를 실행합니다. 구성요소가 DOM에서 제거된 후, React는 cleanup 함수를 실행합니다.
  • dependencies(optional): setup 코드 내에서 참조되는 모든 reactive values의 목록. Reactive values에는 컴포넌트 내에서 직접 선언된 props, state, 모든 변수와 함수가 포함됩니다. 만약 린트(ESlint)가 포함된 경우 모든 reactive value값이 dependency에 올바르게 지정되었는지 검사합니다. dependencies list에는 항목 수가 일정해야 하며 [dep1, dep2, dep3] 과 같이 인라인으로 작성해야 합니다. React는 Object.is비교를 사용하여 각 dependencies를 이전 값과 비교합니다.
    • 만약 dependencies를 생략한다면 컴포넌트가 다시 렌더링할 때마다 Effect가 다시 실행됩니다. 컴포넌트가 다시 실행되는 경우는 첫 렌더링시 이기 때문에 dependencies를 생략된 경우는 첫 렌더링 시에만 실행됩니다.

반환값

useEffectundefined를 반환합니다.

주의 사항

  • useEffect는 Hook이므로 컴포넌트 또는 자체 Hooks의 최상위 레벨에서만 호출할 수 있습니다. 또한 조건문이나 루프문에서 호출할 수 없습니다. 필요한 경우 새 컴포넌트를 추출하고 해당 컴포넌트로 state를 이동합니다.
  • 외부 시스템과 동기화하려는 것이 아니라면 아마 Effect가 필요 없을 것입니다.
  • Strict Mode가 켜져있으면 React는 실제 setup 함수 전에 개발 전용 setup + cleanup 주기를 실행합니다. 이것은 cleanup 로직이 setup logic을 “반사”하고, setup이 수행하는 모든 작업을 중지하거나 취소하는지 확인하는 stress-test입니다. 이로 인해 문제가 발생한다면 cleanup 함수를 구현하세요.
  • dependencies 일부가 컴포넌트 내부에 정의된 개체 또는 함수인 경우 필요 이상으로 Effect가 다시 실행될 위험이 있습니다. 이러한 문제를 해결하려면 dependencies에 불필요한 개체 및 함수를 제거하면 됩니다. Effect 외부에서 state 업데이트 및 non-reactive 로직을 호출할 수 있습니다.
  • Effect가 상호작용에 의해 발생하지 않은 경우 React는 브라우저가 Effect를 실행하기 전에 업데이트된 화면을 먼저 보여지게 하도록 합니다. Effect가 시각적인 작업을 수행하고 지연이 눈에 띄게 나타나는 경우(ex. 깜박임) useEffect 대신 useLayoutEffect를 사용해보세요.
  • Effect가 상호작용에 의해 발생한 경우에도 브라우저는 Effect 내에서 state update를 처리하기 전에 화면을 다시 보여지게 할 수도 있습니다. 일반적으로 올바른 동작이지만, 브라우저가 화면을 다시 그리는 것을 차단해야 하는 경우 useEffectuseLayoutEffect로 바꿔야 합니다.
  • Effect는 client에서만 실행됩니다. 서버 렌더링 중에는 실행되지 않습니다.
 

사용법

1. 외부 시스템에 연결

일부 컴포넌트는 페이지에 표시되는 동안 네트워크, 일부 브라우저 API(storage, cookie…)또는 외부 라이브러리에 연결된 상태를 유지해야합니다. 이러한 시스템은 React에 의해 제어되지 않으므로 외부(external)라고 칭합니다.
컴포넌트를 위에서 말한 외부 시스템에 연결하려면 컴포넌트 최상위 레벨에서 useEffect를 호출하세요.
import { useEffect } from 'react'; import { createConnection } from './chat.js'; function ChatRoom({ roomId }) { const [serverUrl, setServerUrl] = useState('https://localhost:1234'); useEffect(() => { const connection = createConnection(serverUrl, roomId); connection.connect(); return () => { connection.disconnect(); }; }, [serverUrl, roomId]); // ... }
useEffect에 두 개의 인수를 전달해야 합니다:
  1. 해당 시스템에 연결하는 setup 함수:
      • 해당 시스템에서 연결을 끊는 코드가 있는 경우 cleanup 함수를 반환해야 합니다.
  1. 해당 함수 내에서 사용되는 컴포넌트의 모든 값을 포함하는 dependencies 목록입니다.
React는 필요할 때마다 setupcleanup 기능을 호출하며 이는 여러 번 발생할 수 있습니다.
  1. 컴포넌트가 페이지에 추가될 때(mounts) setup 코드가 실행됩니다.
  1. dependencies가 번경될때마다 컴포넌트를 다시 렌더링합니다.
      • 먼저, cleanup 코드가 이전 props 및 state로 실행됩니다.
      • 그런 다음 setup 코드가 새로은 props 및 state로 실행됩니다.
  1. 컴포넌트가 페이지에서 제거(unmounts)될 때 마지막으로 cleanup code가 한번 실행됩니다.
 
위의 예시에서 순서를 설명하겠습니다.
  1. ChatRoom 컴포넌트가 페이지에 추가되면 초기 serverUrlroomId로 채팅방에 연결됩니다.
  1. 다시 렌더링의 결과로 serverUrl 또는 roomId가 변경되면(ex. 사용자가 다른 대화방을 선택한 경우) Effect는 이전 ChatRoom에서 연결이 끊어지고, 다음 ChatRoom에서 연결됩니다.
  1. ChatRoom 컴포넌트가 페이지에서 제거되면 Effect는 마지막으로 연결을 끊습니다.
 
개발단계에서는 버그를 찾는 데 도움이 되도록 React는 setup전에 setup 및 cleanup 코드를 한 번 더 실행합니다. 이는 Effect의 로직이 올바르게 구현되었는지 확인하는 stress-test입니다. 이로 인해 눈에 보이는 문제가 발생하면 cleanup 함수에 일부 logic이 누락된 것입니다. cleanup기능은 setup기능이 수행하는 모든 작업을 중지하거나 실행을 취소해야 합니다. 일반적으로 사용자는 한 번 호출되는 setupcleanupsetup 순서를 구분할 수 없어야 합니다.
 
모든 Effect를 독립적인 프로세스로 작성하고 한 번에 단일 setup/cleanup에 대해 생각해보세요. 컴포넌트가 마운트, 업데이트, 언마운트(마운트 해제) 여부는 중요하지 않습니다. cleanup 로직이 올바르게 setup 로직을 “미러링”하면, Effect는 필요할 때마다 setupcleanup을 자주 실행할 수 있습니다.
 

Note

Effect를 사용하면 컴포넌트를 일부 외부 시스템과 동기화할 수 있습니다. 외부 시스템은 다음과 같이 React에 의해 제어되지 않는 모든 코드를 의미합니다.
  • setInterval()clearInterval()로 관리되는 타이머
  • window.addEventListener()window.removeEventListener()를 사용하는 이벤트
  • animation.start()animation.reset()과 같은 API가 있는 타사 애니메이션 라이브러리
외부 시스템에 연결하지 않는 경우 Effect가 필요하지 않을 수 있습니다.
 

 

2. 커스텀 hooks을 감싸는 Effect

Effect는 “탈출구”입니다: “React 외부로 이동”해야할 때와 사용사례에 적합한 내장 솔루션이 없을 때 사용합니다. Effect를 수동으로 작성해야하는 경우가 많다면, 일반적으로 컴포넌트가 의존하는 동작을 위해 일부 커스텀 hook으로 추출해야 한다는 신호입니다.
예를 들어, 이 useChatRoom 커스텀 hooks는 보다 선언적인 API 뒤에서 Effet의 로직을 “숨깁니다”.
function useChatRoom({ serverUrl, roomId }) { useEffect(() => { const options = { serverUrl: serverUrl, roomId: roomId }; const connection = createConnection(options); connection.connect(); return () => connection.disconnect(); }, [roomId, serverUrl]); }
그런 다음 컴포넌트에서 다음과 같이 사용할 수 있습니다.
function ChatRoom({ roomId }) { const [serverUrl, setServerUrl] = useState('https://localhost:1234'); useChatRoom({ roomId: roomId, serverUrl: serverUrl }); // ...
 

마무리

react 기본기를 다시 공부하려고 여러 블로그를 보았지만 react 공식문서가 가장 잘 나와있는 것 같다. 이제부터 공식문서를 번역하면서 개념을 공부하는 습관을 들이려고 한다!

References