[React] 클라이언트 Ajax 요청 (2) - Side Effect, Effect Hook

선정·2022년 6월 15일
0

Today I Learned

  • Side Effect
  • Effect Hook

Side Effect (부수 효과)

함수 내에서 어떤 구현이 함수 외부에 존재하는 값이나 상태에 영향을 끼치는 행위

React에서는 컴포넌트가 화면에 렌더링된 이후에 비동기로 처리되어야 하는 부수적인 효과들을 흔히 Side Effect라고 한다.


React에서 Side Effect의 대표적인 예

  • AJAX 요청 시 발생
  • 이벤트를 활용해 DOM을 직접 조작할 때 발생
  • localStorage API 사용 시 발생
  • Timer API(setTimeout, setInterval) 사용 시 발생

let foo = 'hello';

function bar() {
  foo = 'world';
}

bar();
console.log(foo) // 'world'

위의 예제에서 전역 변수 foo를 bar라는 함수가 수정했다 (재할당됨). 여기서 함수 bar는 Side Effect를 발생시킨 것이다.



Pure Function (순수 함수)

오직 함수의 입력만이 함수의 결과에 영향을 주는 함수

보통 Side Effect를 설명할 때 함께 언급되는 개념이다. 순수함수는 Side Effect가 없는 함수로, 동일한 인수가 들어갈 경우 항상 같은 값을 리턴하며 외부 상태에 영향을 끼치지 않는 함수이다. 그래서 예측 가능한 함수이기도 하다.

function upper(str) {
  return str.toUpperCase();
}

upper("hello") // 'HELLO'
// toUpperCase 메서드는 원본 인수를 수정X
// upper 함수는 순수함수이다.

  • Math.random()이 순수 함수가 아닌 이유?
    : 실행할 때마다 무작위로 0~1 사이의 값을 반환하므로 동일 인수를 전달 했을 때, 항상 같은 값이 리턴됨이 보장되지 않는다.

  • AJAX 요청을 하는 함수가 순수 함수가 아닌 이유?
    : 네트워크 및 서버 상태에 따라 응답이 달라질 수 있으므로 마찬가지로 동일 인수를 전달 했을 때, 항상 같은 값이 리턴됨이 보장되지 않는다.


리액트에서는 AJAX 요청, Timer API나 LocalStorage API 사용하는 경우, Side Effect가 발생할 수 있다. 이 때, React는 Side Effect를 다루기 위한 Hook인 Effect Hook을 제공한다.


순수함수 참고



Effect Hook

useEffect는 컴포넌트 내에서 Side Effect를 실행할 수 있게 하는 Hook이다.

어떤 값이 변경될 때마다 특정 코드를 실행시키고 싶을 때, 또는 최초 렌더링 시에만 한번 데이터를 받아오고 싶을 때 주로 useEffect를 사용한다.

// useEffect(함수, [종속성1, 종속성2, ...])
useEffect(() => {}, [])

useEffect의 첫 번째 인자에는 실행시키고 싶은 함수, 두 번째 인자에는 의존성 배열(dependency array)이 온다.

의존성 배열은 조건을 담고 있는데 여기서 조건은 어떤 값의 변경이 일어날 때를 의미한다. 이 배열 안에 들어간 값이 변경될 때마다 첫 번째 인자의 함수가 실행된다.


최초 렌더링 시에 한 번만 실행하고 싶을 때

  • 의존성 배열을 빈배열로 둔다.
import React, { useState, useEffect } from 'react';

const fruits = ['apple', 'strawberry', 'banana', 'peach', 'grape'];

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

  useEffect(() => {
    // count 상태가 변경될 때마다 타이틀이 변경된다.
    document.title = fruits[Math.floor(Math.random() * 5)];
  }, []);

  return (
    <div>
      <input
        type="text"
        value={value}
        onChange={(e) => setValue(e.target.value)}
      />
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
    </div>
  );
}

export default Example;

최초 렌더링 시, useEffect 내의 함수가 실행되면서 fruits 배열에서 무작위로 선택된 요소가 상단 탭의 타이틀이 된다. 이후 Click me 버튼을 누르거나 input 창에 타이핑을 해 count 값이나 value 값을 시켜도 useEffect 내의 함수는 다시 실행되지 않는다.


특정 값이 변할 때마다 실행하고 싶을 때

  • 의존성 배열에 해당 값을 담는다.
  • 특정 값을 넣어도 최초 렌더링 시 무조건 한 번은 실행된다.
import React, { useState, useEffect } from 'react';

const fruits = ['apple', 'strawberry', 'banana', 'peach', 'grape'];

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

  useEffect(() => {
    // count 상태가 변경될 때마다 타이틀이 변경된다.
    document.title = fruits[Math.floor(Math.random() * 5)];
  }, [count]);

  return (
    <div>
      <input
        type="text"
        value={value}
        onChange={(e) => setValue(e.target.value)}
      />
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
    </div>
  );
}

export default Example;

위 코드에서 Click me 버튼을 누르면 count 값이 업데이트 되고, useEffect의 의존성 배열은 count 값이 변경됨을 캐치해 함수를 실행한다. 그래서 count 값이 변경될 때마다 상단 탭의 타이틀이 랜덤으로 변경된다. value 값은 의존성 배열 내에 담지 않았기 때문에 변경되더라도 타이틀이 변경되지 않는다.


의존성 배열을 생략한다면?

useEffect의 두 번째 인자는 사실 필수 값은 아니다. 의존성 배열을 생략해버리면 최초 렌더링 시 한 번, 그리고 이후 리렌더링이 발생할 때마다 useEffect 내의 함수가 실행 된다.

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

const fruits = ['apple', 'strawberry', 'banana', 'peach', 'grape'];

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

  useEffect(() => {
    // count, value 상태가 변경될 때마다 타이틀이 변경된다.
    document.title = fruits[Math.floor(Math.random() * 5)];
  });

  return (
    <div>
      <input
        type="text"
        value={value}
        onChange={(e) => setValue(e.target.value)}
      />
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
    </div>
  );
}

export default Example;

위의 코드는 의존성 배열에 count만 담았던 때와 달리, value 값이 변경될 때도 상단 탭의 타이틀이 변경된다. count 값이 변경될 때, value 값이 변경될 때 둘 다 리렌더링이 발생하기 때문이다.

profile
starter

0개의 댓글