React/Redux 마스터

Redux 마스터(6): Redux에서 복잡한 상태 관리 및 성능 최적화

atomicdev 2024. 10. 11. 16:31
728x90

Redux에서 복잡한 상태 관리 및 성능 최적화

애플리케이션 규모가 커질수록 상태 관리의 복잡성도 증가하게 됩니다. 이 단계에서는 상태 분리Normalization 기법을 사용하여 복잡한 상태를 관리하고, 성능을 최적화하는 방법을 배웁니다. 효율적인 리듀서 설계와 상태 관리 전략을 통해 성능 문제를 해결할 수 있는 방법을 살펴보겠습니다.

Redux에서 복잡한 상태 관리

1. 상태 분리(State Splitting)

상태가 복잡해질수록 모든 상태를 하나의 큰 리듀서에서 관리하는 것은 비효율적일 수 있습니다. 이 경우 상태를 여러 개의 작은 리듀서로 분리하여 관리하는 것이 중요합니다. 이를 통해 상태 구조를 더욱 명확하게 만들고, 각 리듀서가 독립적으로 상태를 처리할 수 있게 됩니다.

예시: 상태 분리

// userSlice.js
import { createSlice } from '@reduxjs/toolkit';

const userSlice = createSlice({
  name: 'user',
  initialState: { name: '', email: '' },
  reducers: {
    setUser: (state, action) => {
      state.name = action.payload.name;
      state.email = action.payload.email;
    },
  },
});

export const { setUser } = userSlice.actions;
export default userSlice.reducer;

// postsSlice.js
import { createSlice } from '@reduxjs/toolkit';

const postsSlice = createSlice({
  name: 'posts',
  initialState: [],
  reducers: {
    setPosts: (state, action) => {
      return action.payload;
    },
  },
});

export const { setPosts } = postsSlice.actions;
export default postsSlice.reducer;
 

위 코드에서는 user와 posts 상태를 분리하여 각 리듀서가 독립적으로 상태를 처리합니다. 이 방식은 상태가 복잡해질수록 유지보수가 쉬워지고, 성능 최적화에도 도움이 됩니다.

2. Normalization 기법

Normalization은 중첩된 데이터를 평평하게 만들어 상태 관리의 복잡성을 줄이는 기법입니다. 중첩된 데이터를 직접 상태에 저장하면 업데이트와 조회 시 복잡해질 수 있지만, 데이터를 평평하게 저장하고 ID를 참조하면 상태 업데이트가 훨씬 더 간편해집니다.

예시: 상태 Normalization

const normalizedState = {
  users: {
    byId: {
      1: { id: 1, name: 'Alice' },
      2: { id: 2, name: 'Bob' },
    },
    allIds: [1, 2],
  },
  posts: {
    byId: {
      101: { id: 101, title: 'First Post', userId: 1 },
      102: { id: 102, title: 'Second Post', userId: 2 },
    },
    allIds: [101, 102],
  },
};

이 구조는 각각의 데이터를 ID를 통해 관리하며, 별도의 키(byId, allIds)로 데이터를 정리하여 중복된 데이터 관리나 업데이트를 더 쉽게 처리할 수 있습니다.

3. 리듀서 최적화

리듀서는 상태 업데이트의 중심이기 때문에 리듀서를 최적화하는 것이 성능 향상에 큰 도움이 됩니다. 리듀서의 로직이 복잡해지면 상태 변화가 일어날 때마다 성능에 부하가 걸릴 수 있기 때문에, 불필요한 상태 변경을 최소화하고, 불변성을 지키는 것이 중요합니다.

예시: 불필요한 상태 변경 최소화

const userReducer = (state, action) => {
  switch (action.type) {
    case 'SET_USER_NAME':
      if (state.name !== action.payload) {
        return { ...state, name: action.payload };
      }
      return state;
    default:
      return state;
  }
};
 

위 코드에서는 상태 값이 변경되지 않는 경우 새로운 상태를 반환하지 않음으로써 불필요한 리렌더링을 방지할 수 있습니다.

4. 성능 최적화 방법

  • 불변성 유지: Redux에서는 상태가 불변해야 하기 때문에, 상태 업데이트 시 항상 새 객체를 반환해야 합니다. Immer 라이브러리와 같은 도구를 사용하면 이 작업을 더 쉽게 처리할 수 있습니다.
  • 메모이제이션 사용: reselect와 같은 라이브러리를 사용하여 선택된 상태 값만 다시 계산하는 방식으로 성능을 최적화할 수 있습니다.
  • 리듀서 로직 단순화: 리듀서 내에서 복잡한 로직을 처리하지 말고, 비즈니스 로직은 미들웨어나 액션에서 처리하여 리듀서를 최대한 단순하게 유지합니다.

5. Redux DevTools를 통한 성능 모니터링

Redux DevTools를 사용하면 상태 변화와 액션의 흐름을 실시간으로 추적할 수 있으며, 성능 문제를 빠르게 파악할 수 있습니다. 이 도구를 활용하여 상태 관리가 어떻게 이루어지고 있는지 모니터링하는 것이 중요합니다.

const store = configureStore({
  reducer: rootReducer,
  devTools: process.env.NODE_ENV !== 'production',
});

6. 주요 개념 정리

  • 상태 분리: 상태를 여러 리듀서로 분리하여 복잡한 상태를 효율적으로 관리.
  • Normalization: 중첩된 데이터를 평평하게 만들어 상태 업데이트를 쉽게 처리.
  • 리듀서 최적화: 불필요한 상태 변경을 최소화하고, 상태 불변성을 유지.
  • 성능 최적화: 메모이제이션과 같은 기술을 통해 상태 조회 성능을 최적화.

7. 장점과 단점

장점:

  • 복잡한 애플리케이션에서 상태 관리가 더 명확해지고 유지보수가 쉬워짐.
  • 상태 Normalization을 통해 상태 업데이트와 조회가 간편해짐.
  • Redux DevTools를 통해 상태 변화와 액션 흐름을 쉽게 모니터링.

단점:

  • 초기에 상태 분리나 Normalization 구조를 설계하는 데 시간과 노력이 필요함.
  • 상태가 지나치게 세분화되면 리듀서 간 데이터 공유가 복잡해질 수 있음.

결론

Redux에서 복잡한 상태 관리와 성능 최적화는 대규모 애플리케이션에서 필수적입니다. 상태 분리와 Normalization 기법을 통해 복잡한 상태를 효율적으로 관리하고, 성능을 극대화할 수 있습니다. 이러한 기법을 적용하여 확장 가능한 상태 관리 구조를 설계하는 것이 중요합니다.

728x90