team logo icon

리액트 상태관리?!

리액트 상태관리란?

좋은 상태관리란?




리액트에서의 좋은 상태 관리란

애플리케이션의 다양한 상태(데이터)를 효율적이고 체계적으로 관리하는 것

=> 애플리케이션의 예측 가능성, 유지보수성, 그리고 확장성을 향상!!


  1. 예측 가능성: 상태의 변화가 명확한 로직에 따라 일어나 애플리케이션의 동작을 예측하기 쉽습니다.

  2. 모듈성: 상태 관리 로직이 특정 컴포넌트에 과도하게 결합되지 않고, 필요에 따라 재사용 가능합니다.

  3. 디버깅 용이성: 상태 변화의 흐름을 쉽게 추적하고 디버깅할 수 있습니다.

  4. 컴포넌트의 독립성: 각 컴포넌트가 필요한 상태만을 관리하며, 전역 상태는 필요한 컴포넌트에만 주입됩니다.


Props Drilling이란?


Props Driling은 상위에 있는 Component1에서 Component4stateprops를 통해 전달할 때 Component2Component3Props를 전달하는 용도로만 쓰면서 데이터를 전달하는 현상을 말합니다


➡ 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>
  );
}

즉, App 컴포넌트에서 상태를 변경하는 함수를 최하위 컴포넌트인 Child4까지 전달하기 위해 여러 중간 컴포넌트를 거쳐야 합니다. 이러한 방식은 유지 관리가 어렵고, 컴포넌트 간의 의존성을 높이는 단점이 있습니다.


props drilling 해결 예시


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>
  );
}
//useDispatch 훅을 사용하여 Redux 스토어의 dispatch 함수에 접근합니다. 이를 통해 액션을 디스패치할 수 있습니다. plusNum과 minusNum 함수는 각각 type이 'Plus'와 'Minus'인 액션 객체를 디스패치합니다. 이러한 액션들은 Redux 리듀서에서 처리!!


상태관리 라이브러리 Redux를 활용하여 해결한 예시~~

useSelector와 useDispatch를 사용하여 Child4 컴포넌트에만 state를 전달해주었으며 코드의 가독성과 유지보수가 좋아졌다는 부분을 확인할 수 있습니다..



그렇다면 Redux란?


Redux는 JavaScript 애플리케이션의 상태 관리를 위한 도구입니다. 주로 React와 같은 라이브러리나 프레임워크와 함께 사용되지만, 순수 JavaScript 또는 다른 UI 라이브러리와도 함께 사용할 수 있습니다. Redux는 애플리케이션의 전역 상태를 관리하는 중앙 저장소(Store)를 제공하여, 다양한 컴포넌트 간의 상태 공유와 관리를 용이하게 합니다.

Redux의 핵심 개념

  1. Store: 애플리케이션의 전역 상태를 저장하는 객체입니다. Redux에서는 하나의 애플리케이션에 단 하나의 스토어만 가집니다.

  2. Action: 상태 변화를 일으키는 일이 무엇인지 설명하는 객체입니다. 액션 객체는 주로 type 필드를 포함하며, type은 상태 변화를 설명하는 문자열입니다. 필요에 따라 추가 데이터를 포함할 수도 있습니다.

  3. Reducer: 액션을 받아 이전 상태를 다음 상태로 변환하는 순수 함수입니다. 리듀서는 (previousState, action) => newState 형태의 함수로, 이전 상태와 액션을 인자로 받아 새로운 상태를 반환합니다.

  4. Dispatch: 액션을 스토어에 보내는 과정입니다. 이 과정을 통해 애플리케이션의 상태가 업데이트됩니다.

    https://velog.io/@s_soo100/Redux-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%A6%AC%EB%8D%95%EC%8A%A4-%EC%8A%A4%ED%84%B0%EB%94%941


렌더링을 효과적으로 관리하려면??


  1. 컴포넌트 분리와 재사용:

    • 관련 있는 UI 부분을 컴포넌트로 분리하여 재사용함으로써 코드의 가독성을 높이고, 불필요한 랜더링을 줄일 수 있습니다.

  2. React.memo:

    • 함수 컴포넌트에서 React.memo를 사용하면 컴포넌트의 props가 변경되지 않았을 경우 불필요한 랜더링을 방지할 수 있습니다.

  3. Hooks(useState, useMemo, useCallback)를 사용한 최적화:

    • useState의 함수형 업데이트를 사용하여 불필요한 객체 생성을 피할 수 있습니다.

    • useMemouseCallback을 사용하여 계산 비용이 많은 연산과 함수의 재생성을 방지할 수 있습니다.

  4. 상태 관리 라이브러리 사용:

    • 상태 관리 라이브러리(예: Redux, MobX)를 사용하여 애플리케이션의 상태를 효율적으로 관리할 수 있습니다. 이는 특히 복잡한 상태 로직이나 다수의 컴포넌트 간 상태 공유가 필요할 때 유용합니다.


따라서 애플리케이션의 특정 부분에서 발생하는 성능 문제를 진단하기 위해 리액트 개발자 도구와 같은 성능 분석 도구를 활용하는 것이 좋습니다.

https://cocoder16.tistory.com/36

최신 아티클
lighthouse에 대해
문성희
|
2024.05.13
lighthouse에 대해
lighthouse에 대해
prettier, eslint, styleLint에 대해
이진
|
2024.05.10
prettier, eslint, styleLint에 대해
4주차 공유과제
Article Thumbnail
박채연
|
2024.05.10
Prettier, ESLint, StyleLint
prettier, eslint, stylelint