Westudy.log 10 - useEffect

Jakesjk·2022년 12월 25일
0

Dev.Tech.Log

목록 보기
16/26
post-thumbnail

Side Effect, useEffect


Side Effect

SideEffect? 부작용? = 부수효과 : 주요한 효과에 따라서 발생하는 효과

프로그래밍에서는 코드가 의도한 주된 효과 외에 추가적으로 발생하는 효과라 정의할 수 있다.
프로그맹에서 함수가 하고자하는 본질적인 역할(Input값을 받아서 Output값을 산출하는 것)외의 모든 행위들을 의미한다.

SideEffect 예시
//SideEffect 없음
const sum = (x) => {
  return x + 1;
};

//SideEffect 있음 - 외부 값에 의한 SideEffect
let num;

const sum = (x) => {
  num = x + 1;
};

//SideEffect 있음2 - DOM조작에 의한 SideEffect
const printNum = (x) => {
  console.log(x);
};

const changeTitle = (newTitle) => {
  const title = document.getElementById('title');

  title.innerText = newTitle;
};

즉, Sideffect는 input값을 받아 output을 산출하는 과정에서

  • 외부 값을 읽어오는 행위
  • 외부 값을 변경하는 행위

를 의미한다.

React에서 SideEffect

React에서 rendering이란 Props,State를 기반으로 UI를 그려내는 행위이다.
함수 컴포넌트에서 { input - Props,State }, { output - JSX }

예시
  • DataFetching
    프론트엔드가 백엔드API를 통해서 저장된 데이터를 가져오는 행위
  • DOM 접근 및 조작
  • 구독(Subscribe)
    프로그래밍에서 구독이란 어떤 것의 변화를 계속해서 지켜보고 변화가 발생하면 특정 액션을 취하는 것을 말한다. 프로그래밍에서 시간을 구독한다고 생각하면, 시간의 변화에 따라 원하는 특정액션을 이행할 수 있음에 대해 생각해볼 수 있다.

useEffect

React에서 올바른 SideEffect 발생 시점
const App = () => {
  return <h1>Hello World</h1>;
};

const App = () => {
  const doSideEffect = () => {
    // do some side effect
  };

  doSideEffect();

  return <h1>Hello World</h1>;
};

// 문제상황 발생: 렌더링과정에서 1. sideEffect가 렌더링을 blocking한다.
// 2. 매 렌더링 마다 sideEffect가 수행된다.

기본적으로 코드는 위에서 아래 방향으로 순차적으로 실행됩니다. 따라서 App 함수 컴포넌트는doSideEffect() 동작이 끝날 때까지 JSX를 리턴하는 코드로 넘어가지 않습니다. 컴포넌트가 JSX를 return 하기 전까지는 UI가 브라우저상에 렌더링 되지 않기 때문에 결국 사이드 이펙트가 끝나기 전까지 렌더링을 하지 못하고 멈춰있게 됩니다.

즉, 사용자가 UI가 업데이트되는 것을 보기까지 오랜 시간이 소요된다는 것입니다. 이는 곧 사용자에게 좋지 못한 사용자 경험을 제공한다는 의미입니다.

SideEffect는

  • 렌더링을 Blocking 하지 않기 위해서 렌더링이 모두 다 완료되고 난 후 실행할 수 있어야 한다.
  • 매 렌더링마다 실행되는 것이 아니라 내가 원할 때만 조건부로 실행할 수 있어야 한다.
useEffect 사용법

useEffect(CallbackFunction

const App = () => {
  // 코드 생략

  doSideEffect();

  return <h1>Hello, Wecoder</h1>;
};

//rendering 전 sideEffect발생 해결
import { useEffect } from 'react';

const App = () => {
  // 코드 생략

  useEffect(doSideEffect);

  return <h1>Hello, Wecoder</h1>;
};

이번에는 side effect를 발생시키는 함수를 바로 호출하는 것이 아니라 useEffect의 인자로 전달했습니다. 위와 같이 useEffect의 인자로 전달된 콜백 함수는 곧바로 호출되는 것이 아니라 모든 렌더링이 완료된 후에 호출됩니다. 즉 렌더링을 blocking 하지 않고 side effect를 발생시킬 수 있게 되는 것입니다.

조건부로 sideEffect 발생시키기

useEffect(콜백 함수, 의존성 배열);
useEffect의 동작 방식은 간단합니다.

첫 번째 렌더링 이후에는 무조건 useEffect에 전달된 콜백 함수를 호출하고 다음 렌더링부터는 아래의 조건에 따라 동작합니다.

의존성 배열이 전달되지 않았다면 매 렌더링마다 콜백 함수를 호출한다.
의존성 배열이 전달되었다면 의존성 배열의 값을 검사한다.

  • 의존성 배열에 있는 값 중 하나라도 이전 렌더링과 비교했을 때 달라졌다면 콜백 함수를 호출한다.
  • 의존성 배열에 있는 값이 이전 렌더링과 비교했을 때 모두 다 같다면 콜백 함수를 호출하지 않는다.

즉, useEffect에서 첫 번째 인자인 콜백 함수는 실행시킬 동작을 결정하고 두 번째 인자인 의존성 배열은 실행시킬 타이밍을 결정짓는다고 할 수 있습니다.

import { useEffect } from 'react'

// 사용법
useEffect(콜백 함수, 의존성 배열);

// 1. 의존성 배열이 전달되지 않았으므로 매 렌더링마다 side effect가 실행된다
useEffect(() => {
  // side effect
});

// 2. 첫 번째 렌더링 이후에 side effect를 실행하고
// 그 이후에는 value 값이 변했을 때만 실행한다.
useEffect(() => {
  // side effect
}, [value]);

// 3. 첫 번째 렌더링 이후에 side effect를 실행하고
// 그 이후에는 value1, value2 중 하나라도 변하면 side effect를 실행한다.
useEffect(() => {
  // side effect
}, [value1, value2]);

//4. 첫번째 렌더링 이후 실행되고 그 뒤 실행되지 않는다.
useEffect(() => {
  // data fetching side effect
}, []);


함수 컴포넌트의 렌더링과 useEffect가 발생하는 과정을 풀어서 설명하자면 아래와 같습니다.

  • 컴포넌트가 렌더링 된다.
    (최초로 진행되는 렌더링은 브라우저에 처음으로 이 컴포넌트가 보였다는 의미로 mount 라고 표현합니다.)

  • useEffect 첫 번째 인자로 넘겨준 콜백 함수가 호출된다. (Side Effect)

  • 컴포넌트의 state 또는 props가 변경되었을 경우 리렌더링이 발생한다. (update)

  • useEffect는 두 번째 인자에 들어있는 의존성 배열을 확인한다

    • 만약 의존성 배열이 전달되지 않았거나 / 의존성 배열 내부의 값 중 이전 렌더링과 비교했을 때 변경된 값이 하나라도 있다면 첫 번째 인자로 넘겨준 콜백 함수가 호출된다. (Side Effect)
    • 의존성 배열 내부의 값 중 이전 렌더링과 비교했을 때 변경된 값이 없다면 콜백 함수를 호출하지 않는다.
    • state 또는 props가 변경된다면 3~4의 과정을 반복
  • 컴포넌트가 더 이상 필요 없어지면 화면에서 사라진다.

(컴포넌트가 브라우저의 화면에서 사라졌다는 의미로 unmount라고 표현합니다.)

CleanUp Effect

side effect에는 여러 종류가 있을 수 있습니다. 그중에서는 반드시 clean up이 필요한 effect들이 있을 수도 있습니다. 어떤 종류의 side effect가 clean up이 필요할까요? 그 기준은 해당 side effect가 지속적으로 남아있는가?를 생각해 보시면 됩니다. 아래의 side effect는 지속적으로 남아있지 않기 때문에 clean up이 필요하지 않습니다.

CleanUp 방법

useEffect는 side effect를 clean up 할 수 있는 방법을 제공해 줍니다. 결론부터 말하자면 useEffect에서 side effect를 cleanup 하기 위해서는 useEffect에 전달한 콜백 함수에서 clean up을 하는 함수를 리턴하면 됩니다.

useEffect(() => {
  const button = document.getElementById('consoleButton');

  const printConsole = () => {
    console.log('button clicked');
  };

  button.addEventListener('click', printConsole);
});

//CleanUp return
useEffect(() => {
  const button = document.getElementById('consoleButton');

  const printConsole = () => {
    console.log('button clicked');
  };

  button.addEventListener('click', printConsole);

  // side effect를 clean up 하기 위한 함수를 선언한다.
  const removeEventListener = () => {
    button.removeEventListener('click', printConsole);
  };

  // clean up 함수를 return 한다.
  return removeEventListener;
});

profile
Dreams come true

0개의 댓글