React | side/use/clean up Effect

주미·2022년 1월 17일
0

React

목록 보기
2/2
post-thumbnail

side Effect

❗️Rendering in React

React에서 함수 컴포넌트의 rendering = state, props를 기반으로 UI 요소를 그려내는 행위

렌더링의 결과물 = UI 요소(화면에 JSX 문법으로 무엇이 나타날지를 적어둔 컴포넌트들)
렌더링 결과물에 영향을 주는 요소는 = state, props
즉, input(state & props)에 따라 다른 output(UI)을 표현하는 함수(function)와도 구조적으로 동일

❗️side Effect

프로그래밍 측면에서의 side Effect는 함수가 어떤 동작을 할 때,
input - output 이외의 다른 값을 조작한다면, 이 함수에는 Side Effect(부수 효과) 가 있다고 표현

[예시]
let count = 0

function greetWithSideEffect(name) { // Input
  count = count + 1 // Side Effect!

  return `${name}님 안녕하세요!` // Output
}

greetWithSideEffect() 라는 함수는 이름을 받아 인삿말을 리턴하는 함수입니다.

하지만 이 함수는 단순히 input과 output만 존재하는 함수가 아닙니다.
실행하는 중간에 함수 외부 세계에 있는 count라는 변수를 조작합니다.
이는 함수의 결과값 이외의 다른 상태를 변경시키는 행위 에 해당하므로 Side Effect 가 있다고 할 수 있습니다.

함수 컴포넌트의 Side Effect는 state와 props를 받아서 UI를 그려내는 것 이외의 행위라고 할 수 있습니다.

그렇다면 함수 컴포넌트에서의 Side Effect는, 렌더링이 아니고 외부 세계에 영향을 주는 어떠한 행위입니다.
(Ex : Data Fetching, DOM에 직접 접근(ex. Event Listener 등록), 구독(ex. setInterval)과 같은 행위)

use Effect

❌ side Effect들을 함수의 body 자리(render)에서 실행시키면 안됩니다.

함수 컴포넌트의 리턴 값은 UI 요소이고, state&props의 변화가 있을 때마다 함수가 실행됨
= 매 렌더링 때마다 함수 body에 있는 로직이 실행됨
또한 렌더링과 무관한 로직이 렌더링 과정에서 실행되기 때문에 렌더링 자체에 영향을 줘 성능 상 악영향을 끼칠 수도 있음

[예시]
function greetWithSideEffect({ name }) { // 
  // Bad!
  document.title = `${name}님 안녕하세요!`; // Side Effect

  return <div>{`${name}님 안녕하세요!`}</div>; // Output
}
  • React에서는 이런 Side Effect를 일으키기 적절한 장소로서 useEffect hook을 제공
  • useEffect는 Side Effect를 렌더링 이후에 발생시킴(예외: useLayoutEffect)
  • useEffect가 수행되는 시점에 이미 DOM이 업데이트되었음을 보장한다는 뜻
    = 바꿔 말하면 Side Effect가 렌더링에 영향을 주지 않도록 설계되었음을 의미

❗️ 의존성 배열(Dependancy Array)

[예시]
import { useEffect } from 'react';

function greetWithSideEffect({ name }) { // Input
  useEffect(() => {
    // Good!
    document.title = `${name}님 안녕하세요!`; // Side Effect
  }, [name]);

  return <div>{`${name}님 안녕하세요!`}</div>; // Output
}
  • 만약 Side Effect 이후 업데이트 된 정보가 있어 새롭게 화면이 그려져야 할 경우(state, props의 업데이트) 새롭게 렌더링
  • 함수 컴포넌트는 최신 state와 props를 반영한 화면을 리턴하게 됩니다.
  • Effect를 일으킬 타이밍은 useEffect의 두 번째 인자인 의존성 배열(Dependancy Array)를 통해 표현하게 됨
  import { useEffect } from "react"

  // 사용법
  useEffect( 실행시킬 동작, [ 타이밍 ] )
  document.addEventListener("타이밍", 실행시킬 동작) // 추상화 한 예시

  // 매 렌더링마다 Side Effect가 실행되어야 하는 경우
  useEffect(() => {
    // Side Effect
  })

  // Side Effect가 첫 번째 렌더링 이후 한번 실행 되고,
  // 이후 특정 값의 업데이트를 감지했을 때마다 실행되어야 하는 경우
  useEffect(() => {
    // Side Effect
  }, [value])

  // Side Effect가 첫 번째 렌더링 이후 한번 실행 되고,
  // 이후 어떤 값의 업데이트도 감지하지 않도록 해야 하는 경우
  useEffect(() => {
    // Side Effect
  }, [])

❗️ useEffect를 사용한 렌더링 사이클

  • 컴포넌트 렌더링 (최초로 진행되는 렌더링은 브라우저에 처음으로 이 컴포넌트가 보여졌다는 의미로 mount 라고 표현)
  • useEffect 첫 번째 인자로 넘겨준 함수(callback) 실행(Side Effect)
  • 재 렌더링(re-render)
    (state나 props가 변경된 경우)
  • useEffect는 두 번째 인자에 들어있는 의존성 배열을 체크
    1) 두 번째 인자에 아무런 값도 넘기지 않았거나
    2) 인자로 넘긴 배열에 들어있는 값 중 업데이트된 것이 하나라도 존재
    첫 번째 인자로 넘겨준 함수(callback)가 실행(Side Effect)
    3) 하나도 없거나 빈 배열이라면, 아무일도 X
  • 만약 앞에서 일으킨 Effect에서 state나 props를 변경시켰다면 재 렌더링
  • (중략...)
  • 컴포넌트가 필요 없어지면 화면에서 사라짐 (컴포넌트가 브라우저의 화면에서 사라졌다는 의미로 unmount라고 표현)

clean up Effect

❗️ clean up Effect

이전에 일으킨 Side Effect를 정리할 필요가 있을 때 사용

useEffect(() => {
  function handleScroll() {
    console.log(window.scrollY)
  }
  document.addEventListener("scroll", handleScroll)
}, [])
  • 위의 코드는 페이지에 스크롤 이벤트가 일어날 때마다 console에 현재 스크롤이 위치한 Y 좌표를 출력
  • Side Effect이므로 useEffect 안에서 사용하고, 이벤트 리스너는 한번만 등록하면 되기 때문에 의존성 배열에는 빈 배열을 넣음
  • 하지만 우리가 이 페이지를 벗어났을 때 이 이벤트 리스너는 더 이상 필요없어질 수 있음(이 경우 우리가 일으켰던 Effect를 정리해줘야 함)
useEffect(() => {
  function handleScroll() {
    console.log(window.scrollY)
  }

  document.addEventListener("scroll", handleScroll)
  return () => {
    document.removeEventLisnter("scroll", handleScroll)
  }
}, [])
  • Cleanup Effect를 일으킬 수 있도록 useEffect 안에 해당 로직을 정리하는 동작을 정의

❌ 주의할 점은 단순히 컴포넌트가 생성되고, 사라지는 시점에만 Cleanup Effect가 실행되는 건 아님
다음 Effect가 일어나기 전에, 이전 Effect의 영향을 정리해줘야 한다는 컨셉을 꼭 기억하기

Render와 Clean Up, Effect 사이의 관계

0개의 댓글