목차
1. Redux란?
Flux개념을 바탕으로한 React에서 현재 가장 많이 사용되는 State 관리 라이브러리 입니다.
Flux는 Facebook에서 클라이언트-사이드 웹 어플리케이션을 만들기 위해 사용하는 어플리케이션 아키텍쳐이다.
Flux Architecture는 MVC 패턴의 문제점을 보완할 목적으로 고안되었다.
페이스북과 같은 대규모 어플리케이션에서는 MVC가 너무 복잡했다.
이 같은 문제의 대표적인 사례가 바로 페이스북의 안 읽은 글 갯수(unread count) 표시이다. 사용자가 읽지 않았던 글을 읽으면 읽지 않은 글 갯수에서 읽은 글 수만큼 빼면 되는 일견 단순해보이는 기능인데도, 페이스북 서비스에서 이를 MVC로 구현하기는 어려웠다고 한다. 어떤 글을 '읽음' 상태로 두면, 먼저 글을 다루는 thread 모델을 업데이트 해야하고 동시에 unread count 모델도 업데이트 해야한다. 대규모 MVC 애플리케이션에서 이 같은 의존성과 순차적 업데이트는 종종 데이터의 흐름을 꼬이게 하여 예기치 못한 결과를 불러일으킨다.
결국 페이스북 개발팀은 MVC를 버리고 다른 아키텍처를 적용하기로 한다.
그것이 바로 Flux이다.
참고 : https://velog.io/@pjj186/Redux
리덕스 데이터 전달 방식은 아래 그림과 같다.
아래 데이터 전달 방식만 봐도 Redux를 사용하므로 얼마나 편리해지는지 알 수 있다.
Redux를 사용하지 않을 시, 컴포넌트의 깊이가 깊어질수록, 전달하는 과정이 점점 길어진다.
하지만 Redux를 사용하면, store에서 상태를 저장하고 필요시 꺼내서 쓰기 때문에
복잡한 로직을 사용하지 않아도 된다.
2. Redux 주요 개념
Action
- 어떠한 이벤트, 동작
- 객체형태
- dispatch의 파라미터로 전달됨.
- type 프로퍼티 필요
{
type : 'ADD_TODO',
data: {
id: 1,
text: '리덕스 액션1'
}
}
Action Creator
- 선택적으로 사용
- 유지보수를 더 쉽게 하기 위해서.
export function addToro(data){
return {
type: "ADD_TODO",
data
}
}
Dispatch
- 스토어의 내장함수 중 하나.
- 액션 발생시킬떄 사용
- 액션을 파라미터로 전달함.
Store
- 한 어플리케이션 당 하나의 스토어 생성
- 상태 저장
Reducer
- 변화를 일으키는 함수
const initialState = {
todo: [
{
id: 1,
text: '할 일',
},
{
id: 2,
text: '할 일',
},
]
}
// state가 undefined일 때는 initialState를 기본값으로 사용
function reducer(state = initialState, action) {
switch (action.type) {
case ADD_TODO:
return {
...state, // 불변성 유지 (spread 연산자 사용 예시)
todo: state.todo.concat({
id: action.id,
text: action.text,
isCompleted: false,
}),
};
default:
return state;
}
}
참고 :https://redux.js.org/tutorials/fundamentals/part-2-concepts-data-flow
3. 리덕스는 언제 필요할까?
상태관리를 편하게 해주는 Redux, 무조건 적용해야될까?
“But it’s probably not going to be the best or most efficient tool at any of those use cases. Other tools like React Query or Apollo are much more specialized for this specific case of data fetching.”
⇒ 리덕스가 꼭 최선이 아닐수도 있다.
리덕스가 필요한 상황
- 어플리케이션 내 여러 곳에서 사용해야될 state(상태)가 있을때
- state가 자주 업데이트 될 때
- 해당 state를 업데이트하는 로직이 복잡할 때
- 큰 프로젝트로 많은 사람들과 공동작업을 진행할 때
- 시간이 지남에 따라 state가 어떻게 업데이트 되는지 봐야될 때
참고: https://changelog.com/posts/when-and-when-not-to-reach-for-redux
4. Redux-Toolkit에 대해서
Redux Toolkit (줄여서 "RTK")은 Redux 로직을 작성하기 위해 Redux 공식적으로 추천하는 방법입니다.
Redux의 문제점 보완을 위해 등장
Redux의 문제점
- 저장소 구성의 복잡성
- 많은 패키지 필요성(의존성)
- 한 작업 시 필요한 수 많은 코드양(boilerplate)
Redux-Toolkit 특징
- Simple: 스토어 설정, 리듀서 생성, 불변성 업데이트 로직 사용을 편리하게 하는 기능 포함
- Opitionated: 스토어 설정에 관한 기본 설정 제공, 일반적으로 사용되는 redux addon이 내장
- Powerful : Immer에 영감을 받아 '변경'로직으로 '불변성'로직 작성 가능, state 전체를 slice로 자동으로 만들 수 있음
- Effective : 적은 코드에 많은 작업을 수행 가능
참고 :
(기존 redux 코드 vs redux toolkit 사용한 코드 정리가 잘 되어있음)
https://velog.io/@djaxornwkd12/Redux-Toolkit-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0
4-1. Redux-Toolkit 폴더 구조
리덕스 공식문서에서 추천하는 폴더 구조
“Structure Files as Feature Folders with Single-File Logic
Redux itself does not care about how your application's folders and files are structured. However, co-locating logic for a given feature in one place typically makes it easier to maintain that code.
Because of this, we recommend that most applications should structure files using a "feature folder" approach (all files for a feature in the same folder). Within a given feature folder, the Redux logic for that feature should be written as a single "slice" file, preferably using the Redux Toolkit createSlice API. (This is also known as the "ducks" pattern). While older Redux codebases often used a "folder-by-type" approach with separate folders for "actions" and "reducers", keeping related logic together makes it easier to find and update that code.”
요약 하자면, 현재 리덕스는 기능 컴포넌트 파일과 Redux의 Slice파일을 함께 두는것을 추천한다.
참고 문서 : Redux 공식문서
https://redux.js.org/tutorials/essentials/part-2-app-structure
4-2. Redux-Toolkit 사용법
configureStore.js
import { configureStore } from '@reduxjs/toolkit'
import usersReducer from '../features/users/usersSlice'
import postsReducer from '../features/posts/postsSlice'
import commentsReducer from '../features/comments/commentsSlice'
export default configureStore({
reducer: {
users: usersReducer,
posts: postsReducer,
comments: commentsReducer
}
})
counterSlice.js
import { createSlice } from '@reduxjs/toolkit'
export const counterSlice = createSlice({
name: 'counter',
initialState: {
value: 0
},
reducers: {
increment: state => {
// Redux Toolkit allows us to write "mutating" logic in reducers. It
// doesn't actually mutate the state because it uses the immer library,
// which detects changes to a "draft state" and produces a brand new
// immutable state based off those changes
state.value += 1
},
decrement: state => {
state.value -= 1
},
incrementByAmount: (state, action) => {
state.value += action.payload
}
}
})
export const { increment, decrement, incrementByAmount } = counterSlice.actions
export default counterSlice.reducer
공식 문서에서
“You can only write "mutating" logic in Redux Toolkit's createSlice and createReducer because they use Immer inside! If you write mutating logic in reducers without Immer, it will mutate the state and cause bugs!”
⇒ 현재 redux-toolkit에서 제공하는 createSlice와 createReducer는 Immer라이브러리가 안에 사용되었기 때문에 불변성을 신경쓰지 않아도 된다!!!
> 옛날 Redux 코드 vs 현재 redux-toolkit의 createSlice사용
옛날 Redux 코드
function handwrittenReducer(state, action) {
return {
...state,
first: {
...state.first,
second: {
...state.first.second,
[action.someId]: {
...state.first.second[action.someId],
fourth: action.someValue
}
}
}
}
}
현재 Redux-Toolkit 사용 후 코드
function reducerWithImmer(state, action) {
state.first.second[action.someId].fourth = action.someValue
}
액션 사용 및 state 불러오기
import React, { useState } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import {
decrement,
increment,
incrementByAmount,
incrementAsync,
selectCount
} from './counterSlice'
import styles from './Counter.module.css'
export function Counter() {
const count = useSelector(selectCount)
const dispatch = useDispatch()
const [incrementAmount, setIncrementAmount] = useState('2')
return (
<div>
<div className={styles.row}>
<button
className={styles.button}
aria-label="Increment value"
onClick={() => dispatch(increment())}
>
+
</button>
<span className={styles.value}>{count}</span>
<button
className={styles.button}
aria-label="Decrement value"
onClick={() => dispatch(decrement())}
>
-
</button>
</div>
{/* omit additional rendering output here */}
</div>
)
}
참고 :https://redux.js.org/tutorials/essentials/part-2-app-structure
'웹 > (React)리액트' 카테고리의 다른 글
(react-query)useInfiniteQuery로 무한스크롤 구현하기 (상세 설명, React with Typescript) (0) | 2024.10.29 |
---|---|
(React)리액트 클래스에 조건문/리액트에서 클래스 다양한 방법으로 사용해보기/클래스명 여러개/className 여러개 일때/클래스에 변수 사용하기 (20) | 2023.07.11 |
(React) 리액트 드롭다운 메뉴 만들기/마우스 오버시 드롭다운 - 1탄 (2) | 2022.12.17 |
(React with Typescript) module not found 'sass' 에러 해결, scss 사용법 (0) | 2022.10.27 |
(React) 리액트 커스텀 훅 custom hook 사용법+input 초기화 까지 - input (0) | 2022.09.24 |
댓글