프로젝트를 진행하면서 가장 어려웠던 부분 중 하나는 파일 관리와 코드의 복잡성이었다.
Redux를 사용하기로 결정했을 때, 처음에는 각각의 액션, 리듀서, 상수, 미들웨어를 따로 관리하다 보니 폴더 구조가 금방 복잡해지고, 필요한 파일을 찾는 데에도 시간이 많이 걸렸다.
게다가 Redux의 기본 사용 방식은 보일러플레이트 코드가 많아서, 새로운 기능을 추가하거나 상태를 관리할 때마다 같은 패턴을 반복하는 일이 많았다.
이런 문제를 해결하고자 Redux Toolkit을 도입했고, 결과적으로 코드 관리의 복잡성과 폴더 구조의 혼란을 크게 줄일 수 있었다.
여기에서 Redux Toolkit을 사용하면서 느낀 점과 구체적인 방법을 공유해보려고 한다.
1. Redux Toolkit의 도입: 보일러플레이트 감소
Redux의 가장 큰 단점 중 하나는 상태 관리를 위해 작성해야 할 코드가 많다는 점이었다.
액션 타입, 액션 생성자, 리듀서를 각각 작성하고 연결해야 했는데, 이 과정이 반복되면서 코드가 너무 길어지고 읽기 어려워졌다.
Redux Toolkit의 createSlice를 사용하면서 이런 보일러플레이트 코드가 크게 줄었다. 예를 들어, 인증 상태를 관리하기 위해 작성했던 authSlice는 다음과 같이 간단한 구조로 정리되었다:
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import apiClient from '../api/clientapi';
interface User {
account: string;
email: string;
name: string;
nickname: string;
phone: string;
thumbnail: string;
uid: number;
}
interface AuthState {
token: string | null;
isAuthenticated: boolean;
user: User | null;
}
const initialState: AuthState = {
token: localStorage.getItem('token') || null,
isAuthenticated: !!localStorage.getItem('token'),
user: JSON.parse(localStorage.getItem('user') || 'null'),
};
const authSlice = createSlice({
name: 'auth',
initialState,
reducers: {
login(state, action: PayloadAction<{ token: string; user: User }>) {
state.token = action.payload.token;
state.isAuthenticated = true;
state.user = action.payload.user;
localStorage.setItem('token', action.payload.token);
localStorage.setItem('user', JSON.stringify(action.payload.user));
},
logout(state) {
state.token = null;
state.isAuthenticated = false;
state.user = null;
localStorage.removeItem('token');
localStorage.removeItem('user');
delete apiClient.defaults.headers.common["X-AUTH-TOKEN"];
},
},
});
export const { login, logout } = authSlice.actions;
export default authSlice.reducer;
createSlice는 액션과 리듀서를 한 곳에서 정의할 수 있어서 코드 구조가 단순해졌고, 상태 관리 로직을 한눈에 파악할 수 있게 되었다. 특히 immer가 내장되어 있어 상태를 불변성 걱정 없이 간단히 업데이트할 수 있는 점이 유용했다.
2. 파일 구조를 단순화하다
Redux Toolkit을 사용하기 전에는 액션과 리듀서를 각각의 파일로 나누는 방식으로 작업했는데, 프로젝트가 커질수록 이 구조가 불편했다. 관련된 로직이 서로 다른 폴더에 흩어져 있어서, 프로젝트에 대한 전반적인 파악이 어려워진 느낌이 들었다.
Redux Toolkit에서는 slice 단위로 코드를 작성하면서 파일 구조가 훨씬 단순해졌다. authSlice를 예로 들면, 액션과 리듀서가 모두 authSlice.ts 파일에 포함되어 있어, 인증 관련 로직을 확인하거나 수정해야 할 때 해당 파일 하나만 보면 되는 구조로 설정했다.
이로 인해 폴더 구조도 자연스럽게 간결해졌다:
/src
/services
authSlice.ts
모든 인증 관련 상태 관리와 API 호출 로직이 authSlice.ts에 집중되어 있으니, 유지보수가 훨씬 쉬워졌다. (물론 더 좋은 방법이 있기는 할 수 있겠다만 ...)
해당 구조를 도입하여 새로운 기능을 추가하거나 기존 코드를 수정할 때 어디를 수정해야 할지 바로 파악할 수 있었다.
3. 상태 초기화와 비동기 작업의 통합
이전에는 로그아웃 시 상태를 초기화하거나, API 클라이언트와 연동하는 로직을 여러 곳에서 관리해야 했다.
예를 들어, 로컬 스토리지에서 데이터를 삭제하고, Redux 상태를 초기화하고, API 클라이언트 헤더를 제거하는 작업이 각각 다른 파일에 분산되어 있었다.
Redux Toolkit에서는 이런 작업들을 logout 리듀서에 통합할 수 있었다:
logout(state) {
state.token = null;
state.isAuthenticated = false;
state.user = null;
localStorage.removeItem('token');
localStorage.removeItem('user');
delete apiClient.defaults.headers.common["X-AUTH-TOKEN"];
}
이처럼 상태와 로컬 스토리지, API 클라이언트를 한 곳에서 관리하니 코드가 직관적이고 깔끔해진 느낌이 들었다.
로그아웃 버튼의 클릭 이벤트도 다음처럼 간단히 처리할 수 있었다:
import React from 'react';
import { useDispatch } from 'react-redux';
import { logout } from '../services/authSlice';
const LogoutButton: React.FC = () => {
const dispatch = useDispatch();
const handleLogout = () => {
dispatch(logout());
window.location.reload();
};
return <button onClick={handleLogout}>Logout</button>;
};
export default LogoutButton;
마무리
Redux Toolkit을 도입하면서 파일 관리와 코드 복잡성 문제를 어느 정도 해결할 수 있었다.
특히 createSlice를 통해 코드 작성이 간결해지고, 관련 로직이 하나의 파일에 집중되면서 유지보수와 협업이 훨씬 쉬워졌다.
또, 상태 초기화와 비동기 작업을 한 곳에서 관리할 수 있어서, 코드 가독성이 크게 개선되었다.
물론 더 좋은 방법들이 존재할 수 있지만, 이러한 노력으로 코드의 일관성과 효율성이 향상되었고,
빠르게 개발할 수 있는 기반이 마련되었던 경험이었다.
'개발 > 프론트엔드' 카테고리의 다른 글
| 리코일을 도입해보십다 (2) | 2025.03.26 |
|---|---|
| CRA 지고 Vite가 왔다...!! (0) | 2025.03.26 |
| 왜 Redux 대신 Redux Toolkit을 사용하는가? (4) | 2024.12.23 |
| CSR 환경에서 OG 태그 사용의 해결 방안은 무엇일까? (5) | 2024.10.27 |
| 브라우저가 리소스를 로드하는 과정에서 성능을 최적화하는 방법 (3) | 2024.10.05 |