본문 바로가기
frontend/React

[React] React 로컬/전역 상태 관리 - React Hook(useState), React Store(Redux)

by 신림쥐 2024. 1. 24.
728x90

 

     


     

    개요

    React 애플리케이션에서 상태 관리는 컴포넌트 간의 데이터 흐름과 로직을 관리하는 데 핵심적인 역할을 합니다.

    React Store와 다양한 Hook을 활용하면 효율적으로 상태를 관리할 수 있습니다. 이번 포스팅에서는 React Store와 Hook의 개념, 그리고 상태 관리의 최적화를 위한 방법을 정리합니다.

     

    React Hook이란?

    React Hook은 함수형 컴포넌트에서 상태와 생명주기 메서드를 활용할 수 있게 도와주는 API입니다.

     

    useState

    • 역할: 컴포넌트 내에서 상태(state)를 관리.
    • 제한: 컴포넌트 내부에서만 사용 가능하며, 다른 컴포넌트와 상태를 공유할 수 없음.
    • 저장 방법: setState 메서드를 통해 상태 업데이트.
    • 저장 위치: 컴포넌트의 로컬 state에 저장.
    import React, { useState } from 'react';
    
    const Counter = () => {
      const [count, setCount] = useState(0);
    
      const increment = () => setCount(count + 1);
    
      return (
        <div>
          <p>Count: {count}</p>
          <button onClick={increment}>+1</button>
        </div>
      );
    };

     

    커스텀 훅 vs 유틸 함수: 어떤 것을 사용할까?

    • 커스텀 훅: React의 원칙과 생태계를 따르며 상태를 효율적으로 관리하기 위한 도구
      • 목적: 비즈니스 로직의 공통화와 상태 관리.
      • 특징: React 컴포넌트에서만 사용 가능하며, 렌더링과 상태 관리가 필요한 경우 사용
      • : 컴포넌트 간 상태 공유, API 호출 관리.
    import { useState, useEffect } from 'react';
    
    const useCounter = (initialValue = 0) => {
      const [count, setCount] = useState(initialValue);
    
      const increment = () => setCount((prev) => prev + 1);
    
      return { count, increment };
    };
    
    // 사용
    const Counter = () => {
      const { count, increment } = useCounter();
    
      return (
        <div>
          <p>Count: {count}</p>
          <button onClick={increment}>+1</button>
        </div>
      );
    };

     

    • 유틸 함수: 특정 로직을 재사용하기 위해 설계된 일반 JavaScript 함수
      • 목적: 코드 중복 감소와 재사용성 향상.
      • 특징: React의 생태계와 무관한 일반적인 로직이 필요한 경우, 모든 JavaScript 파일에서 호출 가능하다.
    // utils.js
    export const formatDate = (date) => date.toISOString().split('T')[0];
    
    // 사용
    import { formatDate } from './utils';
    
    const DateComponent = () => {
      const today = new Date();
    
      return <p>Today's Date: {formatDate(today)}</p>;
    };

     

     

    React Store 이란?

    애플리케이션 전역 상태를 관리하고, 상태를 여러 컴포넌트에서 공유할 수 있도록 하는 중앙 집중식 상태 관리 시스템으로

    Store는 주로 상태 관리 라이브러리(예: Redux, Zustand, MobX 등)에서 사용되는 개념입니다.

     

    • 정의: 애플리케이션의 상태(state)를 저장하고 관리하는 객체.
    • 역할:
      1. 상태 저장: 애플리케이션의 모든 상태를 한곳에 저장하여 컴포넌트 간 데이터 흐름을 중앙에서 관리합니다.
      2. 상태 공유: 컴포넌트 간 상태를 쉽게 공유할 수 있게 하여 데이터 흐름을 단순화합니다.
      3. 상태 변경 관리: 상태를 직접 수정하지 않고, 상태를 변경하려면 반드시 액션을 통해 Store에 알립니다.
    • Store의 주요 구성 요소
      • State (상태)
        • 현재 애플리케이션의 모든 상태 데이터를 포함합니다.
        • 상태는 읽기 전용이며, 직접 수정할 수 없습니다.
      • Action (행동)
        • 상태를 변경하는 데 필요한 의도를 나타내는 객체.
        • type 필드와 함께, 필요에 따라 추가적인 데이터를 포함합니다.
        • 예: { type: 'INCREMENT', payload: 1 }
      • Reducer (리듀서)
        • Action에 따라 상태를 업데이트하는 순수 함수.
        • 기존 상태와 Action을 입력으로 받아 새로운 상태를 반환합니다.
      • Dispatch
        • Action을 Store에 전달하여 상태를 업데이트하는 메서드.
        • 컴포넌트는 Dispatch를 사용해 상태를 변경하는 요청을 보냅니다.
      • Selector
        • Store에서 특정 데이터를 추출하기 위한 함수.
      • 상태를 컴포넌트에 전달하기 전에 필요한 데이터만 선택적으로 가져옵니다.

     

     

     

    상태 관리 라이브러리

    1. Redux

    • 특징:
      • 가장 널리 사용되는 상태 관리 라이브러리.
      • 중앙 집중식 상태 관리.
      • redux-toolkit을 사용하면 복잡한 설정을 단순화할 수 있음.
      • 미들웨어(redux-thunk, redux-saga)를 통해 비동기 로직 처리 가능.
    • 추천 이유:
      • 큰 규모의 애플리케이션에서 복잡한 상태 관리에 적합.
      • 강력한 디버깅 도구인 Redux DevTools 제공.

     

    2. Zustand

    • 특징:
      • 가볍고 사용하기 쉬운 상태 관리 라이브러리.
      • Redux보다 간결하며 보일러플레이트 코드가 적음.
      • React Context를 사용하지 않고 자체 상태 관리 구현.
    • 추천 이유:
      • 작은 규모의 프로젝트나 간단한 상태 관리를 위해 적합.
      • 성능이 뛰어나고 직관적인 API 제공.

     

    3. Recoil

    • 특징:
      • Facebook에서 개발한 라이브러리.
      • React와 깊게 통합되어 자연스럽게 동작.
      • "Atoms"와 "Selectors"를 통해 상태를 관리하고 파생 데이터 처리 가능.
    • 추천 이유:
      • React 컴포넌트 트리에 최적화된 상태 관리.
      • React와의 강력한 통합을 원하는 경우 적합.

     

    4. MobX

    • 특징:
      • 상태를 관찰 가능한(observable) 객체로 관리.
      • 자동으로 상태 변화를 감지하고 UI를 업데이트.
      • 적은 코드로 상태 관리를 구현할 수 있음.
    • 추천 이유:
      • 선언적이고 직관적인 방식으로 상태 관리.
      • 간단한 설정과 빠른 학습 곡선.

     

    5. Jotai

    • 특징:
      • 최소한의 API로 간단한 상태 관리 제공.
      • 상태를 "원자(atom)"로 나누어 관리.
      • React Context 없이 상태 공유 가능.
    • 추천 이유:
      • 초소형 라이브러리로 React 애플리케이션에 적합.
      • React의 hooks와 잘 맞음.

     

    6. Context API

    • 특징:
      • React에 내장된 상태 관리 기능.
      • 별도 라이브러리 설치 없이 간단한 상태 공유 가능.
      • 성능 최적화를 위해 메모이제이션 사용 필요.
    • 추천 이유:
      • 상태 관리가 복잡하지 않은 프로젝트에서 라이브러리 없이 바로 사용 가능.

     

    라이브러리 학습 곡선 보일러플레이트 커뮤니티 지원 비고
    Redux 높음 많음 매우 활발 대규모 애플리케이션
    Zustand 낮음 적음 활발 간단한 상태 관리, 소규모 프로젝트
    Recoil 중간 중간 활발 React 전용 프로젝트
    MobX 중간 적음 활발 반응형 상태 관리
    Jotai 낮음 적음 활발 간단한 상태 관리
    Context API 낮음 없음 React 커뮤니티 간단한 상태 관리

     

     

    Redux의 핵심 요소

    • Store: 애플리케이션의 전역 상태를 업데이트 및 저장
    • Reducer: 액션과 함께 상태 변경 로직을 처리하는 순수 함수
    • Dispatch: 액션을 통해 상태를 변경하기 요청하기 위한 트리거
    • Selector: 필요한 데이터를 Store에서 추출하여 컴포넌트에 전달

     

    1. Redux Store

    • 정의: Redux에서 상태를 저장하고 관리하는 객체.
    • 역할:
      1. 애플리케이션의 전역 상태를 저장.
      2. 상태를 읽기 위한 getState() 메서드 제공.
      3. 상태를 변경하기 위한 dispatch(action) 메서드 제공.
      4. 상태 변경을 구독하기 위한 subscribe(listener) 메서드 제공.
    • 특징
      • 단일성: Redux 애플리케이션에는 하나의 Store만 존재합니다.
      • 불변성: 상태는 직접 수정되지 않고, **액션(Action)**과 **리듀서(Reducer)**를 통해 새로운 상태 객체로 교체됩니다
      • 예측 가능성: 상태 변경이 항상 동일한 방식으로 이루어지므로 디버깅이 쉽습니다.

     

    2. Redux Reducer

    • 정의 : Reducer는 현재 상태와 액션을 입력받아, 새로운 상태를 반환하는 순수 함수입니다. Redux에서 상태 변경 로직은 모두 Reducer에서 처리됩니다.
    • 역할
      • 상태 변경 로직 처리: Reducer는 상태를 직접 변경하지 않고, 새로운 상태 객체를 반환합니다.
      • 상태의 불변성 유지: 이전 상태를 변경하지 않고, 복사한 상태에 필요한 변경을 적용합니다.
    • 특징:
      • 순수 함수로 작성되어야 하며, 동일한 입력에는 항상 동일한 출력이 보장됩니다.
      • 비동기 작업이나 부수효과를 포함해서는 안 됩니다.

     

    3. Redux Dispatch

    • 정의:dispatch는 Redux에서 상태 변경을 요청하는 메서드입니다. 액션 객체를 Store에 전달하여 리듀서를 통해 상태를 업데이트합니다.
    •  역할:
      1. 액션 전달: dispatch는 액션 객체를 Redux Store에 전달하여 상태 변경을 요청합니다.
      2. 리듀서 호출: Store는 전달받은 액션을 리듀서에 전달하여 상태 변경 로직을 처리합니다.
      3. 상태 변경 트리거: 새로운 상태가 Store에 저장되고, 변경된 상태를 구독하고 있는 컴포넌트들이 업데이트됩니다

    Dispatch의 동작 원리:

    1. 컴포넌트에서 dispatch(action)을 호출.
    2. 액션 객체가 Store로 전달됨.
    3. Store는 리듀서를 호출하여 새로운 상태를 생성.
    4. Store는 새로운 상태를 저장하고, 구독된 모든 리스너를 호출
    // 액션 생성 함수 정의
    const increment = () => ({
      type: 'INCREMENT',
    });
    
    // 컴포넌트에서 dispatch 사용
    import { useDispatch } from 'react-redux';
    
    const Counter = () => {
      const dispatch = useDispatch();
    
      const handleIncrement = () => {
        dispatch(increment()); // 'INCREMENT' 액션을 스토어에 전달
      };
    
      return <button onClick={handleIncrement}>+1</button>;
    };

     


    4. Redux Selector

    • 정의: Selector는 Redux Store에서 특정 데이터를 추출하고 컴포넌트에 전달하기 위한 함수입니다.
    • 역할:
      • 필요한 데이터 추출: Store에서 전역 상태의 특정 부분만 컴포넌트에 전달합니다.
      • 리렌더링 최적화: 불필요한 상태를 제외하여 컴포넌트가 필요하지 않은 상태 변화로 리렌더링되지 않도록 합니다.

    Selector 의 동작 원리:

    1. 구독 생성 : 컴포넌트가 Redux 스토어의 특정 상태에 대한 구독을 생성합니다.
    2. 상태 변경 감지 : Redux 상태가 변경되면 useSelector가 이전 상태와 새로운 상태를 비교합니다.
    3. 컴포넌트 리렌더링 : 상태 변경이 감지되면, 해당 상태를 구독 중인 컴포넌트를 다시 렌더링합니다.
    // 예: state 구조
    const initialState = {
      user: { name: 'Alice', age: 25 },
      posts: [{ id: 1, title: 'Redux Basics' }],
    };
    
    // Selector 함수 작성
    const selectUserName = (state) => state.user.name;
    
    // 컴포넌트에서 사용
    import { useSelector } from 'react-redux';
    
    const UserNameComponent = () => {
      const userName = useSelector(selectUserName); // 상태에서 사용자 이름 가져오기
      return <div>{userName}</div>;
    };

     

     

    결론

    React 애플리케이션에서 상태 관리와 로직의 효율성을 극대화하려면, 상황에 맞는 도구를 선택하는 것이 중요합니다.

    • 로컬 상태 관리는 useState와 같은 React Hook을 활용.
    • 전역 상태 관리는 Redux와 Selector를 통해 효율적으로 처리.
    • React 생태계에 포함된 로직은 커스텀 훅으로, 일반적인 재사용 로직은 유틸 함수로 구현하여 개발 효율성을 높이세요.
    728x90