211122 TIL useEffect

13395 단어 ReactReact

useEffect

Side Effect

Side Effect는 함수가 결과값 이외에 다른 상태를 변경시킬 때를 의미한다.

아래 예시가 있다.

  1. 함수 외부에 존재하는 count 변수를 읽어서 조작한다. 때문에 Side Effect다.
let count = 0;

function sayHello(name){
  count = count + 1; // side effect
  return `${name}님 안녕하세요 🥰`;
}

sayHello('소소'); // '소소님 안녕하세요 🥰'
count; // 1
  1. console.log는 브라우저의 콘솔 창에 출력하는 함수이기 때문에 sayHello에서 값을 반환하는 기능과 별개의 외부 행동을 한다. 때문에 Side Effect다.
function sayHello(name){
  console.log(name); // side effect
  return `${name}님 안녕하세요 🥰`;
}

sayHello('소소'); // '소소님 안녕하세요 🥰'
  1. 외부의 값을 변경하지 않고, 전달된 인수로 num을 받아 내부의 값 2를 더했기 때문에 Side Effect가 아니다.
let count = 0;

function printNum(num){
  const result = num + 2; // side effect가 아니다.
  return result;
}

printNum(1); // 3

이처럼 외부 값을 읽거나 변화시키면 side effect다.

리엑트에서 컴포넌트 렌더링

Rendering이란 쉽게 말하면 화면에 그리는 것을 의미한다.
리엑트는 MVP에서 View를 담당하는 즉, User Interface를 만들 수 있는 라이브러리다.
이 UI는 컴포넌트 단위로 이뤄져 있는데 컴포넌트가 가진 propsstate를 가지고 상태가 변할 때마다 render 함수를 호출하며 그려준다.

즉, propsstate가 Input으로 들어가 UI를 표현하는 Output으로 나오는 함수 구조로 볼 수 있다.

컴포넌트를 만들다보면 UI를 그리는 것과 관련 없는 행위들이 존재한다.
예를 들어 Data Fetching, DOM에 직접 접근, setInterval 같은 행위들이 있다.

UI를 그린다는 리엑트의 목표 외에 다른 행동이 일어나는 이것을 리엑트에서는 side Effect라고 부른다.

즉, 리엑트에서 propsstate 외 다른 행동을 하면 side effect이다.

하지만 propsstate 이외에 API를 통해 데이터를 받아온다던지, 브라우저를 직접적으로 조작해야하는 등 다양한 행동이 필요하다.

이를 보완하기 위해 나온게 바로 useEffect hook다.

useEffect

useEffect는 말그대로 Effect를 쓰겠다, side Effect를 사용하겠다는 의미로 사용한다.

useEffect에 대해 배우기 전 리엑트에서 side Effect를 짚고 넘어가보자.

리엑트에서 sideEffect 문제점

1. 렌더링을 막는 sideEffect

아래와 같이 컴포넌트 안에서 만약 side Effect를 일으키고 UI 렌더링을 반환할 경우 어떤 문제가 생길까?

function Component(){
  // sideEffect...
  
  return //UI rendering
}

바로 렌더링을 막는 문제가 생긴다.

UI를 rendering을 하려면 결국 JSX를 return해야하는데
return 전 side Effect 예시로 API를 받아오는 동작을 함수가 처리한다면 그리고 이 처리가 1분 이상이 걸린다면?

그동안 UI렌더링이 되지 않고 사용자는 빈화면만 막연히 보게 된다. 심지어 무슨 문제가 있는지 설명도 보지 못한다.

사용자들이 우리 웹사이트와 빠빠이하는걸 볼 수 있다 👋

2. 계속 trigger되는 sideEffect

리엑트는 상태가 변할 때마다 리렌더링이 실시된다.
리렌더링이 된다는 것은 이 함수형 컴포넌트가 다시 호출된다는 뜻이다.
즉, stateprops가 바뀔 때 마다 함수형 컴포넌트가 다시 호출된다.
그럼 이 안에 있는 API를 통한 데이터 호출도 다시 실행되는 것이다.

변할 때마다 데이터를 패칭이 된다면 아주 최악이다.
1분 걸렸던 패칭을 호출될 때마다 걸린다면 서버의 부화와 클라이언트가 필요없는 연산을 계속해야 한다.

우리는 데이터 패칭을 시작할 때 딱 1번 필요하지 계속해서 받아오려고 연산할 필요가 없다.

sideEffect를 해결하는 useEffect

이러한 리엑트의 sideEffect를 해결하기 위해 나온 것이 useEffect다.

useEffect(effect, dependency array);

useEffect는 리엑트에서 만든 hook이기 때문에 쓰려면 import하는 것을 잊지 말아야한다.

1. 렌더링이 종료되면 실행되는 useEffect

아래 예시를 보면 useEffect가 console.log("function exe"); 보다 위에 있음에도 불구하고 콘솔창에는 "function exe"이 먼저 출력되는 것을 확인할 수 있다.

즉, useEffect는 렌더링이 종료되고 난 이후 실행됨을 알 수 있다.

2. 매번 바뀔 필요 없이, [Dependency Array]에 있는 얘들이 바뀔 때만 실행하는 useEffect

useEffect의 두 번째 인자로 특정 값을 넣으면 이 특정 값이 바뀔 때만 해당 함수를 다시 실행할 수 있다.

// count state가 바뀔 때마다 console.log()가 실행된다.
useEffect(()=>{
  console.log('effect trigger');
},[count])

useEffect를 통해 디버깅을 할 수 있다.

[Dependency Array]

  1. 랜더링 이후 특정 시점(특정 값이 변할 때)에 함수 실행
useEffect(()=>{
  console.log('effect trigger');
},[count])

이런 경우 해당 값이 변할 때마다 console.log가 찍히기 때문에 해당 값이 제대로 동작되는지 확인하는 디버깅용으로도 사용할 수 있다.

  1. 컴포넌트 마운트 이후 첫 렌더링때만 실행
useEffect(()=>{
  console.log('empty dependency');
},[])
  1. 항상 실행
useEffect(()=>{
  console.log('effect trigger');
});

만약 [count, keyState] 같이 배열에 여러 개가 들어있다면 하나만 바뀌어도 실행되기 때문에 만약 따로 실행해야 한다면 분리해서 하나씩만 설정하는 게 좋다.

이는 유지보수 측면에서도 내가 하고자 하는 동작 하나에만 useEffect 적용하는 것이 좋다.

이를 관심사 분리라고 한다. 하나의 동작을 처리한다.

clean up Effect

말 그대로 이펙트를 치운다는 뜻이다.

이전에 내가 발생시킨 이펙트가 단발성이면 괜찮지만 addEventListener()처럼 구독형 이펙트를 만들었고 해제를 하지 않으면 사용자가 탭을 닫기 전까지 그 이벤트는 계속 일어난다.

리엑트는 SPA기 때문에 불필요한게 계속 돌아가고, 만약 백엔드와 API를 콜이 계속 발생하는 현상이 나타난다.

useEffect(()=>{
  // 마운트 시점 동작
  const count = setInterval(()=>{
    console.log('count');
  }, 1000)
  
  // 언마운트 시점 클린 업
  return () =>{
    clearInterval(count);
  }
}, [])

clean up 이펙트가 발생하는 정확한 시점은 다음 이펙트가 발생하기 즉, 전 새로운 이펙트가 발생하기 전에 clean up을 시키고 함수를 발생시킨다.

+) 버튼에 붙은 이벤트는 버튼 자체가 화면에서 사라지기 때문에 clean up을 할 필요가 없다.

좋은 웹페이지 즐겨찾기