[React] 상태 관리

play·2022년 7월 5일
0

React

목록 보기
6/9
post-thumbnail

Chapter1. 상태 관리

Chapter2. Redux

Redux : 컴포넌트와 상태를 분리하여 전역에서 상태 관리를 해줄 수 있게 해주는 상태 관리 라이브러리. React 애플리케이션을 개발할 때 Redux를 사용하면 React 컴포넌트 간의 복잡한 데이터 흐름을 따라갈 필요가 없어짐

Chapter1. 상태 관리

상태 : 변하는 데이터, UI에 동적으로 표현될 데이터

  • ex) "장바구니에 담기"와 같은 버튼을 눌러, 해당 물품을 장바구니에 추가
    [일반구매/정기배송]중 현재 선택된 탭이 무엇인지 나타내는 상태
    상품 선택 선택 여부, 상품 수량
  • 상태를 다룰 때 Side Effect(함수 또는 컴포넌트의 입력 외에도 함수의 결과에 영향을 미치는 요인. 네트워크 요청, API호출)는 주요 고려 대상.
  • fetch와 같은 API 요청이 없어도 컴포넌트는 작동되어야 한다. 어떤 데이터가 들어오는지 상관하지 않고 데이터가 fake데이터라도 표현할 수 있어야 한다. = presentation 컴포넌트

1. 로컬

특정 컴포넌트 안에서만 관리되는 상태. 보통 컴포넌트 내에서만 영향을 끼치며 다른 컴포넌트와 데이터를 공유하지 않는 폼(form)데이터는 대부분 여기에 속한다.
ex) input box, select box 등 입력값을 받는 경우

2. 전역

프로덕트 전체 혹은 여러 컴포넌트에서 관리되는 상태. 다른 컴포넌트와 상태를 공유하고 영향을 끼치는 상태. 전반에 영향을 미친다.

전역 상태에서의 데이터 무결성

동일한 데이터는 항상 같은 곳에서 데이터를 가지고 온다.
데이터 무결성 : 데이터의 정확성을 보장하기 위해 데이터의 변경이나 수정 시 제한을 두어 안정성을 저해하는 요소를 막고 데이터의 상태들을 항상 옳게 유지하는 것.

전역 상태 관리 Case

다크 모드 기능, 국제화 설정

상태 관리를 위한 각종 툴

  • React Context

  • Redux

  • MobX

  • 상태 관리 툴은 어떤 문제를 해결하는지?

    • 전역 상태를 위한 저장소를 제공
    • props drilling 이슈 해결 : A라는 컴포넌트에 상태가 있고, I라는 컴포넌트가 해당 상태를 사용한다고 하면, 그 중간에 존재하는 C, G 등은 굳이 name이라는 상태가 필요하지 않음에도, 컴포넌트에 props를 만들어 자식 컴포넌트에 넘겨주어야 했다. 이러한 props drilling(프로퍼티 내려꽂기) 문제는 **전역 상태 저장소가 있고, 어디서든 해당 저장소에 접근할 수 있다면 해결될 것
  • 상태 관리 툴이 반드시 필요한 것은 아니다. 대부분 React 공식 문서의 "React로 사고하기"를 통해 문제를 해결할 수 있다.

  • 상태가 어디에 위치해야 하는지를 익히자.


Chapter2. Redux

컴포넌트들의 상태 관련 로직을 다른 파일들로 분리시켜 효율적으로 관리하게 해주는 상태 관리 라이브러리

Redux 사용 이유

  • React 예시를 들자면 컴포넌트3, 컴포넌트6에서만 사용되는 상태는 -> React 데이터 흐름에 따라 최상위 컴포넌트에 위치시키는 게 적절 -> 하지만 이런 상태 배치는 다음과 같은 이유로 비효율적임

    • 해당 상태를 직접 사용하지 않는 최상위 컴포넌트, 상위 컴포넌트도 상태 데이터를 갖게 됨
    • 상태 끌어올리기, Props 내려주기를 여러 번 해야 함
    • 애플리케이션이 복잡해질수록 데이터 흐름도 복잡해짐
    • 컴포넌트 구조가 바뀐다면 지금의 데이터 흐름을 완전히 바꿔야 할 수도 있음
      ---> 상태 관리 라이브러리 Redux는 전역 상태를 관리할 수 있는 저장소 Store를 제공함으로써 이 문제들을 해결함
  • 하나의 root에 하나의 store만 존재 -> 관리하기 쉬움

  • 순수함수를 사용하기 때문에 상태를 예측할 수 있다

  • props 내려주기

Redux 구조

상태 관리

  1. 상태가 변경되어야 하는 이벤트가 발생하면, 변경될 상태에 대한 정보가 담긴 Action 객체가 생성
  2. 이 Action 객체는 Dispatch 함수의 인자로 전달됨
  3. Dispatch 함수는 Action 객체를 Reducer 함수로 전달함
  4. Reducer 함수는 Action 객체의 값을 확인하고, 그 값에 따라 전역 상태 저장소 Store의 상태를 변경함
  5. 상태가 변경되면, React는 화면을 다시 렌더링

--> 즉, Redux에서는 Action → Dispatch → Reducer → Store 순서로 데이터가 단방향으로 흐른다.

Action

어떤 액션을 취할 것인지 정의해 놓은 객체

// payload가 필요 없는 경우
{ type: 'INCREASE' }

// payload가 필요한 경우
{ type: 'SET_NUMBER', payload: 5 }

type : 해당 Action 객체가 어떤 동작을 하는지 명시하는 역할. Snake Case로 작성. 필요에 따라 payload를 작성해 구체적인 값을 전달함

액션 생성자(Action Creater) : Action 객체를 생성하는 함수. 파라미터를 받아와서 액션 객체 형태로 만들어준다.
Action을 직접 작성하기보단 이 방법을 많이 사용

예시

// payload가 필요 없는 경우
const increase = () => {
  return {
    type: 'INCREASE'
  }
}

// payload가 필요한 경우
const setNumber = (num) => {
  return {
    type: 'SET_NUMBER',
    payload: num
  }
}

Dispatch

Reducer로 Action을 전달해주는 함수. Dispatch의 전달인자로 Action 객체가 전달된다.

// Action 객체를 직접 작성하는 경우
dispatch( { type: 'INCREASE' } );
dispatch( { type: 'SET_NUMBER', payload: 5 } );

// 액션 생성자(Action Creator)를 사용하는 경우
dispatch( increase() );
dispatch( setNumber(5) );

// Action 객체를 전달받은 Dispatch 함수는 Reducer를 호출.

Reducer

Dispatch에게서 전달받은 Action 객체의 type 값에 따라서 상태를 변경시키는 함수. 순수함수여야 함.
현재의 상태와, 전달받은 액션 두 가지 파라미터를 받아온다.

const count = 1

// Reducer를 생성할 때에는 초기 상태를 인자로 요구.
const counterReducer = (state = count, action) {

  // Action 객체의 type 값에 따라 분기하는 switch 조건문
  switch (action.type)

    //action === 'INCREASE'일 경우
    case 'INCREASE':
			return state + 1

    // action === 'DECREASE'일 경우
    case 'DECREASE':
			return state - 1

    // action === 'SET_NUMBER'일 경우
    case 'SET_NUMBER':
			return action.payload

    // 해당 되는 경우가 없을 땐 기존 상태를 그대로 리턴
    default:
      return state;
}
// Reducer가 리턴하는 값이 새로운 상태가 된다.

combineReducers : 여러 개의 Reducer를 사용하는 경우, 하나의 Reducer로 합쳐줌

import { combineReducers } from 'redux';

const rootReducer = combineReducers({
  counterReducer,
  anyReducer,
  ...
});

Store

상태가 관리되는 오직 하나뿐인 저장소의 역할, Redux 앱의 state가 저장되어 있는 공간

createStore : Reducer를 연결해서 Store를 생성하는 메서드

import { createStore } from 'redux';

const store = createStore(rootReducer);
  • 구독(Subscribe) : 스토어 내장함수. 함수 형태의 값을 파라미터로 받아옴. 특정 함수를 파라미터로 넣어 호출하면 상태가 업데이트될 때마다(액션이 디스패치 되었을 때마다) 함수가 호출됨. 일종의 이벤트 리스너다.
  • dispatch : 디스패치도 스토어 내장함수.

Redux Hooks

Action, Reducer, Dispatch, Store를 연결.
Redux Hooks는 React에서 Redux를 사용할 때 활용할 수 있는 Hooks 메서드를 제공한다.

1) useSelector() : 컴포넌트와 state를 연결하여 Redux의 state에 접근할 수 있게 해주는 메서드

// Redux Hooks 메서드는 'redux'가 아니라 'react-redux'에서 불러온다. 
import { useSelector } from 'react-redux'
const counter = useSelector(state => state.counterReducer)
console.log(counter) // 1

2) useDispatch() : Action 객체를 Reducer로 전달해 주는 메서드

import { useDispatch } from 'react-redux'

const dispatch = useDispatch()
dispatch( increase() )
console.log(counter) // 2

dispatch( setNumber(5) )
console.log(counter) // 5

Redux 3가지 원칙

  1. Single source of truth
    동일한 데이터는 항상 같은 곳에서 가지고 와야 한다.
    = Redux에는 데이터를 저장하는 Store라는 단 하나뿐인 공간이 있음. 하나의 애플리케이션 안에는 하나의 스토어만 사용하자. 이렇게 하면 애플리케이션 디버깅이 쉬워지고 서버와 직렬화가 될 수 있고 클라이언트에서 데이터를 쉽게 받아들여올 수 있다.

  2. State is read-only
    상태는 읽기 전용이라는 뜻으로, React에서 상태갱신함수로만 상태를 변경할 수 있었던 것처럼,
    = Redux의 상태도 직접 변경할 수 없고, Action 객체가 있어야만 상태를 변경할 수 있음

  3. Changes are made with pure functions
    변화를 일으키는 리듀서 함수는 순수함수여야한다.
    1) 리듀서 함수는 이전 상태와 액션 객체를 파라미터로 받음
    2) 파라미터 외의 값에는 의존하면 안 됨
    3) 이전 상태는 건들지 않고, 변화를 준 새로운 상태 객체를 만들어서 리턴
    4) 똑같은 파라미터로 호출된 리듀서 함수는 늘 같은 결과 값을 반환해야 함.

When should I use Redux?

  • 프로젝트 규모가 크고 많은 사람들에 의해 코드가 관리될 때
  • 앱의 여러 위치에서 필요한 많은 양의 상태들이 존재할 때
  • 상태가 자주 업데이트 될 때
  • 상태를 업데이트 하는 로직이 복잡할 때
  • 상태의 업데이트를 확인해야 할 때
profile
블로그 이사했습니다 🧳

0개의 댓글