728x90
반응형

NextJS에서 상태를 관리하기 수월하게 해주는 redux를 TypeScript와 함께 사용해보자.

이 포스트는 counter에 대한 state를 다루는 포스트이다.

 

사전 준비

create-next-app으로 프로젝트를 생성한다.

 

$ npx create-next-app nextredux --typescript

 

웹으로 설치가 잘 되었는지 확인한다.

$ npm run dev

 

 

필요한 npm 패키지를 설치한다.

$ npm i react-redux redux next-redux-wrapper
$ npm i redux-devtools-extension redux-logger --dev-save

 

store 폴더와 하위에 index.ts파일을 생성해준다.

 

 

/store/index.ts

import { createStore, applyMiddleware, compose } from "redux";
import { createWrapper } from "next-redux-wrapper";
import { createLogger } from "redux-logger";
import { composeWithDevTools } from "redux-devtools-extension";
import rootReducer from "reducer/index";

const configureStore = () => {
	const logger = createLogger();
	const enhancer = compose(composeWithDevTools(applyMiddleware(logger)));
	const store = createStore(rootReducer, enhancer);
	return store;
};

const wrapper = createWrapper(configureStore, { debug: true });

export default wrapper;

아직 reducer 폴더를 만들지 않았기 때문에 오류가 뜬다. 천천히 해보자.

 

 

만든 store를 _app.tsx 파일에 wrapper 적용을 하자.

 

_app.tsx

import { AppProps } from "next/app";
import { NextPage } from "next";
import wrapper from "../store"; //좀전에 만든 store.ts 파일

const MyApp: NextPage<AppProps> = ({ Component, pageProps }: AppProps) => {
	return (
		<>
			<Component {...pageProps} />
		</>
	);
};

export default wrapper.withRedux(MyApp);

 

 

interfaces폴더를 아래와 같이 생성한다.

 

index.ts파일을 수정한다.

/store/interfaces/index.ts

export * from "./counter/counter.interfaces";
export * from "./counter/counterAct.interfaces";

 

 

RootState.ts파일을 수정한다.

/store/interfaces/RootState.ts

import { CounterState } from "./index";

export interface RootStateInterface {
	counter: CounterState;
}

 

 

counter.interfaces.ts 파일을 수정한다.

/store/interfaces/counter/counter.interfaces.ts

export interface CounterState {
	count: number;
}

 

 

counterAct.interfaces.ts 파일을 수정한다.

/store/interfaces/counter/counterAct.interfaces.ts

export enum actionTypesCounter {
	COUNTER_INCREMENT = "COUNTER_INCREMENT", // 숫자 + 1
	COUNTER_DECREMENT = "COUNTER_DECREMENT",  // 숫자 - 1
	COUNTER_RESET = "COUNTER_RESET", // 초기화 
}

export type ActionsCounter = CounterIncrement | CounterDecrement | CounterReset;

export interface CounterIncrement {
	type: actionTypesCounter.COUNTER_INCREMENT;
}

export interface CounterDecrement {
	type: actionTypesCounter.COUNTER_DECREMENT;
}

export interface CounterReset {
	type: actionTypesCounter.COUNTER_RESET;
}

 

interfaces는 완성되었다. 이번엔 reducer를 만들어보자.

 

 

index.ts 파일을 수정한다.

/store/reducer/index.ts

import { combineReducers, Reducer, AnyAction } from "redux";
import { RootStateInterface } from "../interfaces/RootState";
import counter from "./counter";

const rootReducer: Reducer<
	RootStateInterface,
	AnyAction
> = combineReducers<RootStateInterface>({
	counter,
});

export default rootReducer;
export type RootState = ReturnType<typeof rootReducer>;

 

 

counter.ts 파일을 수정한다

/store/reducer/counter.ts

import { HYDRATE } from "next-redux-wrapper";
import {
	CounterState,
	actionTypesCounter,
	ActionsCounter,
} from "../interfaces";

export const initialState: CounterState = {
	count: 0,
};

interface HydratePayload {
	counter: CounterState;
}

const counter = (
	state = initialState,
	action: ActionsCounter | { type: typeof HYDRATE; payload: HydratePayload },
): CounterState => {
	switch (action.type) {
		case HYDRATE:
			return { ...state, ...action.payload.counter };
		case actionTypesCounter.COUNTER_INCREMENT:
			return {
				...state,
				...{ count: state.count + 1 },
			};

		case actionTypesCounter.COUNTER_DECREMENT:
			return {
				...state,
				...{ count: state.count - 1 },
			};

		case actionTypesCounter.COUNTER_RESET:
			return {
				...state,
				...{ count: initialState.count },
			};

		default:
			return state;
	}
};
export default counter;

 

 

그다음 pages/counter/index.tsx 파일을 만든다.

 

pages/counter/index.tsx

import React from "react";
import { useDispatch, useSelector } from "react-redux";
import { RootStateInterface } from "../../store/interfaces/RootState";
import { CounterState } from "../../store/interfaces";
import { actionTypesCounter } from "../../store/interfaces/counter/counterAct.interfaces";

const Counter = () => {
	const { count } = useSelector(
		(state: RootStateInterface): CounterState => state.counter,
	);
	const dispatch = useDispatch();
	const handleClick = (num: number) => {
		if (num === 1) {
			dispatch({type : actionTypesCounter.COUNTER_INCREMENT});
		} else if (num === 0) {
			dispatch({type : actionTypesCounter.COUNTER_DECREMENT});
		} else if (num === 2) {
			dispatch({type : actionTypesCounter.COUNTER_RESET});
		}
	};
	return (
		<div>
			<div>현재값:{count}</div>
			<div>
				<button onClick={() => handleClick(1)}>+</button>
				<button onClick={() => handleClick(0)}>-</button>
				<button onClick={() => handleClick(2)}>reset</button>
			</div>
		</div>
	);
};

export default Counter;

 

실행해본다.

 

 

정상적으로 카운트state가 버튼 클릭에따라 바뀌는 것을 확인할 수 있다.

 

액션을 만들어서 State를 다루는 방법은 Nest - Next 카테고리에서 확인할 수 있다.

728x90
반응형

+ Recent posts