프론트엔드 개발/React

리액트) state의 모든 것 (props/state/로컬/전역/서버상태)

Ella Seon 2023. 8. 16. 22:10

0. props와 state

props state
- 부모 컴포넌트에서 자식컴포넌트로 전달되는 읽기전용 데이터
- 자식은 props를 사용해서 부모에게 데이터를 건네줄 수 없다. 부모가 props로 setState를 넘겨주면 자식이 그 함수를 이용해 값을 건네주는 방식이다(Lifting State UP)
- 컴포넌트 내부에서 관리되며, 변할 수 있는 데이터
- state의 변경은 컴포넌트의 재렌더링을 유발한다.(웹 애플리케이션을 렌더 하는데 있어 영향을 미칠 수 있는 값)

 

🤔 props는 읽기전용이라는데 자식컴포넌트에서 상태 바꾸고 싶으면 어떻게 함? (Lifting State Up) 더보기 누르기

더보기

🔸Lifting State Up (State 끌어올리기)

1) 부모컴포넌트에서 'useState' 사용해서 state 관리

2) 자식 컴포넌트로 state와 setState 함수 전달

3) 자식 컴포넌트에서 전달받은 상태 변경 함수를 호출해서 부모 컴포넌트의 상태를 간접적으로 변경할 수 있음

 

아래는 예제코드

import React, { useState } from 'react';
import ChildComponent from './ChildComponent';

function ParentComponent() {
  const [data, setData] = useState("Initial Data");

  return (
    <div>
      <h1>Parent Component</h1>
      <ChildComponent data={data} setData={setData} />
    </div>
  );
}

// ChildComponent.js
import React from 'react';

function ChildComponent(props) {
  const handleChange = () => {
    props.setData("Data changed by Child!");
  };

  return (
    <div>
      <h2>Child Component</h2>
      <p>Data from Parent: {props.data}</p>
      <button onClick={handleChange}>Change Data</button>
    </div>
  );
}

export default ChildComponent;

1. 상태의 종류

🔸범위에 따라서 보는 상태

local(지역상태) global(전역상태)
- 특정 컴포넌트 안에서만 관리되는 상태
- 하위 컴포넌트에 데이터를 전달하기 위해 props를 사용하는데 어플리케이션이 클수록 props drilling 이 증가하여 반복작업이 많아지고 유지보수가 어려워지는 단점이 있음
- 프로덕트 전체, 혹은 여러 컴포넌트에서 관리되는 상태
(리덕스, MobX,Recoil,Context API)
- props 과정이 생략되어, 작업이 적고 유지보수가 간편해지는 장점이 있으나, 상태관리 오류시 나타나는 사이드 이펙트가 커지는 단점이 있음
예제 : form 데이터 ( input , select 등과 같이 입력값을 받는 경우) , 토글 버튼의 상태 다크모드 / 라이트모드 / 언어 설정모드 / 사용자 로그인 정보

 

🔸역할에 따라서 보는 상태

UI 상태 서버 캐시 상태 Form 상태 URL 상태
컴포넌트의 시각적 표현
직접적으로 연관된 상태
서버로부터 데이터를 가져와 캐싱해놓는 상태 입력폼의 상태관리
(Form 의 로딩, Submitting, disabled, validation 등등 데이터)
URL 에 따른 애플리케이션의 상태

(주로 라우팅 라이브러리와 연계하여 사용되며, URL 의 변경에 따라 특정 UI나 데이터를 표시하는데 사용)
1) 모달의 열림/닫힘
2) 탭의 선택 상태
3) 드롭다운 메뉴의 표시
4) 애니메이션
API 요청, 결과를 저장, 복수의 위치에서 사용 1) 로그인 폼의 이메일과 비밀번호 입력 상태
2) 회원가입 폼의 유효성 검사 상태
1) /users/1의 URL로 접근했을 때 1번 사용자의 프로필을 보여주는 상태
2) 쿼리 파라미터나 해시를 기반으로 한 필터링 상태 등.

2. 상태 관리

- 상태 설계는 개발자마다 중요하게 생각하는 부분이 다르기 때문에 정답은 없다. 상태 설계는 한번 설계되어 프로젝트가 진행되면 고치기 쉽지 않다. 따라서 상태 관리에 대한 많은 고민을 하고 프로젝트를 시작해야한다. 잘못된 상태 설계는 성능이슈는 물론 유지보수 관점에서도 큰 영향을 끼친다.

🤔상태관리가 왜 중요할까?
1) 불필요한 렌더링을 줄여 성능 최적화와 사용자 경험을 향상시킬 수 있다. 
2) 유지보수를 효율적으로 할 수있다.
더보기

1) 불필요한 렌더링을 줄일 수 있다.

 🔸불필요한 렌더링을 피해야 하는 이유

- 성능 저하 : 큰 리스트나 테이블 같은 많은 양의 데이터를 보여주는 컴포넌트가 불필요하게 자주 렌더링 될 경우 애플리케이션의 반응속도가 저하될 수 있다.  더불어 메모리 사용량을 증가시키며, 가비지 컬렉션에도 추가 부하를 주게되어 전반적인 성능에 영향을 준다.

- 배터리 소모 증가 : 웹 앱에서 불필요한 렌더링이 자주 발생하면 CPU 사용량이 증가해서 배터리 소모가 빠르게 될 수 있다. 

- 사용자 경험 저하 : 애니메이션, 스크롤, 입력 등의 사용자 인터렉션 중 불필요한 렌더링이 발생하면 동작이 끊기는 것 처럼 보여서 부드럽지 않게 느껴질 수 있다. 

- 네트워크 트래픽 발생 : 렌더링 과정에서 Side Effect(ex: API 호출) 포함되어있을 경우 렌더링마다 API 호출을 여러번 발생 시킬 수 있어 서버에 부담이 간다. 

 

 🔸리액트가 렌더링되는 조건

1) state 변경이 있을 때 

2) 부모 컴포넌트로부터 새 props 가 들어오면 자식 컴포넌트는 재 렌더링된다.

3) 부모 컴포넌트로 부터 받은 props가 변경되면 props값을 받은 자식 컴포넌트도 재 렌더링 된다. 

4) 부모 컴포넌트가 업데이트 되어 재렌더링 되면 자식 컴포넌트도 재렌더링된다.

 

 🔸불필요한 렌더링 예제

function App() {
  const [name, setName] = useState('John');
  const [age, setAge] = useState(30);

  return (
    <div>
      <Profile name={name} age={age} />
      <button onClick={() => setName('Jane')}>Change Name</button>
      <button onClick={() => setAge(age + 1)}>Increase Age</button>
    </div>
  );
}


function Profile({ name, age }) {
  console.log('Profile 컴포넌트 렌더링!');
  return (
    <div>
      <p>Name: {name}</p>
      <p>Age: {age}</p>
    </div>
  );
}

여기서 "Change Name" 버튼을 클릭하면 이름만 변경되어야 하지만 Profile 컴포넌트 전체가 불필요하게 다시 렌더링된다. App 컴포넌트 내에서 setName 함수를 호출하면 'name' 상태가 변경된다. name 상태 변경으로 인해 App 컴포넌트가 다시 렌더링 되고, 이 과정에서 Profile 컴포넌트에 전달하는 name prop 도 변경된다. 이 변경된 name prop 때문에 Profile 컴포넌트도 다시 렌더링 되는것이다. 이 때, Profile 컴포넌트의 age prop은 변경되지 않았지만, 리액트의 기본 동작 방식에 따라 Profile 컴포넌트는 name prop의 변경으로 인해 전체적으로 다시 렌더링 된다.

 

🔸불필요한 렌더링 해결책

 

 

2) 유지보수를 효율적으로 할 수 있다.

- 잘못된 상태구조는 데이터 흐름을 파악하기 어렵게 만들어 코드를 수정하는데 오랜시간이 걸릴 수 있음

- 상태가 특정 컴포넌트에 과도하게 결합되어있다면 재사용하기 어려워진다.

- 상태가 여러 곳에서 변경될 수 있거나 상태변경 로직이 복잡할 경우 버그 발생 확률이 높아진다.

 

🔸좋은 상태관리란?

1️⃣전역상태는 언제 필요할까?

- 다수의 컴포넌트간에 상태 의존성이 높아질때 -> 즉, 다른 컴포넌트와 상태를 공유하고 영향을 끼치는 상태

- 언어,다크모드 같은 변화가 잦지 않고 서비스 전반에 걸친상태

 

🤔그렇다면 왜 다수의 컴포넌트에 영향을 미칠때 전역상태로 관리하는 것이 좋을까?

 

1) Single source of truth(신뢰할 수 있는 단일 출처) : 동일한 데이터는 항상 같은 곳에서 데이터를 가지고오자

2) 전역상태에서의 ‘데이터의 무결성’  : 데이터의 정확성을 보장하기 위해 데이터의 변경이나 수정 시 제한을 두어 안정성을 저해하는 요소를 막고 데이터 상태들을 항상 옳게 유지하는 것

https://bbeeyaks-moment.tistory.com/entry/section3Unit4-React-%EC%83%81%ED%83%9C-%EA%B4%80%EB%A6%AC111

즉, 서로 다른 컴포넌트가 동일한 상태를 다룬다면 출처는 오직 한곳이어야 한다. (바로 전역공간!)

출처가 한곳이라면 데이터가 변할 때 여러곳에서 동기화를 할 필요가 없다. 하지만, 사본이 있을 경우 데이터가 변경될때마다 변경사항을 모든 사본에 반영해야한다. 이는 에러가 발생하기 쉽고 코드 유지보수도 어렵게 만든다. 

 

※ 동기화 : 한곳에서 상태가 변경되면, 데이터의 변화를 모든곳에서 일관적으로 반영하는 것, 

 

전역상태 저장소에서 다크모드를 관리할 경우 모든 컴포넌트에게 light 라는 상태를 일관적으로 뿌릴 수 있어서 동기화를 할 필요가 없지만,


각 로컬에서 light 상태를 관리할 경우
모든 컴포넌트를 light 로 바꿔주는 데이터 일치, 즉 동기화 과정이 필요하다

 

2️⃣서버상태를 전역상태로 관리해도 되는걸까...?

- 서버상태를 관리하는 라이브러리(React-Query, SWR) 가 없었을 때는  API 호출을 통해 받아온 서버 상태를 전역상태에 저장해서 관리했었다. 

- 클라이언트와 서버사이의 데이터 불일치 문제

 전역상태에서 서버상태를 캐싱하게되면(클라이언트에서 서버의 데이터를 가져와 로컬상태에 저장하면) 서버 상태가 특정 시점에 캡쳐된다. 캡처된다는 건 특정 시점의 서버상태를 말한다.

(ex: 오전 10시에 서버에서 데이터를 가져와 클라이언트에 저장했다면, 오전 10시의 서버 데이터 상태를 반영함. 이후 서버 데이터가 변경되더라도 클라이언트의 캐싱된 데이터는 그 변경사항을 알 수 없다)

서버 데이터를 캐싱한 클라이언트의 상태를 인터렉션에 따라 update해서 서버 데이터와 동기화 한다고 해도 시간이 지남에 따라서 본질적으로는 다른데이터가 된다. 

 

따라서, 전역상태엥서 서버 상태를 관리하지 말고 React-Query 등과 같은 서버 캐싱 전용 라이브러리 사용을 권장한다.


참고자료

https://www.stevy.dev/react-state-management-guide/

 

리액트 상태 관리 가이드

Stevy의 개발 블로그 입니다.

www.stevy.dev

https://bbeeyaks-moment.tistory.com/entry/section3Unit4-React-%EC%83%81%ED%83%9C-%EA%B4%80%EB%A6%AC111

 

section3/Unit4/ [React] 상태 관리(11/1)

블로깅 주제 상태관리 1. 지금 현재, 당신의 기분이나 느낌을 표현해 주세요. 리액트 커스텀 컴포넌트 어드벤스드는 매우 어려웠다... 어제 밤에 탈탈 털리고 잤다^^7 2. 오늘 무엇을 학습한 내용

bbeeyaks-moment.tistory.com

https://medium.com/@yujso66/%EB%B2%88%EC%97%AD-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%83%81%ED%83%9C-%EA%B4%80%EB%A6%AC%EC%9D%98-%EC%83%88%EB%A1%9C%EC%9A%B4-%ED%9D%90%EB%A6%84-6e5ed0022e39

 

[번역] 리액트 상태 관리의 새로운 흐름

상태 관리 라이브러리가 해결하고자 하는 핵심 문제를 이해합시다. 그리고 최근에 확산된 모던 라이브러리들이 어떤 방식으로 문제를 해결하고 있는지도 확인해봅시다.

medium.com

https://www.youtube.com/watch?v=gER3iJZyd0A