install
# 공유라이브러리 리덕스-툴킷 설치
npm i react-redux @reduxjs/toolkit
필자는 타입스크립트 사용
type
// IAuth.ts : 인터페이스
import IUser from "./IUser";
export default interface IAuth {
isLoggedIn : boolean, // 로그인 상태(true , false)
user? : IUser | null, // 유저 객체
}
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// IUser.ts : 인터페이스
export default interface IUser {
// EMAIL VARCHAR2(1000) -- id (email)
// PASSWORD VARCHAR2(1000), -- 암호
// USERNAME VARCHAR2(1000), -- 유저명
// CODE_NAME VARCHAR2(1000), -- 권한코드명(ROLE_USER, ROLE_ADMIN)
// DELETE_YN VARCHAR2(1) DEFAULT 'N',
// INSERT_TIME VARCHAR2(255),
// UPDATE_TIME VARCHAR2(255),
// DELETE_TIME VARCHAR2(255)
email? : string | null,
password : string,
username : string,
codeName : string, // 권한
}
index.tsx
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { BrowserRouter } from "react-router-dom";
import { Provider } from "react-redux";
import { store } from "./store/store";
const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement
);
root.render(
<Provider store={store}> // 추가
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
auth
( 리듀서 정의 )
// Todo : 공유저장소를 정의하는 파일 : 공유함수 , 공유변수
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import IUser from "../../types/auth/IUser";
import AuthService from "../../services/auth/AuthService";
import IAuth from "../../types/auth/IAuth";
// Todo : localStorage 에서 user 값 가져오기 : 문자열 -> 객체로 변환 : JSON.parse
// user = webToken이 있음
const user = JSON.parse(localStorage.getItem("user") || "null");
// Todo : 의미
// Async의 의미 : 비동기
// createAsyncThunk(함수명 , (변수 , thunkAPI) => {실행문}) : 리덕스 비동기 함수 적용
// thunkAPI : 에러메시지 처리 : 고정적으로 붙여서 사용
// -> 사용법 : thunkAPI.rejectWithValue(에러메시지)
// Todo : async ~ await 비동기 코딩 방식(최근)
// 비동기함수 처리 :
// promise -> 함수실행.then().catch() [옛날 방식]
// async () => { await 함수명 }; [현대방식] : 요즘 뜨는 비동기 함수를 처리하는 실행문
/* ------ 공유 함수 정의 --------*/
// Todo : 회원가입 공유함수 (비동기 함수)
export const register = createAsyncThunk(
"auth/register", // 공유 함수를 실행시킬 함수명
async (user: IUser, thunkAPI) => {
// 화살표 함수 == 비동기 함수
try {
const response = await AuthService.register(user); // 회원가입 service 실행
return response.data;
} catch (error: any) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
return thunkAPI.rejectWithValue("회원가입 에러 : " + message);
}
}
);
// Todo : 로그인 공유함수 (비동기 함수)
export const login = createAsyncThunk(
// 함수명
"auth/login",
// 비동기 함수
async (user: IUser, thunkAPI) => {
try {
const data = await AuthService.login(user); // 로그인 service 실행
return { user: data };
} catch (error: any) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
return thunkAPI.rejectWithValue("로그인 에러 : " + message);
}
}
);
// Todo : 로그아웃 공유함수 (비동기 함수)
export const logout = createAsyncThunk(
"auth/logout", // 공유 함수를 실행시킬 함수명
async () => {
await AuthService.logout(); // 로그아웃 service 실행
}
);
/* ------ 공유 변수 정의 --------*/
// Todo : State(공유 변수) 초기값 정의
const initialState: IAuth = user
? { isLoggedIn: true, user } // user 가 있으면 initialState = { isLoggedIn: true, user }
: { isLoggedIn: false, user: null }; // user 가 없으면 initialState = { isLoggedIn: false, user: null }
// Todo : 실제 공유 저장소 (리듀서 정의) : 공유 변수의 값을 정의
// name : 리듀서의 이름
// state : state(공유 변수)
// reducers : 동기로 실행시 : 동기 함수 정의할때 사용하는 속성
// extraReducers : 비동기로 실행시 : 비동기함수 정의시 사용하는 속성
// Todo : 사용법 : extraReducers : (builder) => {builder.addCase (비동기 함수명.fulfilled) , 실행함수} : 비동기함수 성공시 실행 함수 실행
// Todo : 사용법 : extraReducers : (builder) => {builder.addCase (비동기 함수명.rejected) , 실행함수} : 비동기함수 실패시 실행 함수 실행
// Todo : state , action 은 공유저장소(리덕스)에서 관리하는 값 , 한마디로 알아서 된다
const authSlice = createSlice({
name: "auth",
initialState,
// 동기로 실행 시
reducers: {},
// 비동기로 함수 실행 시 : register, login, logout 은 모두 비동기 함수임
extraReducers: (builder) => {
builder
// register 비동기함수 성공시
.addCase(register.fulfilled, (state) => {
state.isLoggedIn = false;
})
// register 비동기함수 실패시
.addCase(register.rejected, (state) => {
state.isLoggedIn = false;
})
// login 비동기함수 성공시
.addCase(login.fulfilled, (state, action) => {
state.isLoggedIn = true;
state.user = action.payload.user; // action.payload : user 객체 저장
})
// login 비동기함수 실패시
.addCase(login.rejected, (state, action) => {
state.isLoggedIn = false;
state.user = null;
})
// logout 비동기함수 성공시
.addCase(logout.fulfilled, (state) => {
state.isLoggedIn = false;
state.user = null;
});
},
});
export default authSlice.reducer;
store
( 리덕스 저장소 생성 )
import { configureStore } from '@reduxjs/toolkit'
import authReducer from "./slices/auth";
import { useDispatch } from 'react-redux';
// Todo : 공유 저장소 만드는 곳 : store
// seting redux-toolkit
// 리덕스 - 툴킷 환경설정 파일
// 리덕스를 사용하기 쉽게 줄여논 lib : redux-tookit
// 리덕스 : 공유저장소 (공유변수(전역변수) , 공유함수)
export const store = configureStore({
// Todo : 공유 저장소 이름 : 리듀서(reducer)
reducer: {
auth: authReducer, // 공유 저장소 1개 (여러개 만들 수 있음)
},
devTools: true,
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
export const useAppDispatch = () => useDispatch<AppDispatch>();
공유 저장소 변수 가져오기
// Todo : 공유 저장소 변수(state.변수명) 가져오기
// Todo : 사용법 : useSelector((state) => {state.변수명})
// Todo : 로그인 정보 상태 변수를 가져오고 싶음 (isLoggedIn : true / false)
const {isLoggedIn} = useSelector((state : RootState) => state.auth );
공유 저장소 함수 가져오기
// Todo : 공유저장소 함수 가져오기
// Todo : 불러오기 : useAppDispatch()
// Todo : 함수 사용법 : dispatch(함수명)
const dispatch = useAppDispatch();
redux + 유효성 검사 예제
import React , {useState} from "react";
import "../../assets/css/login.css";
import { Formik , Form , Field , ErrorMessage } from "formik";
import * as Yup from "yup";
import { useNavigate } from "react-router-dom";
import { useSelector } from "react-redux";
import { RootState, useAppDispatch } from "../../store/store";
import { login } from "../../store/slices/auth";
import IUser from "../../types/auth/IUser";
// 1) 로그인 로직
// 2) 유효성 체크 lib 적용 : Yup , Formik
function Login() {
let navigate = useNavigate();
// Todo : 공유 저장소 변수(state.변수명) 가져오기
// Todo : 사용법 : useSelector((state) => {state.변수명})
// Todo : 로그인 정보 상태 변수를 가져오고 싶음 (isLoggedIn : true / false)
const {isLoggedIn} = useSelector((state : RootState) => state.auth );
// Todo : 공유저장소 함수 가져오기
// Todo : 불러오기 : useAppDispatch()
// Todo : 함수 사용법 : dispatch(함수명)
// Todo : 함수 사용법 : dispatch(login) , dispatch(logout)
const dispatch = useAppDispatch();
// Todo : 유효성 체크 lib
// Todo : Formik 객체 초기화 (initialValues) : html 태그에서
// Todo : 체크대상 (email , password) : Field 태그
const initialValues = {
email : "",
password : ""
}
// Todo : 함수 정의
// Todo : Formit 라이브러리 : validationSchema
// Todo : validationSchema : 유효성 체크 규칙을 정의
// Todo : validationSchema = Yup.object().shape({유효성 체크규칙})
const validationSchema = Yup.object().shape({
// email , password 유효성 규칙 :
// string() : 자료형이 문자열인가?
// required(에러메세지) => 필수필드
email: Yup.string().required("email 은 필수 입력입니다."),
password: Yup.string().required("password 는 필수 입력입니다."),
});
// Todo : isLoggedIn (로그인 상태변수 true / false)
// Todo : isLoggedIn == true 강제이동
if(isLoggedIn) {
navigate("/home")
}
// Todo : 로그인 함수 : submit(Formit)
// Todo : Formit lib 에서 자동으로 email , password 값을 보내줌
const handleLogin = (formValue : any) => {
const { email , password } = formValue;
const data : IUser = {
email, // == email : email (생략 가능)
password, // == password : password
username: "", // 유저 이름
codeName : "ROLE_USER" // 권한
}
// Todo : 공유 로그인 함수 호출
dispatch(login(data))
// Todo : .unwrap() : redux의 공유함수 에러처리를 샐행하게 하는 함수
.unwrap()
.then(() => {
alert("로그인 성공했습니다.")
navigate("/home")
window.location.reload(); // 페이지 새로고침
})
.catch((e : Error) => {
console.log(e);
});
}
return (
<div>
<div className="row justify-content-center">
<div className="col-xl-10 col-lg-12 col-md-9">
<div className="card mt-5">
<div className="card-body p-0">
{/* <!-- Nested Row within Card Body --> */}
<div className="row">
<div className="col-lg-6 bg-login-image"></div>
<div className="col-lg-6">
<div className="p-5">
<div className="text-center">
<h1 className="h4 mb-4">Welcome Back!</h1>
</div>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={handleLogin}
>
{({ errors, touched }) => (
<Form className="user">
<div className="form-group">
<Field
type="email"
name="email"
className={
"form-control form-control-user mb-3" +
(errors.email && touched.email
? " is-invalid"
: "")
}
placeholder="Enter Email Address..."
/>
<ErrorMessage
name="email"
component="div"
className="invalid-feedback"
/>
</div>
<div className="form-group">
<Field
type="password"
name="password"
className={
"form-control form-control-user mb-3" +
(errors.password && touched.password
? " is-invalid"
: "")
}
id="exampleInputPassword"
placeholder="Password"
/>
<ErrorMessage
name="password"
component="div"
className="invalid-feedback"
/>
</div>
<button
type="submit"
className="btn btn-primary btn-user w-100 mb-3"
>
Login
</button>
<hr />
<a
href="/"
className="btn btn-google btn-user w-100 mb-2"
>
<i className="fab fa-google fa-fw"></i> Login
with Google
</a>
<a
href="/"
className="btn btn-naver btn-user w-100 mb-2"
>
<i className="fa-solid fa-n"></i> Login with
Naver
</a>
<a
href="/"
className="btn btn-kakao btn-user w-100 mb-3"
>
<i className="fa-solid fa-k"></i> Login with
Kakao
</a>
</Form>
)}
</Formik>
<hr />
<div className="text-center">
<a className="small" href="/forgot-password">
Forgot Password?
</a>
</div>
<div className="text-center">
<a className="small" href="/register">
Create an Account!
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
export default Login;
'React > React - 외부라이브러리' 카테고리의 다른 글
React - 폼 유효성 체크 라이브러리 formik , yup (1) | 2023.12.17 |
---|---|
외부 라이브러리 - 유효성 체크 lib (1) | 2023.11.14 |
외부라이브러리 - swiper (0) | 2023.11.06 |
외부라이브러리 - DaterangePicker (1) | 2023.10.31 |
외부라이브러리 - Jquery-ui Calendar (0) | 2023.10.31 |