[React] 상태 관리 & Props Drilling

jungmin Lee·2023년 9월 15일
0

프론트엔드 개발에서의 상태 관리

1. 전역 상태 관리

프론트엔드 개발에서 상태는 변하는 데이터이며 UI에 동적으로 표현될 데이터를 상태라고 할 수 있다. 상태를 다룰 때에는 함수 또는 컴포넌트의 입력 외에도 함수의 결과에 영향을 미치는 요인으로 Side Effect를 고려해야하며 네트워크 요청, API 호출이 대표적이 예시라고 볼 수 있다. React의 주요 개발 원칙 중 UI를 컴포넌트 우선으로 개발하는 방식이며 어떤 데이터가 들어오는지에 영향 받지 않고 컴포넌트 그 자체에 집중하는 것을 말한다. 그러나 앱을 개발하면서 side effect가 생길 수 있다. 상태를 특정 컴포넌트 안에서만 관리되는 상태인 로컬 상태와 프로덕트 전체 혹은 여러 컴포넌트에서 관리되는 상태인 전역 상태로 구분할 수 있다. 로컬 상태는 보통 컴포넌트 내에서만 영향을 끼치는 상태로 input box, select box 등과 같이 폼 데이터로 예시를 들 수 있다. 전역 상태는 다른 컴포넌트와 상태를 공유하고 영향을 끼치는 상태로 장바구니에 담긴 물품의 선택 여부로 총 주문 금액, 갯수 등을 업데이트하거나 전달해줘야 한다. 서로 다른 컴포넌트가 사용하는 상태의 종류가 다르면 전역 상태로 다룰 필요가 없지만 동일한 상태를 다룬다면 출처는 한 곳으로 정해야 한다. 즉, 데이터 무결성을 위한 방법론으로 동일한 데이터는 항상 같은 곳에서 데이터를 가지고 오도록 해야하며 Single source of truth로 다양한 곳에서 언급되는 원칙이다.


상태 관리를 위한 각종 툴
▷ React Context
▷ Redux
▷ MobX
➡ 상태 관리 툴은 전역 상태 저장소를 제공하여 Props driling 이슈를 해결할 수 있다.

2. Props Driling

Props Driling은 상위에 있는 Component1에서 Component4로 state를 props를 통해 전달할 때 Component2와 Component3을 Props를 전달하는 용도로만 쓰면서 데이터를 전달하는 현상을 말한다.
➡ Props의 전달 횟수가 5회 이내라면 크게 문제가 되지 않지만 전달 과정이 늘어난다면 코드의 가독성과 유지보수가 나빠지게 되며 state 변경시 Props 전달 과정에서 관여된 컴포넌트들이 리렌더링되면서 웹성능에 악영향을 줄 수 있다.


해결 방법 예시
▷ 컴포넌트와 관련있는 state는 될 수 있으면 가까이 유지
▷ 상태관리 라이브러리를 사용
➡ 상태관리 라이브러리를 사용하면 전역으로 관리하는 저장소에서 직접 state를 꺼내쓸 수 있다.

Props Driling 예시

예시를 보면 부모 컴포넌트에 있는 Props를 Child4 컴포넌트에 전달해주기 위해서 Child1, Child2, Child3 컴포넌트에 plusNum과 minusNum이 Prop로 내려준 것을 확인할 수 있다. 지금의 상황보다 복잡한 구조의 컴포넌트가 있다면 상태 관리에 굉장히 어려움이 있게 될 것이다.

import React, { useState } from 'react';
import styled from 'styled-components';

export default function App() {
  const [number, setNumber] = useState(1);

  const plusNum = () => {
    setNumber(number + 1);
  };

  const minusNum = () => {
    setNumber(number - 1);
  };
  console.log('Parents');
  return (
    <Container>
      <Text weight size="1.5rem">
        [Parents Component]
      </Text>
      <Quantity>{`수량 : ${number}`}</Quantity>
      <Child1 plusNum={plusNum} minusNum={minusNum} />
    </Container>
  );
}

function Child1({plusNum, minusNum}) {
  console.log('Child1');
  return (
    <Container>
      <Text>[Child 1 Component]</Text>
      <Child2 plusNum={plusNum} minusNum={minusNum} />
    </Container>
  );
}

function Child2({plusNum, minusNum}) {
  console.log('Child2');
  return (
    <Container>
      <Text>[Child 2 Component]</Text>
      <Child3 plusNum={plusNum} minusNum={minusNum}/>
    </Container>
  );
}

function Child3({plusNum, minusNum}) {
  console.log('Child3');
  return (
    <Container>
      <Text>[Child 3 Component]</Text>
      <Child4 plusNum={plusNum} minusNum={minusNum}/>
    </Container>
  );
}

function Child4({ plusNum, minusNum }) {
  console.log('Child4');
  return (
    <Container>
      <Text>[Child 4 Component]</Text>
      <Button onClick={plusNum}>👍</Button>
      <Button onClick={minusNum}>👎</Button>
    </Container>
  );
}

Props Driling 해결 예시

상태관리 라이브러리 Redux를 활용하여 해결한 예시이다. useSelector와 useDispatch를 사용하여 Child4 컴포넌트에만 state를 전달해주었으며 코드의 가독성과 유지보수가 좋아졌다는 부분을 확인할 수 있다.

import React, { useState } from 'react';
import styled from 'styled-components';
import { useSelector, useDispatch } from 'react-redux';

export default function App() {
  const number = useSelector((state) => state);
  console.log('Parents');
  return (
    <Container>
      <Text weight size="1.5rem">
        [Parents Component]
      </Text>
      <Text>
      </Text>
      <Text weight color="tomato">
        (Redux를 사용하는 경우)
      </Text>
      <Quantity>{`수량 : ${number}`}</Quantity>
      <Child1 />
    </Container>
  );
}

function Child1() {
  console.log('Child1');
  return (
    <Container>
      <Text>[Child 1 Component]</Text>
      <Child2 />
    </Container>
  );
}

function Child2() {
  console.log('Child2');
  return (
    <Container>
      <Text>[Child 2 Component]</Text>
      <Child3 />
    </Container>
  );
}

function Child3() {
  console.log('Child3');
  return (
    <Container>
      <Text>[Child 3 Component]</Text>
      <Child4 />
    </Container>
  );
}

function Child4() {
  const dispatch = useDispatch();
  const plusNum = () => {
    dispatch({ type: 'Plus' });
  };

  const minusNum = () => {
    dispatch({ type: 'Minus' });
  };
  console.log('Child4');
  return (
    <Container>
      <Text>[Child 4 Component]</Text>
      <Button onClick={plusNum}>👍</Button>
      <Button onClick={minusNum}>👎</Button>
    </Container>
  );
}
profile
Leejungmin

0개의 댓글