TIL 홈페이지 만들기 9

냐모·2021년 3월 24일
0

홈페이지 만들기

목록 보기
9/10

TIL 홈페이지 만들기 9

npm i @reduxjs/toolkit react-redux redux next-redux-wrapper redux-saga axios
npm i -D @types/react-redux
를 설치한다.
front 디렉토리 안에 reducers 디렉토리를 만들고 index.ts, userReducer.ts 폴더를 생성한다.

reducers/userReducer.ts

import { createSlice, PayloadAction, createAsyncThunk } from "@reduxjs/toolkit";


export interface User {
	loadMyInfoLoading: boolean;
	loadMyInfoDone: boolean;
	loadMyInfoError: any;
	me: any;

	userLoginLoading: boolean;
	userLoginDone: boolean;
	userLoginError: any;

	userLogOutLoading: boolean;
	userLogOutDone: boolean;
	userLogOutError: any;
	
	userAddLoading: boolean;
	userAddDone: boolean;
	userAddError: any;
}


export const initialState = {
	loadMyInfoLoading: false,
	loadMyInfoDone: false,
	loadMyInfoError: null,
	me: null,

	userAddLoading: false,
	userAddDone: false,
	userAddError: null,

	userLoginLoading: false,
	userLoginDone: false,
	userLoginError: null,

	userLogOutLoading: false,
	userLogOutDone: false,
	userLogOutError: null,
} as User;


export const userReducer = createSlice({
	name: 'users',
	initialState,
	reducers: {
		LOAD_MY_INFO_REQUEST(state) {
			state.loadMyInfoLoading = true;
			state.loadMyInfoDone = false;
			state.loadMyInfoError = null;
		},
		LOAD_MY_INFO_SUCCESS(state, action) {
			state.loadMyInfoLoading = false;
			state.loadMyInfoDone = true;
			state.me = action.payload;
		},
		LOAD_MY_INFO_FAILURE(state, action) {
			state.loadMyInfoLoading = false;
			state.loadMyInfoError = action.payload;
		},
		USER_ADD_REQUEST(state, action) {
			state.loadMyInfoLoading = true;
			state.loadMyInfoDone = false;
			state.loadMyInfoError = null;
		},
		USER_ADD_SUCCESS(state, action) {
			state.loadMyInfoLoading = false;
			state.loadMyInfoDone = true;
			state.me = action.payload;
		},
		USER_ADD_FAILURE(state, action) {
			state.loadMyInfoLoading = false;
			state.loadMyInfoError = action.payload;
		},
		USER_LOGIN_REQUEST(state, action) {
			state.loadMyInfoLoading = true;
			state.loadMyInfoDone = false;
			state.loadMyInfoError = null;
		},
		USER_LOGIN_SUCCESS(state, action) {
			state.loadMyInfoLoading = false;
			state.loadMyInfoDone = true;
			state.me = action.payload;
		},
		USER_LOGIN_FAILURE(state, action) {
			state.loadMyInfoLoading = false;
			state.loadMyInfoError = action.payload;
		},
		USER_LOG_OUT_REQUEST(state, action) {
			state.loadMyInfoLoading = true;
			state.loadMyInfoDone = false;
			state.loadMyInfoError = null;
		},
		USER_LOG_OUT_SUCCESS(state, action) {
			state.loadMyInfoLoading = false;
			state.loadMyInfoDone = true;
			state.me = action.payload;
		},
		USER_LOG_OUT_FAILURE(state, action) {
			state.loadMyInfoLoading = false;
			state.loadMyInfoError = action.payload;
		},
	}
});


export const {
	LOAD_MY_INFO_REQUEST, LOAD_MY_INFO_SUCCESS, LOAD_MY_INFO_FAILURE,
	USER_ADD_REQUEST, USER_ADD_SUCCESS, USER_ADD_FAILURE,
	USER_LOGIN_REQUEST, USER_LOGIN_SUCCESS, USER_LOGIN_FAILURE,
	USER_LOG_OUT_REQUEST, USER_LOG_OUT_SUCCESS, USER_LOG_OUT_FAILURE,
} = userReducer.actions;
export default userReducer.reducer;

reducers/index.ts

import { HYDRATE } from 'next-redux-wrapper';
import { combineReducers } from "@reduxjs/toolkit";

import userReducer from './userReducer';

// (이전상태, 액션) => 다음상태
const reducer = (state, action) => {
	switch (action.type) {
		case HYDRATE:
			return action.payload;
		default: {
			const combinedReducer = combineReducers({
				userReducer
			});
			return combinedReducer(state, action);
		}
	}
};

export type ReducerType = ReturnType<typeof reducer>;

export default reducer;

front 디렉토리에 sagas 디렉토리를 만들어 주고 그 안에 index.ts, userSaga.ts 파일을 만들어 준다.

sagas/userSaga.ts

import { all, fork, call, put, take, takeEvery, takeLatest, delay } from 'redux-saga/effects';
import {PayloadAction} from "@reduxjs/toolkit";
import axios from 'axios';

import {
	LOAD_MY_INFO_REQUEST, LOAD_MY_INFO_SUCCESS, LOAD_MY_INFO_FAILURE,
	USER_ADD_REQUEST, USER_ADD_SUCCESS, USER_ADD_FAILURE,
	USER_LOG_IN_REQUEST, USER_LOG_IN_SUCCESS, USER_LOG_IN_FAILURE,
	USER_LOG_OUT_REQUEST, USER_LOG_OUT_SUCCESS, USER_LOG_OUT_FAILURE,
} from '../reducers/userReducer';


function loadMyInfoAPI() {
	return axios.get('/user' )
}

function* loadMyInfo(action: PayloadAction) {
	try {
		console.log(action);
		// yield delay(1000);
		const res = yield call(loadMyInfoAPI)
		yield put(LOAD_MY_INFO_SUCCESS(res.data));
	} catch (err) {
		console.error(err);
		yield put(LOAD_MY_INFO_FAILURE(err));
	}
}

function userAddAPI(data: {email: string,  password: string}) {
	return axios.post('/user', data);
}

function* userAdd(action: PayloadAction<{email: string, password: string}>) {
	try {
		const data = {
			email: action.payload.email,
			password: action.payload.password
		}
		const res = yield call(userAddAPI, data)
		yield put(USER_ADD_SUCCESS(res.data));
	} catch (err) {
		console.error(err);
		yield put(USER_ADD_FAILURE(err));
	}
}

function userLogInAPI(data: {email: string, password: string}) {
	return axios.post('/user/login', data);
}

function* userLogIn(action: PayloadAction<{email: string, password: string}>) {
	try {
		const data = {
			email: action.payload.email,
			password: action.payload.password
		}
		const res = yield call(userLogInAPI, data)
		yield put(USER_LOG_IN_SUCCESS(res.data));
	} catch (err) {
		console.error(err);
		yield put(USER_LOG_IN_FAILURE(err));
	}
}

function userLogOutAPI() {
	return axios.post('/user/logout');
}

function* userLogOut() {
	try {
		yield call(userLogOutAPI )
		// yield  delay(1000)
		yield put({
			type: USER_LOG_OUT_SUCCESS,
		})
	} catch (e) {
		console.log(e);
		yield put({
			type: USER_LOG_OUT_FAILURE,
			error: e.response.data
		})
	}
}


function* watchLoadMyInfo() {
	yield takeLatest(LOAD_MY_INFO_REQUEST, loadMyInfo);
}

function* watchUserAdd() {
	yield takeLatest(USER_ADD_REQUEST, userAdd)
}

function* watchUserLogin() {
	yield takeLatest(USER_LOG_IN_REQUEST, userLogIn)
}

function* watchUserLogOut() {
	yield takeLatest(USER_LOG_OUT_REQUEST, userLogOut)
}



export default function* userSaga() {
	yield all([
		fork(watchLoadMyInfo),
		fork(watchUserAdd),
		fork(watchUserLogin),
		fork(watchUserLogOut),
	])
}

sagas/index.ts

import { all, fork } from 'redux-saga/effects';

import userSaga from "./userSaga";

export default function* rootSaga() {
	yield all([
		fork(userSaga),
	])
}

그 다음에 front 디렉토리에 store.ts 폴더를 생성하고 아래와 같이 입력한다.

import { configureStore } from '@reduxjs/toolkit';
import { createWrapper } from 'next-redux-wrapper';
import rootReducer from './reducers';
import createSagaMiddleware, { Task } from 'redux-saga';
import { Store } from 'redux';
import rootSaga from './sagas';

// Next Redux Toolkit Saga를 사용할때는
// confugureStore에서 강제로 sagaTask를 만들어주기 위함
interface SagaStore extends Store {
	sagaTask?: Task;
}

const store = () => {
	const devMode = process.env.NODE_ENV === 'development'; // 개발모드
	const sagaMiddleware = createSagaMiddleware();
	const store = configureStore({
		reducer: rootReducer,
		middleware: [sagaMiddleware],
		devTools: devMode,
	});

	// Next Redux Toolkit 에서 saga를 사용해야할 때
	(store as SagaStore).sagaTask = sagaMiddleware.run(rootSaga);

	return store;
};

const wrapper = createWrapper(store, {
	// 이 부분이 true면 디버그때 자세한 설명이 나옵니다. (개발할때는 true로)
	debug: process.env.NODE_ENV === 'development',
});

export default wrapper;

그 다음 _app.tsx 파일을 아래와 같이 수정한다.

import { AppProps } from 'next/app';
import { GlobalStyles } from '../components/GlobalStyles';
import { Global } from '@emotion/react';
import wrapper from '../store';

function App({ Component, pageProps }: AppProps) {
	return (
		<>
			<Global styles={GlobalStyles} />
			<Component {...pageProps}>
			</Component>
		</>
	)
}

// redux, saga 설정
export default wrapper.withRedux(App);

이제 서버를 재실행 하면 정상적으로 작동되는 것을 확인 할 수 있다.

profile
안녕하세요

0개의 댓글

관련 채용 정보