일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- abstract
- 추상메서드
- EnhancedFor
- 메소드오버로딩
- NestedFor
- 환경설정
- 오라클
- 자동차수리시스템
- oracle
- 자바
- Java
- 예외처리
- 참조형변수
- 컬렉션 타입
- 집합_SET
- 객체 비교
- 생성자오버로드
- cursor문
- 한국건설관리시스템
- 컬렉션프레임워크
- 사용자예외클래스생성
- 정수형타입
- 대덕인재개발원
- exception
- GRANT VIEW
- 예외미루기
- 인터페이스
- 다형성
- 어윈 사용법
- 제네릭
- Today
- Total
거니의 velog
(33) 리덕스 툴킷 2 본문
2. useSelector() / useDispatch()
* 애플리케이션 상태를 변경하기 위해서는 리듀서 함수인 login(), logout()을 호출하거나 변경된 상태를 전달(notify-통지) 받아야만 하는데 이를 위해서 useSelector(), useDispatch() 를 활용한다.
* useSelector() 는 컴포넌트 내에서 애플리케이션 상태를 받아서 자신이 원하는 상태 데이터를 선별(select)하는 용도로 사용한다. 예를 들어 로그인 상태가 변경되는 것을 알아야 하는 컴포넌트는 useSelector() 를 이용하게 된다.
* useDispatch() 는 리듀서를 통해서 만들어진 새로운 애플리케이션 상태를 반영하기 위해서 사용한다. 예를 들어 로그인 페이지에서 로그인이 처리되면 useDispatch()를 이용해서 새로운 애플리케이션 상태를 배포(dispatch)라는 경우에 사용하게 된다.
(1) 로그인 페이지와 로그인
* 현재 사용자의 로그인 상태에 따라 로그인 화면을 보거나 로그인 정보를 사용하는 예제를 작성해 보자. 먼저, 로그인 상황에 대한 처리부터 반영해 보자.
* 로그인 메뉴는 상단 메뉴에서 보이고 있고, BasicMenu.js 컴포넌트에서 처리되고 있다.
* 메뉴는 사용자의 로그인 상황에 따라 다르게 출력될 가능성이 있으므로 로그인 상황을 useSelector() 에서 감지하도록 설정한다.
import React from "react";
import { Link } from "react-router-dom";
import { useSelector } from "react-redux";
const BasicMenu = () => {
const loginState = useSelector((state) => state.loginSlice);
return (
(...)
);
};
export default BasicMenu;
* useSelector()는 파라미터로 함수를 지정하는데, 이 함수를 사용해서 전달되는 애플리케이션 상태 중에 필요한 상태를 골라서 사용한다. 로그인 상태는 loginSlice라는 이름으로 store.js에 등록되어 있다.
* 화면의 메뉴 중에서 JWT를 이용해야 하는 Products와 Todo 메뉴는 email 값이 존재하는 로그인한 사용자에게만 노출하도록 제어한다.
import React from "react";
import { Link } from "react-router-dom";
import { useSelector } from "react-redux";
const BasicMenu = () => {
const loginState = useSelector((state) => state.loginSlice);
return (
<nav id="navbar" className=" flex bg-blue-300">
<div className="w-4/5 bg-gray-500">
<ul className="flex p-4 text-white font-bold">
<li className="pr-6 text-2xl">
<Link to={"/"}>Main</Link>
</li>
<li className="pr-6 text-2xl">
<Link to={"/about"}>About</Link>
</li>
{loginState.email ? ( //로그인한 사용자만 출력되는 메뉴
<>
<li className="pr-6 text-2xl">
<Link to={"/todo/"}>Todo</Link>
</li>
<li className="pr-6 text-2xl">
<Link to={"/products/"}>Products</Link>
</li>
</>
) : (
<></>
)}
</ul>
</div>
<div className="w-1/5 flex justify-end bg-orange-300 p-4 font-medium">
<div className="text-white text-sm m-1 rounded">Login</div>
</div>
</nav>
);
};
export default BasicMenu;
* 브라우저에서 API 통신이 필요한 메뉴는 감춰진 것을 확인할 수 있다.
[로그인 페이지]
* 화면 상단의 오른쪽에는 Login 메뉴가 있으므로 이를 통해서 보여지는 LoginPage를 작성해 본다. pages 폴더 내에 member 폴더를 생성하고 LoginPage.js 파일을 추가한다.
import React from "react";
import BasicMenu from "../../components/menus/BasicMenu";
const LoginPage = () => {
return (
<div className="fixed top-0 left-0 z-[1055] flex flex-col h-full w-full">
<BasicMenu />
<div className="w-full flex flex-wrap h-full justify-center items-center border-2">
<div className="text-2xl">Login Page</div>
</div>
</div>
);
};
export default LoginPage;
* LoginPage에 대한 라우팅 설정을 위해 router 폴더에 memberRouter.js 파일을 추가한다.
import React, { Suspense, lazy } from "react";
const Loading = <div>Loading....</div>;
const Login = lazy(() => import("../pages/member/LoginPage"));
const memberRouter = () => {
return [
{
path: "login",
element: (
<Suspense fallback={Loading}>
<Login />
</Suspense>
),
},
];
};
export default memberRouter;
* 추가된 memberRouter에 대한 설정을 root.js를 이용해서 /member/ 경로로 시작할 때 memberRouter를 이용하도록 설정한다.
import React, { Suspense, lazy } from "react";
import { createBrowserRouter } from "react-router-dom";
import todoRouter from "./todoRouter";
import productsRouter from "./productsRouter";
import memberRouter from "./memberRouter";
(...)
const root = createBrowserRouter([
(...)
{
path: "member",
children: memberRouter(),
},
]);
export default root;
* 브라우저에서 /member/login 경로를 호출하면 LoginPage의 결과를 확인할 수 있다.
* 상단의 BasicMenu 컴포넌트의 마지막 부분에는 로그인이 안된 상황(email 값이 없는 상황)에서는 Login 메뉴로 이동할 수 있도록 <Link>를 처리한다.
import React from "react";
import { Link } from "react-router-dom";
import { useSelector } from "react-redux";
const BasicMenu = () => {
const loginState = useSelector((state) => state.loginSlice);
return (
<nav id="navbar" className=" flex bg-blue-300">
(...)
<div className="w-1/5 flex justify-end bg-orange-300 p-4 font-medium">
{!loginState.email ? (
<div className="text-white text-sm m-1 rounded">
<Link to={"/member/login"}>Login</Link>
</div>
) : (
<></>
)}
</div>
</nav>
);
};
export default BasicMenu;
* 브라우저에서 오른쪽 상단의 Login 메뉴를 클릭하면 /member/login 경로로 이동하고 LoginPage가 출력된다.
[로그인 컴포넌트]
* 로그인 페이지 안에 들어갈 로그인 컴포넌트는 components/member/LoginComponent.js 로 작성한다.
* LoginComponent는 email(아이디)과 pw(패스워드)를 입력받아서 로그인을 처리하는 함수이다. 서버와의 통신은 조금 미루는 대신에 리덕스 툴킷으로 애플리케이션 상태를 처리해 본다.
import React, { useState } from "react";
const initState = {
email: "",
pw: "",
};
const LoginComponent = () => {
const [loginParam, setLoginParam] = useState({ ...initState });
const handleChange = (e) => {
loginParam[e.target.name] = e.target.value;
setLoginParam({ ...loginParam });
};
return (
<div className="border-2 border-sky-200 mt-10 m-2 p-4">
<div className="flex justify-center">
<div className="text-4xl m-4 p-4 font-extrabold text-blue-500">
Login Component
</div>
</div>
<div className="flex justify-center">
<div className="relative mb-4 flex w-full flex-wrap items-stretch">
<div className="w-full p-3 text-left font-bold">Email</div>
<input
className="w-full p-3 rounded-r border border-solid border-neutral-500 shadow-md"
name="email"
type={"text"}
value={loginParam.email}
onChange={handleChange}
></input>
</div>
</div>
<div className="flex justify-center">
<div className="relative mb-4 flex w-full flex-wrap items-stretch">
<div className="w-full p-3 text-left font-bold">Password</div>
<input
className="w-full p-3 rounded-r border border-solid border-neutral-500 shadow-md"
name="pw"
type={"password"}
value={loginParam.pw}
onChange={handleChange}
></input>
</div>
</div>
<div className="flex justify-center">
<div className="relative mb-4 flex w-full justify-center">
<div className="w-2/5 p-6 flex justify-center font-bold">
<button className="rounded p-4 w-36 bg-blue-500 text-xl text-white">
LOGIN
</button>
</div>
</div>
</div>
</div>
);
};
export default LoginComponent;
* LoginPage 내부에 LoginComponent 를 추가하고 화면을 확인한다.
import React from "react";
import BasicMenu from "../../components/menus/BasicMenu";
import LoginComponent from "../../components/member/LoginComponent";
const LoginPage = () => {
return (
<div className="fixed top-0 left-0 z-[1055] flex flex-col h-full w-full">
<BasicMenu />
<div className="w-full flex flex-wrap h-full justify-center items-center border-2">
<LoginComponent />
</div>
</div>
);
};
export default LoginPage;
* 상단 메뉴에서 Login을 선택하면 LoginPage와 LoginComponent를 확인할 수 있다.
[로그인 상태 변경]
* LoginComponent에서는 LOGIN 버튼을 클릭했을 때 리듀서를 호출하고 이를 useDispatch()를 사용해서 애플리케이션의 상태를 변경해 본다.
* LoginComponent에 이벤트 처리를 위한 함수를 추가하고, 버튼과 연결한다.
import React, { useState } from "react";
import { useDispatch } from "react-redux";
import { login } from "../../slices/loginSlice";
const initState = {
email: "",
pw: "",
};
const LoginComponent = () => {
const [loginParam, setLoginParam] = useState({ ...initState });
const dispatch = useDispatch();
const handleChange = (e) => {
loginParam[e.target.name] = e.target.value;
setLoginParam({ ...loginParam });
};
const handleClickLogin = (e) => {
dispatch(login(loginParam));
};
return (
<div className="border-2 border-sky-200 mt-10 m-2 p-4">
(...)
<div className="flex justify-center">
<div className="relative mb-4 flex w-full justify-center">
<div className="w-2/5 p-6 flex justify-center font-bold">
<button
className="rounded p-4 w-36 bg-blue-500 text-xl text-white"
onClick={handleClickLogin}
>
LOGIN
</button>
</div>
</div>
</div>
</div>
);
};
export default LoginComponent;
* 화면 상에서 버튼을 클릭하면 loginSlice의 login()이 동작하는 것을 확인할 수 있다.
* loginSlice로 리듀서가 전달받은 데이터를 확인한다. 리듀서 함수의 두 번째 파라미터는 action으로 payload라는 속성을 이용해서 컴포넌트가 전달하는 데이터를 확인할 수 있다. 이를 이용해서 화면에서 전달된 email을 새로운 상태 값으로 처리한다.
import { createSlice } from "@reduxjs/toolkit";
const initState = {
email: "",
};
const loginSlice = createSlice({
name: "LoginSlice",
initialState: initState,
reducers: {
login: (state, action) => {
console.log("login.....");
// {email, pw} 로 구성
const data = action.payload;
// 새로운 상태
return { email: data.email };
},
logout: (state, action) => {
console.log("logout....");
},
},
});
export const { login, logout } = loginSlice.actions;
export default loginSlice.reducer;
* 위 코드가 반영되면 화면에서 입력한 email 이 애플리케이션의 상태가 되고, useSelector()를 사용하는 메뉴에서는 email 값이 있기 때문에 로그인 전과 후의 메뉴 구성이 달라지면서 'Todo, Product' 메뉴가 노출되고 오른쪽 상단의 'Login' 링크는 사라지는 것을 확인할 수 있다.
'SpringBoot_React 풀스택 프로젝트' 카테고리의 다른 글
(35) 리덕스 툴킷 4 (0) | 2024.03.06 |
---|---|
(34) 리덕스 툴킷 3 (0) | 2024.03.06 |
(32) 리덕스 툴킷 1 (0) | 2024.03.06 |
(31) 스프링 시큐리티와 API 서버 5 (0) | 2024.03.06 |
(30) 스프링 시큐리티와 API 서버 4 (0) | 2024.03.06 |