Redux Toolkit 알아보기 -리듀서 춘추 전국 시대-

김민규·2023년 1월 1일
0
post-thumbnail

Redux의 등장, 그러나...

Redux의 등장으로 인해 개발자들은 상태 관리의 많은 문제점을 해결할 수 있었다. 하지만 이런 Redux에도 몇가지 애로사항들이 존재하였다.


Redux의 문제점

상태의 불변성이 보장되지 않는다

Redux의 경우 reducer에서 action을 처리하면서 상태를 변경시킨다.
reducer 함수의 첫번째 인자의 경우 기존에 누적된 state가 지정되는데, 이를 개발자가 직접 조작할 수 있다. 따라서 상태를 처리하면서 오류가 발생할 가능성이 존재한다.
물론 코딩시 기존 상태를 깊은 복사하여 그 값을 수정하거나 스프레드 문법을 사용하여 오버라이드 하면 해결되긴 하지만 라이브러리 자체에서 상태의 불변성이 확실하게 보장되지 않는다는 점은 여전히 문제거리였다.

Store(저장소)가 거대해진다

Redux의 3원칙 중 하나는 바로 하나의 저장소 안에 하나의 상태 트리를 가진다 는 점이다.
물론 이러한 원칙으로 인하여 저장소를 나누지 않아 관리하기는 쉬워졌지만, 문제는 관리할 상태가 많아질 수록 store의 reducer가 담당하는 일이 너무 커진다는 문제가 발생한다.

떨어지는 코성비

간단한 어플리케이션을 만들면서 Redux를 사용할 경우 초반에 작성해야 할 코드들이 많다.
적은 양의 전역 상태를 관리하기 위해서 Redux를 사용할 경우 비동기 작업시 미들웨어 라이브러리를 추가로 설치해야하고 반복되는 코드도 늘어나며 action을 하나하나 미리 설정 후 적용시키는 등 여러가지 생각해야할 사항들이 다수 존재한다.


Redux Toolkit의 등장

이러한 문제점들을 해결하기 위해서 Redux Toolkit이 씬에 나타났다.
Redux Toolkit은 기존의 Redux가 가지고 있었던 여러 단점들을 보완하여 상태 관리를 더욱 편하게 만들었다.

Redux Toolkit의 존재 의의

불변성이 보장된다.

Redux에서는 불변성을 유지하기 위하여 immer.js 라이브러리를 사용하거나 개발자가 직접 상태를 복사하여 덮는 등의 과정을 거쳐야만 했다.
하지만 RTK의 경우 immer.js가 내장되어있어 상태를 직접 조작해도 기존의 누적된 상태가 변경되는 것이 아니라 변경사항 새로운 상태에 적용되므로 상태를 관리함에 있어 불변성이 보장된다.

편리한 action 관리

기존 Redux는 action에 Type 필드가 필수로 존재하였기에 dispatch시 이에 관한 내용을 작성하여야 했다. 그러다보니 type에 대한 오탈자로 인해 에러가 발생할 수도 있고 이를 방지하기 위해서 action creator를 따로 생성하는 경우가 존재했다.
하지만 RTK는 생성된 slice에서 action 함수를 직접 작성하여 컴포넌트에서 요청하므로 dispatch 과정에서 개발자가 신경 써야할 과정들이 줄어든다.

slice로 store 쪼개서 관리하기

기존의 redux는 하나의 store에 모든 상태를 관리하다보니 store에 할당되는 reducer가 너무 방대해지는 경우가 생겼다.
하지만 RTK는 slice로 상태와 상태의 action 함수를 종류별로 나누면서 reducer의 코드가 확연히 줄어들었고 상태를 목적에 따라 분류하기도 용이해졌다.

이 외에도 네트워크 비동기 처리, 타입스크립트 지원 등 여러 방면에서 기존 Redux의 단점들을 개선한 부분이 존재한다.


기존의 Redux를 Redux Toolkit로 바꾸기

reducer를 createSlice로 쪼개기

RTK는 redux의 방식인 store 생성시나 reducer 함수 인자에 initialState를 설정하는 방식이 아닌 slice라는 개념으로 상태를 나눈다.

RTK의 메서드인 createSlice를 사용하여 slice를 생성 할 수 있으며 slice의 포맷은 다음과 같다.

const counterSlice = createSlice({
  name: 'counter',
  initialState: { count: 0 },
  reducers: {
  	increase: (state, action) => {
  	  state.count += action.payload
  	},
    decrease: (state, action) => {
  	  state.count -= action.payload
  	},
  }
})

export const countReducer = countSlice.reducer;
export const countActions = countSlice.actions;

name
slice의 중복 생성을 방지하기 위한 고유의 값이다.
name이지만 컴포넌트 내부에서의 상태 지정에 사용되지는 않는다.

initialState
Redux의 initialState와 같은 개념이며 해당 slice의 초기 상태 포맷을 설정한다.

reducers
action에 따라 상태의 변화를 처리하는 함수로서 기존 Redux의 reducer 함수와 같은 역할을 한다.
immer.js가 적용되었기에 state에 직접 접근하여 조작하여도 불변성이 보장된다.
Redux에서는 type 필드를 설정하고 type에 따른 함수을 작성 후 불변성 보장 처리 과정을 거쳤지만, RTK에서는 reducers에 action 함수를 작성하는것 만으로 상기한 과정들을 전부 해결 할 수 있다.
이후 컴포넌트에서 action 함수들을 import하여 dispatch에 action 함수를 직접 할당하여 직관적인 상태 변경을 실행 할 수 있다.
참고로 action의 값은 payload라는 프로퍼티로 접근 가능하다.

생성된 slice에 .reducer를 사용하면 action 함수들을 모아놓은 하나의 reducer를 반환하는데, 나중에 store를 구성할 때 slice를 지정하는 용도로 사용된다.

또한 slice에 .actions를 사용하면 action 함수들을 모아놓은 객체를 반환하는데, 나중에 컴포넌트가 action 함수를 직접 dispatch하는 용도로 사용된다.

configureStore를 이용하여 slice로 이루어진 store 구성하기

Redux에서는 createStore 메서드로 store를 만들었지만 RTK는 configureStore를 사용하여 저장소를 구성한다.

const store = configureStore({
  reducer: { counter: counterReducer },
});

configureStore 의 인자로 할당되는 객체의 reducer 프로퍼티는 기존에 생성한 slice들의 reducer가 모인 객체이다.

reducer의 프로퍼티들은 key & value의 형식으로 입력되며 여기서 value는 slice의 reducer를 지정하고, key는 컴포넌트가 store의 특정 상태를 찾는데 사용되는 열쇠가 된다.

컴포넌트에서 reducer로 action 보내기

RTK도 기존의 Redux와 같이 useDispatchuseSelector 메서드를 사용하여 상태를 구독하고 action을 reducer로 전송한다.

하지만 RTK는 약간 다른 방식으로 store에 접근한다.

기존의 Redux의 dispatch 방식은 다음과 같다.

const dispatch = useDispatch();

dispatch({type: 'increase', value: 5});

위처럼 정해진 양식을 dispatch하여 reducer 함수가 action을 해석하도록 작성하지만 RTK에선 다음과 같다.

import { countActions } from "../../store/count-slice";

const dispatch = useDispatch();

dispatch(countActions.increase(5));

위와 같이 리듀서 함수들을 모아놓은 객체를 import하여 컴포넌트 내부에서 실행할 reducer 함수를 직접 요청하도록 작성한다.
기존의 reducer에서 action의 type 필드를 제외한 프로퍼티로 전달되던 값들은 직접 할당한 reducer 함수의 인자에 할당되며 이는 slice의 reducer 함수에서 action.payload로 접근하여 조작 할 수 있다.

컴포넌트에서 store의 특정 상태 구독하기

기존 Redux에서 컴포넌트의 상태를 구독하기 위해선 useSelector를 사용하였다.

const count = useSelector((state) => state.count);

RTK에서도 useSelector를 사용한다는 점은 동일하지만 상태를 지정하는 과정이 다르다.

RTK에서 동일한 방식으로 state를 지정하여 콘솔에 띄울시 store를 구성하면서 작성한 reducer 객체가 나타난다.

여기서 특정 state를 지목하려면 지목하고 싶은 reducer 객체의 key값을 입력한다.

const count = useSelector((state) => state.count);

코드상으로는 동일해보이지만 Redux는 하나의 거대한 state의 프로퍼티로 존재하는 상태에 접근한다는 것과는 다르게 RTK는 분리되어 존재하는 slice에 접근한다는 것이 차이점이다.


Redux와의 차이 정리

profile
Error Driven Development

0개의 댓글