Skip to content

Commit

Permalink
feat: 20240728_about_redux_이재용 (#65)
Browse files Browse the repository at this point in the history
  • Loading branch information
enigsuss authored Sep 6, 2024
1 parent 7d850a7 commit 0342ff7
Showing 1 changed file with 169 additions and 0 deletions.
169 changes: 169 additions & 0 deletions _posts/2024-07-28-Redux-이재용.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
---
layout: post
title: "함수 컴포넌트에서의 Redux 성능 최적화"
author: "이재용"
categories: "프론트엔드 기술블로그"
banner:
image: "https://velog.velcdn.com/images/jjh099/post/3fa558f9-2050-402b-aee7-350c02fc6ef2/image.jpeg"
background: "#000"
height: "100vh"
min_height: "38vh"
heading_style: "font-size: 4.25em; font-weight: bold; text-decoration: underline"
tags: ["redux", "리덕스", "최적화"]
---
## 들어가며

Redux는 리액트 애플리케이션의 강력한 상태 관리 방법을 제공하지만, 잘못 사용하면 성능 이슈가 발생할 수 있습니다. 이번 글에서는 Redux를 사용할 때 적용할 수 있는 성능 최적화 기법에 대해 알아보겠습니다.

---

## **Redux와 함수 컴포넌트에서 발생하는 성능 이슈**

다음과 같이 크게 2가지 문제를 생각해 볼 수 있습니다.

- **불필요한 리렌더링** : Redux 상태가 변경될 때마다, 연결된 컴포넌트들은 리렌더링됩니다. 하지만 모든 상태 변경이 해당 컴포넌트와 관련된 것은 아닐 수 있습니다. 이로 인해 불필요한 리렌더링이 발생하여 성능 저하를 일으킬 수 있습니다.
- **비효율적인 상태 선택과 계산** : 컴포넌트에서 필요한 상태를 선택할 때 불필요한 연산이나 깊은 비교가 발생하면 렌더링 성능에 영향을 미칩니다.

이제, 본격적으로 Redux 성능을 최적화 할 수 있는 방법에 대해 알아봅시다.

---

## **Redux 성능 최적화**

### **1. React Redux Hooks의 효율적 사용**

첫 번째 최적화 기법은 `useSelector` 훅을 최적화하는 방법입니다.

- **`useSelector` 훅의 최적화**:
- `useSelector`는 Redux 스토어의 상태를 선택하여 컴포넌트에서 사용할 수 있게 해주는 훅입니다. 기본적으로 `useSelector`는 참조 비교를 수행하기 때문에, 선택한 상태가 새로운 객체나 배열로 반환되면 내용이 같더라도 리렌더링이 발생할 수 있습니다.
- **해결 방법**:
- `shallowEqual`을 사용하여 얕은 비교를 통해 불필요한 리렌더링을 방지할 수 있습니다. 이는 특히 상태가 객체나 배열일 때 유용합니다.
- 상태가 복잡한 중첩된 객체인 경우, 커스텀 비교 함수를 사용하여 객체 내 특정 값만 비교하도록 최적화할 수 있습니다.

두 번째로, `createSelector`를 사용한 메모이제이션 기법입니다.

- **메모이제이션된 셀렉터 사용 (`createSelector`)**:
- `createSelector`는 Redux Toolkit에 포함된 `reselect` 라이브러리의 일부로, 불필요한 계산을 방지하고 성능을 향상시킬 수 있습니다.
- **사용 예시**:

```jsx
const selectItems = (state) => state.items;
const selectFilter = (state) => state.filter;

const selectFilteredItems = createSelector(
[selectItems, selectFilter],
(items, filter) => items.filter((item) => item.type === filter)
);

const filteredItems = useSelector(selectFilteredItems);

```

- `createSelector`는 상태가 변경되지 않으면 이전에 계산된 결과를 반환하여, 불필요한 연산과 리렌더링을 방지할 수 있습니다.

> **메모이제이션 (Memoiztion)** :
컴퓨터 프로그램이 동일한 계산을 반복적으로 해야할 때, **이전에 계산한 값을 메모리에 저장**하여 **중복적인 계산을 제거**하여 전체적인 실행속도를 빠르게 해주는 기법입니다.
>

---

### **2. Redux Toolkit을 통한 효율적인 상태 관리**

Redux Toolkit은 Redux의 공식 툴킷으로, 보일러플레이트 코드를 줄이고 일반적인 Redux 작업을 간소화합니다. 기본적으로 Immer와 Redux Thunk를 포함하고 있어, 불변성 관리와 비동기 작업 처리가 수월합니다.

- **`createSlice`와 `createSelector` 사용**:
- `createSlice`를 사용하면 상태와 리듀서를 간결하게 정의할 수 있으며, `createSelector`와 결합하여 효율적인 상태 관리와 성능 최적화를 할 수 있습니다.
- **사용 예시**:

```jsx
const usersSlice = createSlice({
name: 'users',
initialState: { list: [], status: 'idle' },
reducers: {
addUser: (state, action) => {
state.list.push(action.payload);
},
},
});
const selectUsers = (state) => state.users.list;
const selectActiveUsers = createSelector(
[selectUsers],
(users) => users.filter((user) => user.isActive)
);
```

- 이를 통해 불필요한 리렌더링과 성능 저하를 방지하며, 애플리케이션의 효율성을 높일 수 있습니다.

---

### **3. 불변성 유지와 Immer의 활용**

불변성을 유지하는 것은 Redux 성능 최적화에서 중요한 역할을 합니다. Redux Toolkit은 내부적으로 `Immer` 라이브러리를 사용하여 불변성을 자동으로 관리합니다. 이를 통해 상태를 직관적이고 간단하게 업데이트할 수 있습니다.

- **불변성 유지**:
- 불변성을 유지하면 상태 변화 추적이 용이하고, 예기치 않은 부작용을 방지할 수 있습니다.
- **Redux Toolkit과 Immer**:
- **사용 예시**:

```jsx
const todosSlice = createSlice({
name: 'todos',
initialState: [],
reducers: {
addTodo: (state, action) => {
state.push(action.payload); // Immer가 자동으로 불변성을 유지해줌
},
toggleTodo: (state, action) => {
const todo = state.find(todo => todo.id === action.payload);
if (todo) {
todo.completed = !todo.completed;
}
},
},
});
```

- 이를 통해 명령형 프로그래밍 스타일로 상태를 쉽게 업데이트하면서도 불변성을 유지할 수 있습니다.

> **Immer** :
상태 변경 시 원본 상태를 복사한 후 변경된 부분만 업데이트하여 새로운 상태를 반환하는 불변성 관리 라이브러리입니다.
직관적인 불변성 관리를 통해, 개발자가 실수로 원본 상태를 수정하는 것을 방지할 수 있습니다.
>

---

### **4. 미들웨어와 캐싱을 활용한 비동기 작업 최적화**

비동기 작업을 처리할 때의 최적화 기법입니다. Redux Thunk를 사용하면 비동기 로직을 명확하게 관리할 수 있으며, 불필요한 API 호출을 줄이기 위해 캐싱을 활용할 수 있습니다.

- **Redux Thunk의 활용**:
- 비동기 작업을 관리하고, 필요한 시점에 상태를 업데이트할 수 있습니다.
- **비동기 작업의 성능 최적화**:
- 예를 들어, 동일한 데이터를 반복적으로 요청하는 대신, 한 번 요청한 데이터를 스토어에 저장하고 필요할 때마다 스토어에서 가져오는 방식으로 성능을 최적화할 수 있습니다.
- **사용 예시**:

```jsx
const fetchUserById = (userId) => async (dispatch, getState) => {
const cachedUser = getState().users.find(user => user.id === userId);
if (cachedUser) {
return; // 캐시된 데이터가 있으면 추가 API 호출을 방지
}
const response = await fetch(`/api/users/${userId}`);
const data = await response.json();
dispatch(usersSlice.actions.addUser(data));
};
```

- 이를 통해 비동기 작업이 성능에 미치는 영향을 최소화할 수 있습니다.

---

## **결론**

지금까지 리액트의 함수 컴포넌트에서 Redux를 사용할 때 적용할 수 있는 다양한 성능 최적화 기법을 살펴보았습니다. `useSelector`의 최적화, `createSelector`를 활용한 메모이제이션, 불변성 유지, 그리고 Redux Thunk를 통한 비동기 작업 최적화는 모두 애플리케이션의 성능과 사용자 경험을 크게 향상시킬 수 있는 중요한 기법들입니다.

다양한 최적화 기법들을 상황에 맞게 적용하여, 보다 빠르고 안정적인 애플리케이션을 개발하시길 바랍니다.

0 comments on commit 0342ff7

Please sign in to comment.