React 공식문서 이해하기 (8)

Syoee·2023년 11월 16일
0

React

목록 보기
8/30
post-thumbnail

Chapter 1. Describing the UI

#8 컴포넌트 순수성 유지

학습 목차

1. 순수성: 수식으로서의 컴포넌트
2. 사이드 이펙트: 의도하지 (않은) 결과
3. 사이드 이펙트를 일으킬 수 있는 곳


1. 순수성: 수식으로서의 컴포넌트

  • 컴퓨터 과학(특히 함수형 프로그래밍의 세계)에서 순수 함수는 다음과 같은 특징을 가진 함수이다.
    • 자신의 일에만 신경쓴다. 호출되기 전에 존재했던 객체나 변수를 변경하지 않는다.
    • 동일 입력, 동일 출력. 동일한 입력이 주어지면 항상 동일한 결과를 반환해야 한다.
  • React는 여러분이 작성하는 모든 컴포넌트가 순수 함수라고 가정한다.
    즉, 작성하는 React 컴포넌트는 동일한 입력이 주어졌을 때 항상 동일한 JSX를 반환해야 한다.
  • 컴포넌트를 레시피라고 생각할 수 있다.
    레시피를 따르고 요리 과정에서 새로운 재료를 넣지 않으면 매번 같은 요리를 얻을 수 있다.
    그 “요리”는 컴포넌트가 렌더링에 반응하기 위해 제공하는 JSX이다.

2. 사이드 이펙트: 의도하지 (않은) 결과

  • React의 렌더링 프로세스는 항상 순수해야 한다.
    컴포넌트는 오직 JSX만을 반환해야 하며, 렌더링 전에 존재했던 객체나 변수를 변경해서는 안된다.
// 예시

let guest = 0;

function Cup() {
  // Bad: changing a preexisting variable!
  // 나쁨: 기존 변수를 변경합니다!
  guest = guest + 1;
  return <h2>Tea cup for guest #{guest}</h2>;
}

export default function TeaSet() {
  return (
    <>
      <Cup />
      <Cup />
      <Cup />
    </>
  );
}
  • 이 컴포넌트는 외부에서 선언된 guest 변수를 읽고 쓰고 있다.
    즉, 이 컴포넌트는 호출할 때마다 다른 JSX가 생성된다는 뜻이다.
  • 게다가 다른 컴포넌트가 guest를 읽으면 렌더링된 시점에 따라 JSX도 다르게 생성되므로 예측할 수 없는 일이다.
  • 아래와 같이 guest를 prop으로 전달함으로써 이 컴포넌트를 고칠 수 있다.
function Cup({ guest }) {
  return <h2>Tea cup for guest #{guest}</h2>;
}

export default function TeaSet() {
  return (
    <>
      <Cup guest={1} />
      <Cup guest={2} />
      <Cup guest={3} />
    </>
  );
}
  • 이제 컴포넌트가 반환하는 JSX는 guest prop에만 의존하므로 순수하다.
  • 일반적으로 컴포넌트가 특정 순서로 렌더링될 것이라고 기대해서는 안된다.
    y = 2x를 y = 5x 앞에 호출하든 뒤에 호출하든 상관없다. 두 수식은 서로 독립적으로 해결된다.
  • 마찬가지로 각 컴포넌트는 렌더링 중에 다른 컴포넌트와 조율하거나 의존하지 말고 “스스로 생각”하게 해야 한다.

2-1. 지역 변이(Local mutation): 컴포넌트의 작은 비밀

  • 위의 예시에서는 컴포넌트가 렌더링하는 동안 기존 변수를 변경하는 것이 문제였다.
    이를 좀 더 무섭게 들리게 하기 위해 “변이” 라고 부르기도 한다.
    순수 함수는 함수의 범위를 벗어난 변수나 호출 전에 생성된 객체를 변이하지 않는다.
  • 하지만 렌더링하는 동안 ‘방금’ 생성한 변수와 객체를 변경하는 것은 괜찮다.
    이 예제에서는 [] 배열을 생성하고 이를 cups 변수에 할당한 다음 컵 12개를 그 안에 push한다.
function Cup({ guest }) {
  return <h2>Tea cup for guest #{guest}</h2>;
}

export default function TeaGathering() {
  let cups = [];
  for (let i = 1; i <= 12; i++) {
    cups.push(<Cup key={i} guest={i} />);
  }
  return cups;
}
  • cups 변수나 [] 배열이 TeaGathering 함수 외부에서 생성되었다면 이는 문제가 될 것이다.
    해당 배열에 항목을 밀어 넣음으로써 기존 객체를 변경하게 될 것이기 때문이다.
  • 하지만 TeaGathering 내부에서 동일한 렌더링 중에 생성했기 때문에 괜찮다.
    TeaGathering 외부의 어떤 코드도 이런 일이 일어났다는 것을 알 수 없다.
    이를 “지역 변이”라고 하며, 컴포넌트의 작은 비밀과 같다.

3. 사이드 이펙트를 일으킬 수 있는 곳

  • 함수형 프로그래밍은 순수성에 크게 의존하지만, 언젠가는 어딘가에서 무언가는 바뀌어야 한다.
    화면 업데이트, 애니메이션 시작, 데이터 변경과 같은 이러한 변경을 사이드 이펙트라고 하며, 렌더링 중에 일어나는 것이 아니라 “부수적으로” 일어나는 일입니다.

  • React에서 사이드 이펙트는 보통 이벤트 핸들러에 속한다.
    이벤트 핸들러는 사용자가 어떤 동작을 수행할 때(예를 들어, 버튼을 클릭할 때) React가 실행하는 함수이다.

  • 이벤트 핸들러가 컴포넌트 내부에 정의되어 있긴 하지만 렌더링 중에는 실행되지 않는다.
    따라서 이벤트 핸들러는 순수할 필요가 없다.

  • 다른 모든 옵션을 다 사용했는데도 사이드 이펙트에 적합한 이벤트 핸들러를 찾을 수 없다면, 컴포넌트에서 useEffect 호출을 통해 반환된 JSX에 이벤트 핸들러를 첨부할 수 있다.
    이렇게 하면 나중에 렌더링 후 사이드 이펙트가 허용될 때 React가 이를 실행하도록 지시한다.
    하지만 이 방법은 최후의 수단으로 사용해야 한다.


요약

  • 컴포넌트는 순수해야 한다.
    • 자신의 일에만 신경씁니다. 렌더링 전에 존재했던 객체나 변수를 변경하지 않아야 한다.
    • 동일한 입력, 동일한 출력. 동일한 입력이 주어지면 컴포넌트는 항상 동일한 JSX를 반환해야 한다.
  • 렌더링은 언제든지 발생할 수 있으므로, 컴포넌트는 서로의 렌더링 순서에 의존해서는 안 됩니다.
  • 컴포넌트가 렌더링에 사용하는 어떠한 입력값도 변이해서는 안 됩니다. 여기에는 props, state 및 context가 포함됩니다. 화면을 업데이트하려면 기존 객체를 변이하는 대신 “set” state를 사용하자.
  • 컴포넌트의 로직을 반환하는 JSX 안에 표현하기 위해 노력하세요. “무언가를 변경”해야 할 때는 보통 이벤트 핸들러에서 이 작업을 수행하고자 할 것입니다. 최후의 수단으로 useEffect를 사용할 수도 있습니다.
  • 순수 함수를 작성하는 데는 약간의 연습이 필요하지만, React 패러다임의 힘을 발휘할 수 있습니다.

React 공식 문서

https://react.dev/

React 비공식 번역 문서

https://react-ko.dev/

MDN

https://developer.mozilla.org/ko/

Wikipedia

https://ko.wikipedia.org/wiki/

profile
함께 일하고 싶어지는 동료, 프론트엔드 개발자입니다.

0개의 댓글

관련 채용 정보