1. Redux의 핵심 구조
✅ Store 내부 (Redux의 핵심 요소)
애플리케이션 상태(state)를 저장하고 관리하는 역할입니다.
- state: 애플리케이션의 현재 상태 (객체 형태)
- reducer: 상태(state)를 변경하는 순수 함수
- getState(): 현재 상태를 반환하는 함수
- dispatch(action): 액션을 전달하여 상태 변경 요청
- subscribe(): 상태 변경을 감지하고 실행할 함수를 등록
✅ Store 외부 (Redux를 사용하는 요소)
Redux는 단독으로 동작하는 게 아니라 UI와 함께 사용됩니다.
UI → Action → Store → Render UI
- Admin (Redux DevTools): 상태 변화를 추적하고 디버깅하는 도구
- Render (React/Next.js UI 업데이트): state 변경 시 컴포넌트가 리렌더링됨
2. Redux 폴더 구조 (Next.js/App Router + TypeScript 기준)
📂 src
┣ 📂 redux
┃ ┣ 📂 features
┃ ┃ ┣ 📂 counter
┃ ┃ ┃ ┣ 📜 counterSlice.ts # 특정 기능(state, reducer, actions) 관리
┃ ┃ ┣ 📂 user
┃ ┃ ┃ ┣ 📜 userSlice.ts # 사용자 관련 상태 관리
┃ ┣ 📜 store.ts # Redux store 설정 (모든 리듀서 연결)
┣ 📂 components
┃ ┣ 📜 Counter.tsx # Redux state를 사용하는 컴포넌트
┣ 📂 app
┃ ┣ 📂 layout.tsx # 전체 앱 레이아웃 설정
┃ ┣ 📂 page.tsx # 메인 페이지 (홈)
┃ ┣ 📂 providers.tsx # Redux Provider 적용
3. Redux 기본 코드 (Next.js/App Router + TypeScript 기준)
1️⃣ Redux Store 설정 (store.ts)
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './features/counter/counterSlice';
import userReducer from './features/user/userSlice';
export const store = configureStore({
reducer: {
counter: counterReducer,
user: userReducer,
// ... 등등 등록해서 사용
},
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
- configureStore()를 사용해 Redux store를 생성
- RootState, AppDispatch 타입을 지정하여 TypeScript에서 사용 편리
2️⃣ 카운터 기능 (counterSlice.ts)
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
interface CounterState {
value: number;
}
const initialState: CounterState = { value: 0 };
const counterSlice = createSlice({
name: 'counter',
initialState,
reducers: {
increment: (state) => { state.value += 1; },
decrement: (state) => { state.value -= 1; },
reset: (state) => { state.value = 0; },
setCount: (state, action: PayloadAction<number>) => { state.value = action.payload; },
},
});
export const { increment, decrement, reset, setCount } = counterSlice.actions;
export default counterSlice.reducer;
- Redux Toolkit의 createSlice를 사용해 액션과 리듀서를 한 번에 정의
3️⃣ Redux Provider 적용 (providers.tsx)
App Router에서는 layout.tsx에서 Redux를 적용할 수 없기 때문에, 별도의 providers.tsx 파일을 만들어 Redux Provider를 감싸줘야 합니다.
'use client';
import { Provider } from 'react-redux';
import { store } from '../redux/store';
interface ProvidersProps {
children: React.ReactNode;
}
export function Providers({ children }: ProvidersProps) {
return <Provider store={store}>{children}</Provider>;
}
- 'use client'를 꼭 추가해야 함 (App Router에서는 기본적으로 서버 컴포넌트이므로 클라이언트 컴포넌트로 지정)
- children을 받아 Redux Provider로 감싸서 상태를 관리할 수 있도록 설정
4️⃣ Redux 적용 (layout.tsx)
Next.js App Router에서는 layout.tsx가 모든 페이지의 공통 레이아웃을 설정하는 역할을 하므로 여기서 Providers.tsx를 감싸면 전체 애플리케이션에서 Redux 상태를 사용할 수 있습니다.
typescript
복사편집
import { Providers } from './providers';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<Providers>
{children}
</Providers>
</body>
</html>
);
}
5️⃣ 예시로 Counter 컴포넌트 생성 (Counter.tsx)
Redux 상태를 사용하여 카운터 UI를 만들기
typescript
복사편집
'use client';
import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement, reset, setCount } from '../redux/features/counter/counterSlice';
import { RootState } from '../redux/store';
const Counter = () => {
const count = useSelector((state: RootState) => state.counter.value);
const dispatch = useDispatch();
return (
<div>
<h1>Counter: {count}</h1>
<button onClick={() => dispatch(increment())}>+</button>
<button onClick={() => dispatch(decrement())}>-</button>
<button onClick={() => dispatch(reset())}>Reset</button>
<button onClick={() => dispatch(setCount(10))}>Set to 10</button>
</div>
);
};
export default Counter;
- 'use client' 추가 필수! (app/ 폴더 내부는 기본적으로 서버 컴포넌트이므로, 클라이언트 컴포넌트로 설정해야 Redux 사용 가능)
- useSelector와 useDispatch를 사용해 Redux 상태를 가져오고, 액션을 실행
6️⃣ 홈 페이지에서 Counter 사용 (page.tsx)
Counter.tsx를 메인 페이지에서 불러와서 등록
import Counter from '../components/Counter';
export default function Home() {
return (
<main>
<h1>Redux Counter Example</h1>
<Counter />
</main>
);
}
4. App Router에서 Redux 적용 흐름 정리
- Redux store 생성 (store.ts)
- slice(counterSlice.ts)를 생성하여 상태 관리
- Redux Provider를 providers.tsx에 별도로 생성
- layout.tsx에서 Providers를 감싸 Redux 적용
- 클라이언트 컴포넌트('use client')에서 Redux 사용
5. 배운점
✅ 전역 상태 관리 (Props Drilling 해결)
- 여러 컴포넌트에서 상태를 쉽게 공유할 수 있음
- props를 계속 넘기는 번거로움을 해결
✅ 예측 가능한 상태 관리
- state가 한 곳(store) 에서 관리되므로 변경 흐름을 쉽게 추적 가능
- 상태 변경은 reducer를 통해서만 이루어져 코드가 예측 가능
✅ 강력한 디버깅 기능
- Redux DevTools를 사용하면 state 변화 과정을 추적 가능
- 이전 상태로 되돌리는 Time-travel debugging 가능
✅ 비동기 데이터 관리 용이
- redux-thunk, redux-saga 등을 이용해 비동기 API 요청을 쉽게 처리 가능
- useEffect에서 상태 관리하는 대신 Redux에서 해결 가능
'Frontend > TypeScript' 카테고리의 다른 글
[TypeScript] 9. 유틸리티 타입 (0) | 2025.01.31 |
---|---|
[TypeScript] 8. 조건부 타입 (0) | 2025.01.30 |
[TypeScript] 7. 타입 조작하기 (0) | 2025.01.29 |
[TypeScript] 6-2. 제네릭 활용 (1) | 2025.01.28 |
[TypeScript] 6-1. 제네릭이란? (0) | 2025.01.28 |