์ ์ธ๊ณ Redux ์ ํ๋ฆฌ์ผ์ด์ ์์ ์ปดํ์ผ ํ์ ์์ ์ฑ์ ํ๋ณดํ๊ณ ๊ฐ๋ฐ์ ๊ฒฝํ์ ํฅ์์ํค์ธ์. ์ด ๊ฐ์ด๋๋ Redux Toolkit๊ณผ ๊ณ ๊ธ ํจํด์ ํฌํจํ์ฌ TypeScript๋ก ํ์ ์์ ์ํ, ์ก์ , ๋ฆฌ๋์, ์คํ ์ด ๊ตฌํ์ ๋ค๋ฃน๋๋ค.
ํ์ ์์ (Type-Safe) Redux: ๊ธ๋ก๋ฒ ํ์ ์ํ ๊ฐ๋ ฅํ ํ์ ๊ตฌํ์ ํตํ ์ํ ๊ด๋ฆฌ ๋ง์คํฐํ๊ธฐ
ํ๋ ์น ๊ฐ๋ฐ์ ๊ดํํ ํ๊ฒฝ์์ ์ ํ๋ฆฌ์ผ์ด์ ์ํ๋ฅผ ํจ์จ์ ์ด๊ณ ์์ ์ ์ผ๋ก ๊ด๋ฆฌํ๋ ๊ฒ์ ๋งค์ฐ ์ค์ํฉ๋๋ค. Redux๋ ์์ธก ๊ฐ๋ฅํ ์ํ ์ปจํ ์ด๋์ ํต์ฌ ์์๋ก ์ค๋ซ๋์ ์๋ฆฌ ์ก์์ผ๋ฉฐ, ๋ณต์กํ ์ ํ๋ฆฌ์ผ์ด์ ๋ก์ง์ ์ฒ๋ฆฌํ๋ ๊ฐ๋ ฅํ ํจํด์ ์ ๊ณตํฉ๋๋ค. ๊ทธ๋ฌ๋ ํ๋ก์ ํธ๊ฐ ๊ท๋ชจ, ๋ณต์ก์ฑ ๋ฉด์์ ์ฑ์ฅํ๊ณ ํนํ ๋ค์ํ ๊ตญ์ ํ๊ณผ ํ์ ํ ๋, ๊ฒฌ๊ณ ํ ํ์ ์์ ์ฑ(type-safety)์ ๋ถ์ฌ๋ ๋ฐํ์ ์ค๋ฅ์ ๋ฏธ๋ก์ ์ด๋ ค์ด ๋ฆฌํฉํ ๋ง ๋ ธ๋ ฅ์ผ๋ก ์ด์ด์ง ์ ์์ต๋๋ค. ์ด ์ข ํฉ ๊ฐ์ด๋๋ ํ์ ์์ Redux์ ์ธ๊ณ๋ฅผ ๊น์ด ํ๊ณ ๋ค์ด, TypeScript๊ฐ ์ํ ๊ด๋ฆฌ๋ฅผ ๊ฒฌ๊ณ ํ๊ณ ์ค๋ฅ์ ๊ฐํ๋ฉฐ ์ ์ธ๊ณ์ ์ผ๋ก ์ ์ง ๋ณด์ ๊ฐ๋ฅํ ์์คํ ์ผ๋ก ์ด๋ป๊ฒ ๋ณํ์ํฌ ์ ์๋์ง ๋ณด์ฌ์ค๋๋ค.
๊ทํ์ ํ์ด ์ฌ๋ฌ ๋๋ฅ์ ๊ฑธ์ณ ์๊ฑฐ๋ ๋ชจ๋ฒ ์ฌ๋ก๋ฅผ ๋ชฉํ๋ก ํ๋ ๊ฐ๋ณ ๊ฐ๋ฐ์์ด๋ ๊ด๊ณ์์ด, ํ์ ์์ Redux๋ฅผ ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ ์ดํดํ๋ ๊ฒ์ ์ค์ํ ๊ธฐ์ ์ ๋๋ค. ์ด๋ ๋จ์ํ ๋ฒ๊ทธ๋ฅผ ํผํ๋ ๊ฒ์ ๋์ด, ์์ ๊ฐ์ ๊ณ ์ทจํ๊ณ ํ์ ์ ๊ฐ์ ํ๋ฉฐ ๋ฌธํ์ ๋๋ ์ง๋ฆฌ์ ์ฅ๋ฒฝ์ ๋์ด ๊ฐ๋ฐ ์ฃผ๊ธฐ๋ฅผ ๊ฐ์ํํ๋ ๊ฒ์ ๋๋ค.
Redux ํต์ฌ: ๊ฐ์ ๊ณผ ํ์ ์ด ์๋ ์ทจ์ฝ์ฑ ์ดํดํ๊ธฐ
ํ์ ์์ ์ฑ ์ฌ์ ์ผ๋ก ๋ ๋๊ธฐ ์ ์ Redux์ ํต์ฌ ์์น์ ๊ฐ๋ตํ ๋์ง์ด ๋ด ์๋ค. ๋ณธ์ง์ ์ผ๋ก Redux๋ JavaScript ์ ํ๋ฆฌ์ผ์ด์ ์ ์ํ ์์ธก ๊ฐ๋ฅํ ์ํ ์ปจํ ์ด๋์ด๋ฉฐ, ์ธ ๊ฐ์ง ๊ธฐ๋ณธ ์์น์ ๊ธฐ๋ฐ์ผ๋ก ๊ตฌ์ถ๋ฉ๋๋ค.
- ๋จ์ผ ์ง์ค ๊ณต๊ธ์(Single Source of Truth): ์ ํ๋ฆฌ์ผ์ด์ ์ ์ ์ฒด ์ํ๋ ๋จ์ผ ์คํ ์ด ๋ด์ ๋จ์ผ ๊ฐ์ฒด ํธ๋ฆฌ์ ์ ์ฅ๋ฉ๋๋ค.
- ์ํ๋ ์ฝ๊ธฐ ์ ์ฉ(State is Read-Only): ์ํ๋ฅผ ๋ณ๊ฒฝํ๋ ์ ์ผํ ๋ฐฉ๋ฒ์ ๋ฐ์ํ ์ผ์ ์ค๋ช ํ๋ ๊ฐ์ฒด์ธ ์ก์ (action)์ ๋ฐํํ๋ ๊ฒ์ ๋๋ค.
- ์์ ํจ์๋ก ๋ณ๊ฒฝ์ด ์ด๋ฃจ์ด์ง(Changes are Made with Pure Functions): ์ก์ ์ ์ํด ์ํ ํธ๋ฆฌ๊ฐ ์ด๋ป๊ฒ ๋ณํ๋๋์ง ์ง์ ํ๋ ค๋ฉด ์์ ๋ฆฌ๋์(reducer)๋ฅผ ์์ฑํฉ๋๋ค.
์ด ๋จ๋ฐฉํฅ ๋ฐ์ดํฐ ํ๋ฆ์ ๋๋ฒ๊น ๋ฐ ์๊ฐ์ด ์ง๋จ์ ๋ฐ๋ผ ์ํ๊ฐ ์ด๋ป๊ฒ ๋ณํ๋์ง ์ดํดํ๋ ๋ฐ ์์ฒญ๋ ์ด์ ์ ์ ๊ณตํฉ๋๋ค. ๊ทธ๋ฌ๋ ์์ JavaScript ํ๊ฒฝ์์๋ ๋ช ์์ ์ธ ํ์ ์ ์๊ฐ ๋ถ์กฑํ์ฌ ์ด๋ฌํ ์์ธก ๊ฐ๋ฅ์ฑ์ด ํผ์๋ ์ ์์ต๋๋ค. ๋ค์์ ์ผ๋ฐ์ ์ธ ์ทจ์ฝ์ ์ ๋๋ค:
- ์คํ๋ก ์ธํ ์ค๋ฅ: ์ก์ ํ์ ๋ฌธ์์ด ๋๋ ํ์ด๋ก๋ ์์ฑ์ ๊ฐ๋จํ ์คํ๋ ๋ฐํ์, ์ ์ฌ์ ์ผ๋ก๋ ํ๋ก๋์ ํ๊ฒฝ์์๊น์ง ๋ฐ๊ฒฌ๋์ง ์์ ์ ์์ต๋๋ค.
- ์ผ๊ด๋์ง ์์ ์ํ ํํ: ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ค๋ฅธ ๋ถ๋ถ์ด ๋์ผํ ์ํ ์กฐ๊ฐ์ ๋ํด ์๋์น ์๊ฒ ๋ค๋ฅธ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ ํ์ฌ ์๊ธฐ์น ์์ ๋์์ผ๋ก ์ด์ด์ง ์ ์์ต๋๋ค.
- ๋ฆฌํฉํ ๋ง ์ ๋ชฝ: ์ํ์ ํํ ๋๋ ์ก์ ์ ํ์ด๋ก๋๋ฅผ ๋ณ๊ฒฝํ๋ ค๋ฉด ์ํฅ์ ๋ฐ๋ ๋ชจ๋ ๋ฆฌ๋์, ์ ๋ ํฐ ๋ฐ ์ปดํฌ๋ํธ๋ฅผ ๊ผผ๊ผผํ๊ฒ ์๋์ผ๋ก ํ์ธํด์ผ ํ๋ฉฐ, ์ด ๊ณผ์ ์ ์ฌ๋์ ์ค์์ ์ทจ์ฝํฉ๋๋ค.
- ๋์ ๊ฐ๋ฐ์ ๊ฒฝํ(DX): ํ์ ํํธ๊ฐ ์์ผ๋ฉด ๊ฐ๋ฐ์, ํนํ ์ฝ๋๋ฒ ์ด์ค์ ์ต์ํ์ง ์๊ฑฐ๋ ๋ค๋ฅธ ์๊ฐ๋์ ์๋ ํ์์ด ๋น๋๊ธฐ์ ์ผ๋ก ํ์ ํ๋ ๊ฒฝ์ฐ ๋ฐ์ดํฐ ๊ตฌ์กฐ ๋ฐ ํจ์ ์๊ทธ๋์ฒ๋ฅผ ์ดํดํ๊ธฐ ์ํด ๋์์์ด ๋ฌธ์๋ ๊ธฐ์กด ์ฝ๋๋ฅผ ์ฐธ์กฐํด์ผ ํฉ๋๋ค.
์ด๋ฌํ ์ทจ์ฝ์ ์ ์ง์ ์ ์ธ ์ค์๊ฐ ํต์ ์ด ์ ํ๋ ์ ์๋ ๋ถ์ฐ ํ์์ ๋์ฑ ์ฌํ๋ฉ๋๋ค. ๊ฒฌ๊ณ ํ ํ์ ์์คํ ์ ๋ชจ๋ ๊ฐ๋ฐ์๊ฐ ๋ชจ๊ตญ์ด๋ ์๊ฐ๋์ ๊ด๊ณ์์ด ์์กดํ ์ ์๋ ๊ณตํต ์ธ์ด, ์ฆ ๋ณดํธ์ ์ธ ๊ณ์ฝ์ด ๋ฉ๋๋ค.
TypeScript์ ์ฅ์ : ์ ์ ํ์ดํ์ด ๊ธ๋ก๋ฒ ๊ท๋ชจ์์ ์ค์ํ ์ด์
JavaScript์ ์์ ์งํฉ์ธ TypeScript๋ ์ ์ ํ์ดํ์ ์น ๊ฐ๋ฐ์ ์ต์ ์ ์ผ๋ก ๊ฐ์ ธ์ต๋๋ค. Redux์๊ฒ ์์ด ์ด๋ ๋จ์ํ ์ถ๊ฐ์ ์ธ ๊ธฐ๋ฅ์ด ์๋๋ผ, ๋ณํ๋ฅผ ์ด๋๋ ๊ธฐ๋ฅ์ ๋๋ค. ํนํ ๊ตญ์ ์ ์ธ ๊ฐ๋ฐ ํ๊ฒฝ์์ TypeScript๊ฐ Redux ์ํ ๊ด๋ฆฌ์ ํ์์ ์ธ ์ด์ ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
- ์ปดํ์ผ ํ์ ์ค๋ฅ ๊ฐ์ง: TypeScript๋ ์ฝ๋๊ฐ ์คํ๋๊ธฐ๋ ์ ์ ์ปดํ์ผ ์ค์ ๋ฐฉ๋ํ ์ข ๋ฅ์ ์ค๋ฅ๋ฅผ ์ฐพ์๋ ๋๋ค. ์ด๋ ์คํ, ํ์ ๋ถ์ผ์น, ์๋ชป๋ API ์ฌ์ฉ์ด IDE์์ ์ฆ์ ํ๋๊ทธ๋์ด ์๋ง์ ๋๋ฒ๊น ์๊ฐ์ ์ ์ฝํด์ค๋ค๋ ์๋ฏธ์ ๋๋ค.
- ํฅ์๋ ๊ฐ๋ฐ์ ๊ฒฝํ(DX): ํ๋ถํ ํ์ ์ ๋ณด๋ฅผ ํตํด IDE๋ ์ง๋ฅ์ ์ธ ์๋ ์์ฑ, ๋งค๊ฐ๋ณ์ ํํธ ๋ฐ ํ์ ๊ธฐ๋ฅ์ ์ ๊ณตํ ์ ์์ต๋๋ค. ์ด๋ ํนํ ๋๊ท๋ชจ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ต์ํ์ง ์์ ๋ถ๋ถ์ ํ์ํ๋ ๊ฐ๋ฐ์๋ ์ ์ธ๊ณ ์ด๋์์๋ ์๋ก์ด ํ์์ ์จ๋ณด๋ฉํ๋ ๋ฐ ์์ด ์์ฐ์ฑ์ ํฌ๊ฒ ํฅ์์ํต๋๋ค.
- ๊ฒฌ๊ณ ํ ๋ฆฌํฉํ ๋ง: ํ์ ์ ์๋ฅผ ๋ณ๊ฒฝํ๋ฉด TypeScript๋ ์ฝ๋๋ฒ ์ด์ค์์ ์ ๋ฐ์ดํธ๊ฐ ํ์ํ ๋ชจ๋ ์์น๋ฅผ ์๋ดํฉ๋๋ค. ์ด๋ฅผ ํตํด ๋๊ท๋ชจ ๋ฆฌํฉํ ๋ง์ด ์ํํ ์ถ์ธก ๊ฒ์์ด ์๋, ์์ ๊ฐ ์๊ณ ์ฒด๊ณ์ ์ธ ํ๋ก์ธ์ค๊ฐ ๋ฉ๋๋ค.
- ์์ฒด ๋ฌธ์ํ ์ฝ๋: ํ์ ์ ์์๋๋ ๋ฐ์ดํฐ ํํ์ ํจ์์ ์๊ทธ๋์ฒ๋ฅผ ์ค๋ช ํ๋ ์ด์์๋ ๋ฌธ์ ์ญํ ์ ํฉ๋๋ค. ์ด๋ ๊ธ๋ก๋ฒ ํ์๊ฒ ๋งค์ฐ ์ค์ํ๋ฉฐ, ์ธ๋ถ ๋ฌธ์์ ๋ํ ์์กด๋๋ฅผ ์ค์ด๊ณ ์ฝ๋๋ฒ ์ด์ค ์ํคํ ์ฒ์ ๋ํ ๊ณต์ ๋ ์ดํด๋ฅผ ๋ณด์ฅํฉ๋๋ค.
- ํฅ์๋ ์ฝ๋ ํ์ง ๋ฐ ์ ์ง ๋ณด์์ฑ: ์๊ฒฉํ ๊ณ์ฝ์ ๊ฐ์ ํจ์ผ๋ก์จ TypeScript๋ ๋ณด๋ค ์ ์คํ๊ณ ์ฌ๋ ค ๊น์ API ์ค๊ณ๋ฅผ ์ฅ๋ คํ์ฌ ์๊ฐ์ด ์ง๋จ์ ๋ฐ๋ผ ์ฐ์ํ๊ฒ ๋ฐ์ ํ ์ ์๋ ๊ณ ํ์ง์ ์ ์ง ๋ณด์ ๊ฐ๋ฅํ ์ฝ๋๋ฒ ์ด์ค๋ก ์ด์ด์ง๋๋ค.
- ํ์ฅ์ฑ ๋ฐ ์ ๋ขฐ์ฑ: ์ ํ๋ฆฌ์ผ์ด์ ์ด ์ฑ์ฅํ๊ณ ๋ ๋ง์ ๊ฐ๋ฐ์๊ฐ ๊ธฐ์ฌํจ์ ๋ฐ๋ผ ํ์ ์์ ์ฑ์ ์ค์ํ ์ ๋ขฐ ๊ณ์ธต์ ์ ๊ณตํฉ๋๋ค. ์จ๊ฒจ์ง ํ์ ๊ด๋ จ ๋ฒ๊ทธ๋ฅผ ๋์ ํ ๊ฑฑ์ ์์ด ํ๊ณผ ๊ธฐ๋ฅ์ ํ์ฅํ ์ ์์ต๋๋ค.
๊ตญ์ ํ์๊ฒ TypeScript๋ ๋ฒ์ฉ ๋ฒ์ญ๊ธฐ ์ญํ ์ ํ์ฌ ์ธํฐํ์ด์ค๋ฅผ ํ์คํํ๊ณ ๋ค์ํ ์ฝ๋ฉ ์คํ์ผ์ด๋ ์์ฌ์ํต ๋ฏธ๋ฌํจ์์ ๋ฐ์ํ ์ ์๋ ๋ชจํธ์ฑ์ ์ค์ ๋๋ค. ์ด๋ ๋ฐ์ดํฐ ๊ณ์ฝ์ ๋ํ ์ผ๊ด๋ ์ดํด๋ฅผ ๊ฐ์ ํ๋ฉฐ, ์ง๋ฆฌ์ ๋ฐ ๋ฌธํ์ ๊ฒฝ๊ณ๋ฅผ ๋์ด ์ํํ ํ์ ์ ์ํด ํ์์ ์ ๋๋ค.
ํ์ ์์ Redux์ ๊ตฌ์ฑ ์์
Redux ์คํ ์ด์ ๊ธฐ๋ณธ ์์๋ถํฐ ์์ํ์ฌ ์ค์ง์ ์ธ ๊ตฌํ์ ๋ํด ์์ธํ ์์๋ณด๊ฒ ์ต๋๋ค.
1. ์ ์ญ ์ํ ํ์ดํ: `RootState`
์์ ํ ํ์ ์์ ํ Redux ์ ํ๋ฆฌ์ผ์ด์ ์ ํฅํ ์ฒซ ๋ฒ์งธ ๋จ๊ณ๋ ์ ์ฒด ์ ํ๋ฆฌ์ผ์ด์ ์ํ์ ํํ๋ฅผ ์ ์ํ๋ ๊ฒ์ ๋๋ค. ์ด๋ ์ผ๋ฐ์ ์ผ๋ก ๋ฃจํธ ์ํ์ ๋ํ ์ธํฐํ์ด์ค ๋๋ ํ์ ๋ณ์นญ์ ์์ฑํ์ฌ ์ํ๋ฉ๋๋ค. ์ข ์ข ์ด๋ ๋ฃจํธ ๋ฆฌ๋์์์ ์ง์ ์ถ๋ก ํ ์ ์์ต๋๋ค.
์์: `RootState` ์ ์
// store/index.ts
import { combineReducers } from 'redux';
import userReducer from './user/reducer';
import productsReducer from './products/reducer';
const rootReducer = combineReducers({
user: userReducer,
products: productsReducer,
});
export type RootState = ReturnType<typeof rootReducer>;
์ฌ๊ธฐ์ ReturnType<typeof rootReducer>๋ rootReducer ํจ์์ ๋ฐํ ํ์
์ ์ถ๋ก ํ๋ ๊ฐ๋ ฅํ TypeScript ์ ํธ๋ฆฌํฐ์ด๋ฉฐ, ์ด๋ ์ ํํ ์ ์ญ ์ํ์ ํํ์
๋๋ค. ์ด ์ ๊ทผ ๋ฐฉ์์ ์ํ ์ฌ๋ผ์ด์ค๋ฅผ ์ถ๊ฐํ๊ฑฐ๋ ์์ ํ ๋ RootState ํ์
์ด ์๋์ผ๋ก ์
๋ฐ์ดํธ๋๋๋ก ๋ณด์ฅํ์ฌ ์๋ ๋๊ธฐํ๋ฅผ ์ต์ํํฉ๋๋ค.
2. ์ก์ ์ ์: ์ด๋ฒคํธ์ ์ ๋ฐ์ฑ
์ก์ ์ ๋ฐ์ํ ์ผ์ ์ค๋ช ํ๋ ์ผ๋ฐ JavaScript ๊ฐ์ฒด์ ๋๋ค. ํ์ ์์ ์ธ๊ณ์์ ์ด ๊ฐ์ฒด๋ค์ ์๊ฒฉํ ๊ตฌ์กฐ๋ฅผ ์ค์ํด์ผ ํฉ๋๋ค. ์ฐ๋ฆฌ๋ ๊ฐ ์ก์ ์ ๋ํ ์ธํฐํ์ด์ค๋ฅผ ์ ์ํ๊ณ ๋ชจ๋ ๊ฐ๋ฅํ ์ก์ ์ ์ ๋์จ ํ์ ์ ์์ฑํจ์ผ๋ก์จ ์ด๋ฅผ ๋ฌ์ฑํฉ๋๋ค.
์์: ์ก์ ํ์ดํ
// store/user/actions.ts
export const FETCH_USER_REQUEST = 'FETCH_USER_REQUEST';
export const FETCH_USER_SUCCESS = 'FETCH_USER_SUCCESS';
export const FETCH_USER_FAILURE = 'FETCH_USER_FAILURE';
export interface FetchUserRequestAction {
type: typeof FETCH_USER_REQUEST;
}
export interface FetchUserSuccessAction {
type: typeof FETCH_USER_SUCCESS;
payload: { id: string; name: string; email: string; country: string; };
}
export interface FetchUserFailureAction {
type: typeof FETCH_USER_FAILURE;
payload: { error: string; };
}
export type UserActionTypes =
| FetchUserRequestAction
| FetchUserSuccessAction
| FetchUserFailureAction;
// Action Creators
export const fetchUserRequest = (): FetchUserRequestAction => ({
type: FETCH_USER_REQUEST,
});
export const fetchUserSuccess = (user: { id: string; name: string; email: string; country: string; }): FetchUserSuccessAction => ({
type: FETCH_USER_SUCCESS,
payload: user,
});
export const fetchUserFailure = (error: string): FetchUserFailureAction => ({
type: FETCH_USER_FAILURE,
payload: { error },
});
UserActionTypes ์ ๋์จ ํ์
์ ๋งค์ฐ ์ค์ํฉ๋๋ค. ์ด๋ ์ฌ์ฉ์ ๊ด๋ฆฌ์ ๊ด๋ จ๋ ์ก์
์ด ๊ฐ์ง ์ ์๋ ๋ชจ๋ ๊ฐ๋ฅํ ํํ๋ฅผ TypeScript์ ์๋ ค์ค๋๋ค. ์ด๋ฅผ ํตํด ๋ฆฌ๋์์์ ์ฒ ์ ํ ๊ฒ์ฌ๋ฅผ ๊ฐ๋ฅํ๊ฒ ํ๊ณ , ๋์คํจ์น๋๋ ๋ชจ๋ ์ก์
์ด ์ด๋ฌํ ๋ฏธ๋ฆฌ ์ ์๋ ํ์
์ค ํ๋๋ฅผ ์ค์ํ๋๋ก ๋ณด์ฅํฉ๋๋ค.
3. ๋ฆฌ๋์: ํ์ ์์ ์ ํ ๋ณด์ฅ
๋ฆฌ๋์๋ ํ์ฌ ์ํ์ ์ก์ ์ ๋ฐ์ ์๋ก์ด ์ํ๋ฅผ ๋ฐํํ๋ ์์ ํจ์์ ๋๋ค. ๋ฆฌ๋์๋ฅผ ํ์ดํํ๋ ๊ฒ์ ๋ค์ด์ค๋ ์ํ์ ์ก์ , ๊ทธ๋ฆฌ๊ณ ๋๊ฐ๋ ์ํ๊ฐ ์ ์๋ ํ์ ๊ณผ ์ผ์นํ๋์ง ํ์ธํ๋ ๊ฒ์ ํฌํจํฉ๋๋ค.
์์: ๋ฆฌ๋์ ํ์ดํ
// store/user/reducer.ts
import { UserActionTypes, FETCH_USER_REQUEST, FETCH_USER_SUCCESS, FETCH_USER_FAILURE } from './actions';
interface UserState {
data: { id: string; name: string; email: string; country: string; } | null;
loading: boolean;
error: string | null;
}
const initialState: UserState = {
data: null,
loading: false,
error: null,
};
const userReducer = (state: UserState = initialState, action: UserActionTypes): UserState => {
switch (action.type) {
case FETCH_USER_REQUEST:
return { ...state, loading: true, error: null };
case FETCH_USER_SUCCESS:
return { ...state, loading: false, data: action.payload };
case FETCH_USER_FAILURE:
return { ...state, loading: false, error: action.payload.error };
default:
return state;
}
};
export default userReducer;
case ๋ธ๋ก ๋ด์์ TypeScript๊ฐ action์ ํ์
์ ์ด๋ป๊ฒ ์ดํดํ๋์ง ์ฃผ๋ชฉํ์ธ์(์: FETCH_USER_SUCCESS ๋ด์์ action.payload๋ { id: string; name: string; email: string; country: string; }์ผ๋ก ์ฌ๋ฐ๋ฅด๊ฒ ํ์ดํ๋ฉ๋๋ค). ์ด๋ ์๋ณ๋ ์ ๋์จ(discriminated unions)์ผ๋ก ์๋ ค์ ธ ์์ผ๋ฉฐ, Redux๋ฅผ ์ํ TypeScript์ ๊ฐ์ฅ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ ์ค ํ๋์
๋๋ค.
4. ์คํ ์ด: ๋ชจ๋ ๊ฒ์ ํ๋ฐ ๋ชจ์ผ๊ธฐ
๋ง์ง๋ง์ผ๋ก, Redux ์คํ ์ด ์์ฒด๋ฅผ ํ์ดํํ๊ณ ๋์คํจ์น ํจ์๊ฐ ๊ฐ๋ฅํ ๋ชจ๋ ์ก์ ์ ์ฌ๋ฐ๋ฅด๊ฒ ์ธ์ํ๋๋ก ํด์ผ ํฉ๋๋ค.
์์: Redux Toolkit์ `configureStore`๋ก ์คํ ์ด ํ์ดํํ๊ธฐ
redux์ createStore๋ ํ์ดํํ ์ ์์ง๋ง, Redux Toolkit์ configureStore๋ ๋ฐ์ด๋ ํ์
์ถ๋ก ์ ์ ๊ณตํ๋ฉฐ ์ต์ Redux ์ ํ๋ฆฌ์ผ์ด์
์ ๊ถ์ฅ๋๋ ์ ๊ทผ ๋ฐฉ์์
๋๋ค.
// store/index.ts (updated with configureStore)
import { configureStore } from '@reduxjs/toolkit';
import userReducer from './user/reducer';
import productsReducer from './products/reducer';
const store = configureStore({
reducer: {
user: userReducer,
products: productsReducer,
},
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
export default store;
์ฌ๊ธฐ์ RootState๋ store.getState์์ ์ถ๋ก ๋๊ณ , ๊ฒฐ์ ์ ์ผ๋ก AppDispatch๋ store.dispatch์์ ์ถ๋ก ๋ฉ๋๋ค. ์ด AppDispatch ํ์
์ ์ ํ๋ฆฌ์ผ์ด์
์ ๋ชจ๋ ๋์คํจ์น ํธ์ถ์ด ์ ์ญ ์ก์
์ ๋์จ ํ์
์ ๋ถํฉํ๋ ์ก์
์ ๋ณด๋ด๋๋ก ๋ณด์ฅํ๊ธฐ ๋๋ฌธ์ ๋งค์ฐ ์ค์ํฉ๋๋ค. ์กด์ฌํ์ง ์๊ฑฐ๋ ์๋ชป๋ ํ์ด๋ก๋๋ฅผ ๊ฐ์ง ์ก์
์ ๋์คํจ์นํ๋ ค๊ณ ํ๋ฉด TypeScript๊ฐ ์ฆ์ ํ๋๊ทธํฉ๋๋ค.
React-Redux ํตํฉ: UI ๊ณ์ธต ํ์ดํํ๊ธฐ
React์ ํจ๊ป ์์
ํ ๋ Redux๋ฅผ ํตํฉํ๋ ค๋ฉด useSelector ๋ฐ useDispatch์ ๊ฐ์ ํ
์ ๋ํ ํน์ ํ์ดํ์ด ํ์ํฉ๋๋ค.
1. `useSelector`: ์์ ํ ์ํ ์๋น
useSelector ํ
์ ์ปดํฌ๋ํธ๊ฐ Redux ์คํ ์ด์์ ๋ฐ์ดํฐ๋ฅผ ์ถ์ถํ ์ ์๋๋ก ํฉ๋๋ค. ์ด๋ฅผ ํ์
์์ ํ๊ฒ ๋ง๋ค๋ ค๋ฉด RootState์ ๋ํด ์๋ ค์ฃผ์ด์ผ ํฉ๋๋ค.
2. `useDispatch`: ์์ ํ ์ก์ ๋์คํจ์น
useDispatch ํ
์ dispatch ํจ์์ ๋ํ ์ ๊ทผ์ ์ ๊ณตํฉ๋๋ค. ์ด๋ AppDispatch ํ์
์ ๋ํด ์์์ผ ํฉ๋๋ค.
3. ์ ์ญ ์ฌ์ฉ์ ์ํ ํ์ ์ด ์ง์ ๋ ํ ์์ฑ
๋ชจ๋ ์ปดํฌ๋ํธ์์ useSelector ๋ฐ useDispatch์ ํ์
์ ๋ฐ๋ณต์ ์ผ๋ก ์ฃผ์ ์ฒ๋ฆฌํ๋ ๊ฒ์ ํผํ๊ธฐ ์ํด, ์ผ๋ฐ์ ์ผ๋ก ๊ฐ๋ ฅํ ๊ถ์ฅ๋๋ ํจํด์ ์ด ํ
๋ค์ ๋ฏธ๋ฆฌ ํ์
์ด ์ง์ ๋ ๋ฒ์ ์ ์์ฑํ๋ ๊ฒ์
๋๋ค.
์์: ํ์ ์ด ์ง์ ๋ React-Redux ํ
// hooks.ts or store/hooks.ts
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
import type { RootState, AppDispatch } from './store'; // Adjust path as needed
// Use throughout your app instead of plain `useDispatch` and `useSelector`
export const useAppDispatch: () => AppDispatch = useDispatch;
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
์ด์ React ์ปดํฌ๋ํธ ์ด๋์์๋ useAppDispatch์ useAppSelector๋ฅผ ์ฌ์ฉํ ์ ์์ผ๋ฉฐ, TypeScript๋ ์์ ํ ํ์
์์ ์ฑ๊ณผ ์๋ ์์ฑ์ ์ ๊ณตํฉ๋๋ค. ์ด๋ ๋๊ท๋ชจ ๊ตญ์ ํ์ ํนํ ์ ์ตํ๋ฉฐ, ๋ชจ๋ ๊ฐ๋ฐ์๊ฐ ๊ฐ ํ๋ก์ ํธ์ ํน์ ํ์
์ ๊ธฐ์ตํ ํ์ ์์ด ํ
์ ์ผ๊ด๋๊ณ ์ฌ๋ฐ๋ฅด๊ฒ ์ฌ์ฉํ๋๋ก ๋ณด์ฅํฉ๋๋ค.
์์: ์ปดํฌ๋ํธ์์์ ์ฌ์ฉ๋ฒ:
// components/UserProfile.tsx
import React from 'react';
import { useAppSelector, useAppDispatch } from '../hooks';
import { fetchUserRequest } from '../store/user/actions';
const UserProfile: React.FC = () => {
const user = useAppSelector((state) => state.user.data);
const loading = useAppSelector((state) => state.user.loading);
const error = useAppSelector((state) => state.user.error);
const dispatch = useAppDispatch();
React.useEffect(() => {
if (!user) {
dispatch(fetchUserRequest());
}
}, [user, dispatch]);
if (loading) return <p>์ฌ์ฉ์ ๋ฐ์ดํฐ๋ฅผ ๋ก๋ฉ ์ค์
๋๋ค...</p>;
if (error) return <p>์ค๋ฅ: {error}</p>;
if (!user) return <p>์ฌ์ฉ์ ๋ฐ์ดํฐ๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค. ๋ค์ ์๋ํด์ฃผ์ธ์.</p>;
return (
<div>
<h2>์ฌ์ฉ์ ํ๋กํ</h2>
<p><strong>์ด๋ฆ:</strong> {user.name}</p>
<p><strong>์ด๋ฉ์ผ:</strong> {user.email}</p>
<p><strong>๊ตญ๊ฐ:</strong> {user.country}</p>
</div>
);
};
export default UserProfile;
์ด ์ปดํฌ๋ํธ์์ user, loading, error๋ ๋ชจ๋ ์ฌ๋ฐ๋ฅด๊ฒ ํ์ดํ๋์์ผ๋ฉฐ, dispatch(fetchUserRequest())๋ AppDispatch ํ์
์ ๋ํด ๊ฒ์ฌ๋ฉ๋๋ค. ์กด์ฌํ์ง ์๋ ์์ฑ์ ์ ๊ทผํ๊ฑฐ๋ ์ ํจํ์ง ์์ ์ก์
์ ๋์คํจ์นํ๋ ค๋ ์๋๋ ์ปดํ์ผ ํ์ ์ค๋ฅ๋ฅผ ๋ฐ์์ํต๋๋ค.
Redux Toolkit (RTK)์ผ๋ก ํ์ ์์ ์ฑ ํฅ์์ํค๊ธฐ
Redux Toolkit์ ํจ์จ์ ์ธ Redux ๊ฐ๋ฐ์ ์ํ ๊ณต์์ ์ด๊ณ ์๊ฒฌ์ด ๋ฐ์๋, ๋ชจ๋ ๊ธฐ๋ฅ์ด ํฌํจ๋ ๋๊ตฌ ์ธํธ์ ๋๋ค. ์ด๋ Redux ๋ก์ง์ ์์ฑํ๋ ๊ณผ์ ์ ์๋นํ ๋จ์ํํ๋ฉฐ, ๊ฒฐ์ ์ ์ผ๋ก ๋ฐ์ด๋ ํ์ ์ถ๋ก ์ ๊ธฐ๋ณธ์ผ๋ก ์ ๊ณตํ์ฌ ํ์ ์์ Redux๋ฅผ ๋์ฑ ์ฝ๊ฒ ์ ๊ทผํ ์ ์๋๋ก ํฉ๋๋ค.
1. `createSlice`: ๊ฐ์ํ๋ ๋ฆฌ๋์ ๋ฐ ์ก์
createSlice๋ ์ก์
์์ฑ์์ ๋ฆฌ๋์ ์์ฑ์ ๋จ์ผ ํจ์๋ก ๊ฒฐํฉํฉ๋๋ค. ์ด๋ ๋ฆฌ๋์์ ํค๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ก์
ํ์
๊ณผ ์ก์
์์ฑ์๋ฅผ ์๋์ผ๋ก ์์ฑํ๊ณ ๊ฐ๋ ฅํ ํ์
์ถ๋ก ์ ์ ๊ณตํฉ๋๋ค.
์์: ์ฌ์ฉ์ ๊ด๋ฆฌ๋ฅผ ์ํ `createSlice`
// store/user/userSlice.ts
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
interface UserState {
data: { id: string; name: string; email: string; country: string; } | null;
loading: boolean;
error: string | null;
}
const initialState: UserState = {
data: null,
loading: false,
error: null,
};
const userSlice = createSlice({
name: 'user',
initialState,
reducers: {
fetchUserRequest: (state) => {
state.loading = true;
state.error = null;
},
fetchUserSuccess: (state, action: PayloadAction<{ id: string; name: string; email: string; country: string; }>) => {
state.loading = false;
state.data = action.payload;
},
fetchUserFailure: (state, action: PayloadAction<string>) => {
state.loading = false;
state.error = action.payload;
},
},
});
export const { fetchUserRequest, fetchUserSuccess, fetchUserFailure } = userSlice.actions;
export default userSlice.reducer;
Redux Toolkit์ PayloadAction ์ฌ์ฉ์ ์ฃผ๋ชฉํ์ธ์. ์ด ์ ๋ค๋ฆญ ํ์
์ ์ก์
์ payload ํ์
์ ๋ช
์์ ์ผ๋ก ์ ์ํ ์ ์๊ฒ ํ์ฌ ๋ฆฌ๋์ ๋ด์ ํ์
์์ ์ฑ์ ๋์ฑ ํฅ์์ํต๋๋ค. RTK์ ๋ด์ฅ๋ Immer ํตํฉ์ ๋ฆฌ๋์ ๋ด์์ ์ง์ ์ ์ธ ์ํ ๋ณํ์ ํ์ฉํ๋ฉฐ, ์ด๋ ๋ถ๋ณ ์
๋ฐ์ดํธ๋ก ๋ณํ๋์ด ๋ฆฌ๋์ ๋ก์ง์ ํจ์ฌ ๋ ์ฝ๊ธฐ ์ฝ๊ณ ๊ฐ๊ฒฐํ๊ฒ ๋ง๋ญ๋๋ค.
2. `createAsyncThunk`: ๋น๋๊ธฐ ์์ ํ์ดํ
๋น๋๊ธฐ ์์
(API ํธ์ถ ๋ฑ) ์ฒ๋ฆฌ๋ Redux์ ์ผ๋ฐ์ ์ธ ํจํด์
๋๋ค. Redux Toolkit์ createAsyncThunk๋ ์ด๋ฅผ ํฌ๊ฒ ๋จ์ํํ๊ณ ๋น๋๊ธฐ ์ก์
์ ์ ์ฒด ๋ผ์ดํ์ฌ์ดํด(pending, fulfilled, rejected)์ ๋ํ ๋ฐ์ด๋ ํ์
์์ ์ฑ์ ์ ๊ณตํฉ๋๋ค.
์์: ์ฌ์ฉ์ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ๋ฅผ ์ํ `createAsyncThunk`
// store/user/userSlice.ts (continued)
import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit';
// ... (UserState and initialState remain the same)
interface FetchUserError {
message: string;
}
export const fetchUserById = createAsyncThunk<
{ id: string; name: string; email: string; country: string; }, // Return type of payload (fulfilled)
string, // Argument type for the thunk (userId)
{
rejectValue: FetchUserError; // Type for the reject value
}
>(
'user/fetchById',
async (userId: string, { rejectWithValue }) => {
try {
const response = await fetch(`https://api.example.com/users/${userId}`);
if (!response.ok) {
const errorData = await response.json();
return rejectWithValue({ message: errorData.message || 'Failed to fetch user' });
}
const userData: { id: string; name: string; email: string; country: string; } = await response.json();
return userData;
} catch (error: any) {
return rejectWithValue({ message: error.message || 'Network error' });
}
}
);
const userSlice = createSlice({
name: 'user',
initialState,
reducers: {
// ... (existing sync reducers if any)
},
extraReducers: (builder) => {
builder
.addCase(fetchUserById.pending, (state) => {
state.loading = true;
state.error = null;
})
.addCase(fetchUserById.fulfilled, (state, action) => {
state.loading = false;
state.data = action.payload;
})
.addCase(fetchUserById.rejected, (state, action) => {
state.loading = false;
state.error = action.payload?.message || 'Unknown error occurred.';
});
},
});
// ... (export actions and reducer)
createAsyncThunk์ ์ ๊ณต๋๋ ์ ๋ค๋ฆญ(๋ฐํ ํ์
, ์ธ์ ํ์
, Thunk API ๊ตฌ์ฑ)์ ๋น๋๊ธฐ ํ๋ฆ์ ์ธ์ฌํ๊ฒ ํ์ดํํ ์ ์๋๋ก ํฉ๋๋ค. TypeScript๋ extraReducers ๋ด์ fulfilled ๋ฐ rejected ์ผ์ด์ค์์ action.payload์ ํ์
์ ์ฌ๋ฐ๋ฅด๊ฒ ์ถ๋ก ํ์ฌ ๋ณต์กํ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ ์๋๋ฆฌ์ค์ ๋ํ ๊ฐ๋ ฅํ ํ์
์์ ์ฑ์ ์ ๊ณตํฉ๋๋ค.
3. RTK๋ก ์คํ ์ด ๊ตฌ์ฑ: `configureStore`
์์ ์ค๋ช
ํ๋ฏ์ด, configureStore๋ ๊ฐ๋ฐ ๋๊ตฌ, ๋ฏธ๋ค์จ์ด ๋ฐ ๋ฐ์ด๋ ํ์
์ถ๋ก ์ผ๋ก Redux ์คํ ์ด๋ฅผ ์๋์ผ๋ก ์ค์ ํ์ฌ ํ๋์ ์ด๊ณ ํ์
์์ ํ Redux ์ค์ ์ ์ด์์ด ๋ฉ๋๋ค.
๊ณ ๊ธ ๊ฐ๋ ๋ฐ ๋ชจ๋ฒ ์ฌ๋ก
๋ค์ํ ํ์ ์ํด ๊ฐ๋ฐ๋ ๋๊ท๋ชจ ์ ํ๋ฆฌ์ผ์ด์ ์์ ํ์ ์์ ์ฑ์ ์ต๋ํ ํ์ฉํ๋ ค๋ฉด ๋ค์ ๊ณ ๊ธ ๊ธฐ์ ๊ณผ ๋ชจ๋ฒ ์ฌ๋ก๋ฅผ ๊ณ ๋ คํ์ญ์์ค.
1. ๋ฏธ๋ค์จ์ด ํ์ดํ: `Thunk` ๋ฐ ์ฌ์ฉ์ ์ ์ ๋ฏธ๋ค์จ์ด
Redux์ ๋ฏธ๋ค์จ์ด๋ ์ข ์ข ์ก์ ์ ์กฐ์ํ๊ฑฐ๋ ์๋ก์ด ์ก์ ์ ๋์คํจ์นํ๋ ๊ฒ์ ํฌํจํฉ๋๋ค. ๋ฏธ๋ค์จ์ด๊ฐ ํ์ ์์ ํ์ง ํ์ธํ๋ ๊ฒ์ ๋งค์ฐ ์ค์ํฉ๋๋ค.
Redux Thunk์ ๊ฒฝ์ฐ, AppDispatch ํ์
(configureStore์์ ์ถ๋ก ๋จ)์ thunk ๋ฏธ๋ค์จ์ด์ ๋์คํจ์น ํ์
์ ์๋์ผ๋ก ํฌํจํฉ๋๋ค. ์ด๋ ํจ์(thunk)๋ฅผ ์ง์ ๋์คํจ์นํ ์ ์์ผ๋ฉฐ, TypeScript๊ฐ ํด๋น ์ธ์์ ๋ฐํ ํ์
์ ์ฌ๋ฐ๋ฅด๊ฒ ํ์ธํ ๊ฒ์์ ์๋ฏธํฉ๋๋ค.
์ฌ์ฉ์ ์ ์ ๋ฏธ๋ค์จ์ด์ ๊ฒฝ์ฐ, ์ผ๋ฐ์ ์ผ๋ก Dispatch์ RootState๋ฅผ ํ์ฉํ๋๋ก ์๊ทธ๋์ฒ๋ฅผ ์ ์ํ์ฌ ํ์
์ผ๊ด์ฑ์ ๋ณด์ฅํฉ๋๋ค.
์์: ๊ฐ๋จํ ์ฌ์ฉ์ ์ ์ ๋ก๊น ๋ฏธ๋ค์จ์ด (ํ์ดํ๋จ)
// store/middleware/logger.ts
import { Middleware } from 'redux';
import { RootState } from '../store';
import { UserActionTypes } from '../user/actions'; // or infer from root reducer actions
const loggerMiddleware: Middleware<{}, RootState, UserActionTypes> =
(store) => (next) => (action) => {
console.log('Dispatching:', action.type);
const result = next(action);
console.log('Next state:', store.getState());
return result;
};
export default loggerMiddleware;
2. ํ์ ์์ ์ฑ ์๋ ์ ๋ ํฐ ๋ฉ๋ชจ์ด์ ์ด์ (`reselect`)
์
๋ ํฐ๋ Redux ์ํ์์ ๊ณ์ฐ๋ ๋ฐ์ดํฐ๋ฅผ ํ์ํ๋ ํจ์์
๋๋ค. reselect์ ๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๋ฉ๋ชจ์ด์ ์ด์
์ ๊ฐ๋ฅํ๊ฒ ํ์ฌ ๋ถํ์ํ ๋ฆฌ๋ ๋๋ง์ ๋ฐฉ์งํฉ๋๋ค. ํ์
์์ ์
๋ ํฐ๋ ์ด๋ฌํ ํ์ ๊ณ์ฐ์ ์
๋ ฅ๊ณผ ์ถ๋ ฅ์ด ์ฌ๋ฐ๋ฅด๊ฒ ์ ์๋๋๋ก ๋ณด์ฅํฉ๋๋ค.
์์: ํ์ ์ด ์ง์ ๋ Reselect ์ ๋ ํฐ
// store/user/selectors.ts
import { createSelector } from '@reduxjs/toolkit'; // Re-export from reselect
import { RootState } from '../store';
const selectUserState = (state: RootState) => state.user;
export const selectActiveUsersInCountry = createSelector(
[selectUserState, (state: RootState, countryCode: string) => countryCode],
(userState, countryCode) =>
userState.data ? (userState.data.country === countryCode ? [userState.data] : []) : []
);
// Usage:
// const activeUsers = useAppSelector(state => selectActiveUsersInCountry(state, 'US'));
createSelector๋ ์
๋ ฅ ์
๋ ํฐ์ ์ถ๋ ฅ์ ํ์
์ ์ฌ๋ฐ๋ฅด๊ฒ ์ถ๋ก ํ์ฌ ํ์๋ ์ํ์ ๋ํ ์์ ํ ํ์
์์ ์ฑ์ ์ ๊ณตํฉ๋๋ค.
3. ๊ฒฌ๊ณ ํ ์ํ ํํ ์ค๊ณ
ํจ๊ณผ์ ์ธ ํ์ ์์ Redux๋ ์ ์ ์๋ ์ํ ํํ๋ก ์์ํฉ๋๋ค. ๋ค์์ ์ฐ์ ํ์ญ์์ค:
- ์ ๊ทํ(Normalization): ๊ด๊ณํ ๋ฐ์ดํฐ์ ๊ฒฝ์ฐ, ์ค๋ณต์ ํผํ๊ณ ์ ๋ฐ์ดํธ๋ฅผ ๋จ์ํํ๊ธฐ ์ํด ์ํ๋ฅผ ์ ๊ทํํ์ญ์์ค.
- ๋ถ๋ณ์ฑ(Immutability): ํญ์ ์ํ๋ฅผ ๋ถ๋ณ์ผ๋ก ์ทจ๊ธํ์ญ์์ค. TypeScript๋ ํนํ Immer(RTK์ ๋ด์ฅ)์ ๊ฒฐํฉ๋ ๋ ์ด๋ฅผ ๊ฐ์ ํ๋ ๋ฐ ๋์์ด ๋ฉ๋๋ค.
-
์ ํ์ ์์ฑ(Optional Properties):
?๋๋ ์ ๋์จ ํ์ (์:string | null)์ ์ฌ์ฉํ์ฌnull๋๋undefined์ผ ์ ์๋ ์์ฑ์ ๋ช ํํ๊ฒ ํ์ํ์ญ์์ค. -
์ํ๋ฅผ ์ํ Enum: ๋ฏธ๋ฆฌ ์ ์๋ ์ํ ๊ฐ(์:
'idle' | 'loading' | 'succeeded' | 'failed')์ TypeScript enum ๋๋ ๋ฌธ์์ด ๋ฆฌํฐ๋ด ํ์ ์ ์ฌ์ฉํ์ญ์์ค.
4. ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ค๋ฃจ๊ธฐ
Redux๋ฅผ ๋ค๋ฅธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ํตํฉํ ๋, ํญ์ ๊ณต์ TypeScript ํ์ดํ(์ข
์ข
npm์ @types ๋ฒ์์์ ์ฐพ์ ์ ์์)์ ํ์ธํ์ญ์์ค. ํ์ดํ์ ์ฌ์ฉํ ์ ์๊ฑฐ๋ ๋ถ์ถฉ๋ถํ ๊ฒฝ์ฐ, ํ์
์์ Redux ์คํ ์ด์์ ์ํํ ์ํธ ์์ฉ์ ํ์ฉํ๊ธฐ ์ํด ์ ์ธ ํ์ผ(.d.ts)์ ์์ฑํ์ฌ ํด๋น ํ์
์ ๋ณด๋ฅผ ๋ณด๊ฐํด์ผ ํ ์ ์์ต๋๋ค.
5. ํ์ ๋ชจ๋ํ
์ ํ๋ฆฌ์ผ์ด์
์ด ์ฑ์ฅํจ์ ๋ฐ๋ผ ํ์
์ ์ค์ ์ง์คํํ๊ณ ์ ๋ฆฌํ์ญ์์ค. ์ผ๋ฐ์ ์ธ ํจํด์ ๊ฐ ๋ชจ๋(์: store/user/types.ts) ๋ด์ ํด๋น ๋ชจ๋์ ์ํ, ์ก์
๋ฐ ์
๋ ํฐ์ ๋ํ ๋ชจ๋ ์ธํฐํ์ด์ค๋ฅผ ์ ์ํ๋ types.ts ํ์ผ์ ๊ฐ๋ ๊ฒ์
๋๋ค. ๊ทธ๋ฐ ๋ค์ ๋ชจ๋์ index.ts ๋๋ ์ฌ๋ผ์ด์ค ํ์ผ์์ ์ด๋ฅผ ๋ค์ ๋ด๋ณด๋
๋๋ค.
ํ์ ์์ Redux์ ์ผ๋ฐ์ ์ธ ํจ์ ๊ณผ ํด๊ฒฐ์ฑ
TypeScript๋ฅผ ์ฌ์ฉํ๋๋ผ๋ ์ผ๋ถ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค. ์ด๋ฅผ ์ธ์งํ๋ ๊ฒ์ ๊ฒฌ๊ณ ํ ์ค์ ์ ์ ์งํ๋ ๋ฐ ๋์์ด ๋ฉ๋๋ค.
1. 'any' ํ์ ์ค๋
TypeScript์ ์์ ๋ง์ ์ฐํํ๋ ๊ฐ์ฅ ์ฌ์ด ๋ฐฉ๋ฒ์ any ํ์
์ ์ฌ์ฉํ๋ ๊ฒ์
๋๋ค. any ํ์
์ ํน์ ํต์ ๋ ์๋๋ฆฌ์ค(์: ์ ๋ง ์ ์ ์๋ ์ธ๋ถ ๋ฐ์ดํฐ๋ฅผ ๋ค๋ฃฐ ๋)์ ์ ํฉํ์ง๋ง, any์ ๊ณผ๋ํ๊ฒ ์์กดํ๋ฉด ํ์
์์ ์ฑ์ ์ด์ ์ ์์ํฉ๋๋ค. unknown์ ์ฌ์ฉํ๊ธฐ ์ ์ ํ์
๋จ์ธ ๋๋ ์ถ์๊ฐ ํ์ํ๋ฏ๋ก ์ ์ฌ์ ์ธ ํ์
๋ถ์ผ์น๋ฅผ ๋ช
์์ ์ผ๋ก ์ฒ๋ฆฌํด์ผ ํ๋ฏ๋ก any ๋์ unknown์ ์ฌ์ฉํ๋๋ก ๋
ธ๋ ฅํ์ญ์์ค.
2. ์ํ ์์กด์ฑ
ํ์ผ์ด ์๋ก ํ์
์ ์ํ์ ์ผ๋ก ๊ฐ์ ธ์ฌ ๋ TypeScript๋ ์ด๋ฅผ ํด๊ฒฐํ๋ ๋ฐ ์ด๋ ค์์ ๊ฒช์ด ์ค๋ฅ๋ก ์ด์ด์ง ์ ์์ต๋๋ค. ์ด๋ ํ์
์ ์์ ๊ตฌํ์ด ๋๋ฌด ๋ฐ์ ํ๊ฒ ์ฝํ ์์ ๋ ์์ฃผ ๋ฐ์ํฉ๋๋ค. ํด๊ฒฐ์ฑ
: ํ์
์ ์๋ฅผ ์ ์ฉ ํ์ผ(์: types.ts)๋ก ๋ถ๋ฆฌํ๊ณ , ๋ฐํ์ ์ฝ๋ ์ํฌํธ์ ๊ตฌ๋ณ๋๋ ๋ช
ํํ๊ณ ๊ณ์ธต์ ์ธ ํ์
์ํฌํธ ๊ตฌ์กฐ๋ฅผ ๋ณด์ฅํ์ญ์์ค.
3. ๋๊ท๋ชจ ํ์ ์ ๋ํ ์ฑ๋ฅ ๊ณ ๋ ค ์ฌํญ
๊ทน๋๋ก ๋ณต์กํ๊ฑฐ๋ ๊น๊ฒ ์ค์ฒฉ๋ ํ์ ์ ๋๋๋ก TypeScript์ ์ธ์ด ์๋ฒ ์๋๋ฅผ ์ ํ์์ผ IDE ์๋ต์ฑ์ ์ํฅ์ ์ค ์ ์์ต๋๋ค. ๋๋ฌผ์ง๋ง, ์ด๋ฌํ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ฉด ํ์ ์ ๋จ์ํํ๊ฑฐ๋ ์ ํธ๋ฆฌํฐ ํ์ ์ ๋ ํจ์จ์ ์ผ๋ก ์ฌ์ฉํ๊ฑฐ๋ ๋ชจ๋๋ฆฌ์ ํ์ ์ ์๋ฅผ ๋ ์๊ณ ๊ด๋ฆฌํ๊ธฐ ์ฌ์ด ๋ถ๋ถ์ผ๋ก ๋๋๋ ๊ฒ์ ๊ณ ๋ คํ์ญ์์ค.
4. Redux, React-Redux ๋ฐ TypeScript ๊ฐ์ ๋ฒ์ ๋ถ์ผ์น
Redux, React-Redux, Redux Toolkit ๋ฐ TypeScript(๋ฐ ํด๋น @types ํจํค์ง) ๋ฒ์ ์ด ํธํ๋๋์ง ํ์ธํ์ญ์์ค. ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋ณ๊ฒฝ ์ฌํญ์ด ๋ค๋ฅธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ ํ์
์ค๋ฅ๋ฅผ ์ ๋ฐํ ์ ์์ต๋๋ค. ์ ๊ธฐ์ ์ผ๋ก ์
๋ฐ์ดํธํ๊ณ ๋ฆด๋ฆฌ์ค ๋
ธํธ๋ฅผ ํ์ธํ๋ฉด ์ด๋ฅผ ์ํํ ์ ์์ต๋๋ค.
ํ์ ์์ Redux์ ๊ธ๋ก๋ฒ ์ฅ์
ํ์ ์์ Redux๋ฅผ ๊ตฌํํ๊ธฐ๋ก ๊ฒฐ์ ํ๋ ๊ฒ์ ๊ธฐ์ ์ ์ธ ์ฐ์ํจ์ ํจ์ฌ ๋ฐ์ด๋์ต๋๋ค. ์ด๋ ํนํ ์ธ๊ณํ๋ ํ๊ฒฝ์์ ๊ฐ๋ฐ ํ์ด ์ด์๋๋ ๋ฐฉ์์ ์ง๋ํ ์ํฅ์ ๋ฏธ์นฉ๋๋ค:
- ๋ค๋ฌธํ ํ ํ์ : ํ์ ์ ๋ณดํธ์ ์ธ ๊ณ์ฝ์ ์ ๊ณตํฉ๋๋ค. ๋์ฟ์ ๊ฐ๋ฐ์๋ ๋ฐ๋์ ๋๋ฃ๊ฐ ์์ฑํ ์ฝ๋์ ์ฝ๋ฉ ์คํ์ผ์ด๋ ์ธ์ด์ ์ฐจ์ด์ ๊ด๊ณ์์ด ๊ณต์ ๋ ๋ช ํํ ํ์ ์ ์์ ๋ํด ์ปดํ์ผ๋ฌ๊ฐ ์ํธ ์์ฉ์ ๊ฒ์ฆํ ๊ฒ์ด๋ผ๋ ํ์ ์ ๊ฐ์ง๊ณ ํตํฉํ ์ ์์ต๋๋ค.
- ์ฅ๊ธฐ ํ๋ก์ ํธ์ ์ ์ง ๋ณด์์ฑ: ์ํฐํ๋ผ์ด์ฆ๊ธ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ข ์ข ์๋ ๋๋ ์ฌ์ง์ด ์์ญ ๋ ์ ๊ฑธ์ณ ์๋ช ์ ๊ฐ์ง๋๋ค. ํ์ ์์ ์ฑ์ ๊ฐ๋ฐ์๊ฐ ์ค๊ณ ๊ฐ๊ณ , ์ ํ๋ฆฌ์ผ์ด์ ์ด ๋ฐ์ ํจ์ ๋ฐ๋ผ ํต์ฌ ์ํ ๊ด๋ฆฌ ๋ก์ง์ด ๊ฒฌ๊ณ ํ๊ณ ์ดํดํ๊ธฐ ์ฝ๊ฒ ์ ์ง๋๋๋ก ๋ณด์ฅํ์ฌ ์ ์ง ๋ณด์ ๋น์ฉ์ ํฌ๊ฒ ์ค์ด๊ณ ํ๊ท๋ฅผ ๋ฐฉ์งํฉ๋๋ค.
- ๋ณต์กํ ์์คํ ์ ํ์ฅ์ฑ: ์ ํ๋ฆฌ์ผ์ด์ ์ด ๋ ๋ง์ ๊ธฐ๋ฅ, ๋ชจ๋ ๋ฐ ํตํฉ์ ํฌํจํ๋๋ก ์ฑ์ฅํจ์ ๋ฐ๋ผ ์ํ ๊ด๋ฆฌ ๊ณ์ธต์ ์์ฒญ๋๊ฒ ๋ณต์กํด์ง ์ ์์ต๋๋ค. ํ์ ์์ Redux๋ ์๋์ ์ธ ๊ธฐ์ ๋ถ์ฑ๋ ๊ธ์ฆํ๋ ๋ฒ๊ทธ๋ฅผ ๋ฐ์์ํค์ง ์๊ณ ํ์ฅํ๋ ๋ฐ ํ์ํ ๊ตฌ์กฐ์ ๋ฌด๊ฒฐ์ฑ์ ์ ๊ณตํฉ๋๋ค.
- ์จ๋ณด๋ฉ ์๊ฐ ๋จ์ถ: ๊ตญ์ ํ์ ํฉ๋ฅํ๋ ์ ๊ท ๊ฐ๋ฐ์์๊ฒ ํ์ ์์ ์ฝ๋๋ฒ ์ด์ค๋ ์ ๋ณด์ ๋ณด๊ณ ์ ๋๋ค. IDE์ ์๋ ์์ฑ ๋ฐ ํ์ ํํธ๋ ์ฆ์ ๋ฉํ ์ญํ ์ ํ์ฌ ์ ๊ท ๊ตฌ์ฑ์์ด ํ์ ์์ฐ์ ์ธ ๊ตฌ์ฑ์์ด ๋๋ ๋ฐ ๊ฑธ๋ฆฌ๋ ์๊ฐ์ ํฌ๊ฒ ๋จ์ถํฉ๋๋ค.
- ๋ฐฐํฌ์ ๋ํ ์ ๋ขฐ: ์ ์ฌ์ ์ค๋ฅ์ ์๋น ๋ถ๋ถ์ด ์ปดํ์ผ ํ์์ ํฌ์ฐฉ๋๋ฏ๋ก, ํ์ ์ผ๋ฐ์ ์ธ ๋ฐ์ดํฐ ๊ด๋ จ ๋ฒ๊ทธ๊ฐ ํ๋ก๋์ ์ ์นจํฌํ ๊ฐ๋ฅ์ฑ์ด ํจ์ฌ ๋ฎ๋ค๋ ๊ฒ์ ์๊ณ ๋ ํฐ ํ์ ์ ๊ฐ์ง๊ณ ์ ๋ฐ์ดํธ๋ฅผ ๋ฐฐํฌํ ์ ์์ต๋๋ค. ์ด๋ ์ ์ธ๊ณ ์ด์ ํ์ ์คํธ๋ ์ค๋ฅผ ์ค์ด๊ณ ํจ์จ์ฑ์ ํฅ์์ํต๋๋ค.
๊ฒฐ๋ก
TypeScript๋ก ํ์ ์์ Redux๋ฅผ ๊ตฌํํ๋ ๊ฒ์ ๋จ์ํ ๋ชจ๋ฒ ์ฌ๋ก๋ฅผ ๋์ด, ๋์ฑ ์์ ์ ์ด๊ณ ์ ์ง ๋ณด์ ๊ฐ๋ฅํ๋ฉฐ ํ์ฅ ๊ฐ๋ฅํ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ๊ธฐ ์ํ ๊ทผ๋ณธ์ ์ธ ๋ณํ์ ๋๋ค. ๋ค์ํ ๊ธฐ์ ํ๊ฒฝ๊ณผ ๋ฌธํ์ ๋งฅ๋ฝ์์ ์ด์๋๋ ๊ธ๋ก๋ฒ ํ์๊ฒ ์ด๋ ์ปค๋ฎค๋์ผ์ด์ ์ ๊ฐ์ํํ๊ณ ๊ฐ๋ฐ์ ๊ฒฝํ์ ํฅ์์ํค๋ฉฐ ์ฝ๋๋ฒ ์ด์ค์ ํ์ง๊ณผ ์ ๋ขฐ์ ๋ํ ๊ณต์ ๋ ์ธ์์ ํจ์ํ๋ ๊ฐ๋ ฅํ ํตํฉ ์์ ์ญํ ์ ํฉ๋๋ค.
Redux ์ํ ๊ด๋ฆฌ์ ๊ฐ๋ ฅํ ํ์ ๊ตฌํ์ ํฌ์ํจ์ผ๋ก์จ, ๋ฒ๊ทธ๋ฅผ ์๋ฐฉํ๋ ๊ฒ๋ฟ๋ง ์๋๋ผ ๊ธฐ์กด ๊ธฐ๋ฅ์ ๋ง๊ฐ๋จ๋ฆด ๊ฒ์ด๋ผ๋ ๋์์๋ ๋๋ ค์ ์์ด ํ์ ์ด ๋ฒ์ฑํ ์ ์๋ ํ๊ฒฝ์ ์กฐ์ฑํ๋ ๊ฒ์ ๋๋ค. Redux ์ฌ์ ์ TypeScript๋ฅผ ์์ฉํ๊ณ , ํ์ ์ถ์ข ์ ๋ถํํ๋ ๋ช ํ์ฑ๊ณผ ์ ๋ขฐ์ฑ์ผ๋ก ๊ธ๋ก๋ฒ ๊ฐ๋ฐ ๋ ธ๋ ฅ์ ๊ฐํํ์ญ์์ค. ์ํ ๊ด๋ฆฌ์ ๋ฏธ๋๋ ํ์ ์์ ํ๋ฉฐ, ์ด๋ ์ฌ๋ฌ๋ถ์ ์์ด ๋ฟ๋ ๊ณณ์ ์์ต๋๋ค.