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)를 저장하고 관리하는 객체.
- 역할:
- 상태 저장: 애플리케이션의 모든 상태를 한곳에 저장하여 컴포넌트 간 데이터 흐름을 중앙에서 관리합니다.
- 상태 공유: 컴포넌트 간 상태를 쉽게 공유할 수 있게 하여 데이터 흐름을 단순화합니다.
- 상태 변경 관리: 상태를 직접 수정하지 않고, 상태를 변경하려면 반드시 액션을 통해 Store에 알립니다.
- Store의 주요 구성 요소
- State (상태)
- 현재 애플리케이션의 모든 상태 데이터를 포함합니다.
- 상태는 읽기 전용이며, 직접 수정할 수 없습니다.
- Action (행동)
- 상태를 변경하는 데 필요한 의도를 나타내는 객체.
- type 필드와 함께, 필요에 따라 추가적인 데이터를 포함합니다.
- 예: { type: 'INCREMENT', payload: 1 }
- Reducer (리듀서)
- Action에 따라 상태를 업데이트하는 순수 함수.
- 기존 상태와 Action을 입력으로 받아 새로운 상태를 반환합니다.
- Dispatch
- Action을 Store에 전달하여 상태를 업데이트하는 메서드.
- 컴포넌트는 Dispatch를 사용해 상태를 변경하는 요청을 보냅니다.
- Selector
- Store에서 특정 데이터를 추출하기 위한 함수.
- 상태를 컴포넌트에 전달하기 전에 필요한 데이터만 선택적으로 가져옵니다.
- State (상태)
상태 관리 라이브러리
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에서 상태를 저장하고 관리하는 객체.
- 역할:
- 애플리케이션의 전역 상태를 저장.
- 상태를 읽기 위한 getState() 메서드 제공.
- 상태를 변경하기 위한 dispatch(action) 메서드 제공.
- 상태 변경을 구독하기 위한 subscribe(listener) 메서드 제공.
- 특징
- 단일성: Redux 애플리케이션에는 하나의 Store만 존재합니다.
- 불변성: 상태는 직접 수정되지 않고, **액션(Action)**과 **리듀서(Reducer)**를 통해 새로운 상태 객체로 교체됩니다
- 예측 가능성: 상태 변경이 항상 동일한 방식으로 이루어지므로 디버깅이 쉽습니다.
2. Redux Reducer
- 정의 : Reducer는 현재 상태와 액션을 입력받아, 새로운 상태를 반환하는 순수 함수입니다. Redux에서 상태 변경 로직은 모두 Reducer에서 처리됩니다.
- 역할
- 상태 변경 로직 처리: Reducer는 상태를 직접 변경하지 않고, 새로운 상태 객체를 반환합니다.
- 상태의 불변성 유지: 이전 상태를 변경하지 않고, 복사한 상태에 필요한 변경을 적용합니다.
- 특징:
- 순수 함수로 작성되어야 하며, 동일한 입력에는 항상 동일한 출력이 보장됩니다.
- 비동기 작업이나 부수효과를 포함해서는 안 됩니다.
3. Redux Dispatch
- 정의:dispatch는 Redux에서 상태 변경을 요청하는 메서드입니다. 액션 객체를 Store에 전달하여 리듀서를 통해 상태를 업데이트합니다.
- 역할:
- 액션 전달: dispatch는 액션 객체를 Redux Store에 전달하여 상태 변경을 요청합니다.
- 리듀서 호출: Store는 전달받은 액션을 리듀서에 전달하여 상태 변경 로직을 처리합니다.
- 상태 변경 트리거: 새로운 상태가 Store에 저장되고, 변경된 상태를 구독하고 있는 컴포넌트들이 업데이트됩니다
Dispatch의 동작 원리:
- 컴포넌트에서 dispatch(action)을 호출.
- 액션 객체가 Store로 전달됨.
- Store는 리듀서를 호출하여 새로운 상태를 생성.
- 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 의 동작 원리:
- 구독 생성 : 컴포넌트가 Redux 스토어의 특정 상태에 대한 구독을 생성합니다.
- 상태 변경 감지 : Redux 상태가 변경되면 useSelector가 이전 상태와 새로운 상태를 비교합니다.
- 컴포넌트 리렌더링 : 상태 변경이 감지되면, 해당 상태를 구독 중인 컴포넌트를 다시 렌더링합니다.
// 예: 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