일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 빌드 및 배포
- 파생상품평가
- 회원가입로직
- userManagement
- Update
- Styled Components
- 밸류즈 홈페이지
- RCPS
- mypage
- Ajax
- 밸류즈
- Typesciprt
- 로그인 로직
- 배포
- 캘린더 라이브러리
- jsonwebtoken
- 인증처리
- ui탬플릿
- stock option
- 이미지 업로드
- MRC
- 달력 라이브러리
- 스프링시큐리티
- 로그인
- 공통메서드
- react
- Token
- register
- 마이페이지
- 관리자페이지
- Today
- Total
I T H
[MySchedule project] 11. 관리자 - 사용자관리 로직 mui x-data-grid와 mui modal 사용 본문
[MySchedule project] 11. 관리자 - 사용자관리 로직 mui x-data-grid와 mui modal 사용
thdev 2024. 2. 7. 21:55관리자용 페이지중에서 사용자 데이터를 관리하는 페이지를 만들려고 한다.
먼저 db에 저장되있는 사용자 데이터들을 조회해 오고, 조회해온 데이터를 수정, 삭제하는 기능과 사용자를 등록하는 기능까지 구현할 것이다.
사용 라이브러리는 mui/x-data grid 를 사용해서 테이블을 구현했고, row를 클릭할 경우 modal 이 뜨게 할 것이다.
modal 라이브러리는 mui/material의 modal을 사용했다.
Frontend
- 구현에 들어가기 앞서, 필요한 라이브러리( mui )를 먼저 다운로드 받아준다.
https://mui.com/material-ui/getting-started/installation/
Installation - Material UI
Install Material UI, the world's most popular React UI framework.
mui.com
- 설치
npm install @mui/x-data-grid
npm install @mui/material @emotion/react @emotion/styled
- import
import {
DataGrid,
GridColDef,
GridRowSelectionModel,
GridRowsProp,
} from "@mui/x-data-grid";
[ src / types / userManagement.ts ]
- 타입 지정
export interface userManagementInput {
textUserId: string;
textUserName: string;
}
export interface userData {
userId: string;
userEmail: string;
userName: string;
userPhone: string;
userImage: string;
//userPassword: string;
role: number;
}
export interface modalInsertData extends userData {
userPassword: string;
userPasswordCheck: string;
}
[ pages / UserManagement / index.tsx ]
1. 조회
- db에 있는 사용자데이터들을 가져와서 mui grid 양식에 맞게 넣어서 출력하였다.
- 관리자로부터 입력받는 input은 이름과 아이디에 따라 db에서 다르게 조회 할 수 있도록 하였음.
2. 수정
- 관리자가 특정 row 클릭시에 해당되는 사용자 데이터를 가져올수 있게 하였고, row 클릭시에는 모달창에 뜨도록 구현함.
- 업데이트의 modal은 한 사용자의 데이터만 들어가므로, 따로 state로 상태를 관리해 ModalUpdatePage.tsx 컴포넌트로 해당 사용자의 데이터를 props로 보내주었음.
- ModalUpdatePage.tsx 에서 props로 받은 데이터는 화면에 출력되고, 관리자가 id(pk)를 제외한 나머지 요소들을 수정할수 있게 하였음.
3. 추가
- 추가버튼 클릭 이벤트시에 ModalInsertPage.tsx컴포넌트의 modal이 open 됨.
- 새로운 사용자 데이터를 등록하는 것이므로, validation check(유효성검사) 부분을 작성해야되고, id중복확인 부분도 작성해야됨.
- validation check가 정상적으로 통과되었다면 db에 사용자를 추가할수 있게 구현하였음.
- 리덕스의 stroe부분은 회원가입 이후의 로그인 로직부터 토큰 생성후 사용자 데이터를 가져오므로, dispatch를 통해 따로 하진 않고, 이번에는 바로 axios를 사용함.
4. 삭제
- mui grid에서 제공해주는 checkbox 클릭시에 사용자 데이터를 가져올수 있는 기능인 rowSelectionModal을 이용해서 사용자 데이터를 파라미터에 담아 백앤드에 보낼수 있음.
- rowSelectionModal 타입이 배열이므로, 여러개의 체크가 된 상태에서도 삭제가 가능하도록 구현함.
import React, { ChangeEvent, useEffect, useState } from "react";
import {
DataGrid,
GridColDef,
GridRowSelectionModel,
GridRowsProp,
} from "@mui/x-data-grid";
import axiosInstance from "../../utils/axios";
import {
modalData,
responseUserData,
userManagementType,
} from "../../types/userManagementType";
import { toast } from "react-toastify";
import ModalUpdatePage from "./ModalUpdatePage";
import ModalInsertPage from "./ModalInsertPage";
const UserManagement = () => {
const [textUserData, setTextUserdata] = useState<userManagementType>({
textUserId: "",
textUserName: "",
});
const [response, setResponse] = useState<responseUserData[]>([]); //백앤드에서 조회해온 데이터
const [modalData, setModalData] = useState<modalData>(); //모달에 넣을 데이터
const [modalInsertYn, setModalInsertYn] = useState<boolean>(false);
const [open, setOpen] = React.useState(false);
const [insertOpen, setInsertOpen] = React.useState(false);
const handleUpdateOpen = () => setOpen(true);
const handleInsertOpen = () => setInsertOpen(true);
//화면 랜더링시 데이터조회 함수 호출
useEffect(() => {
handleSelectUserData();
}, []);
//input onChange 함수
const handleChangeInput = (e: ChangeEvent<HTMLInputElement>) => {
e.preventDefault();
const { name, value } = e.target;
setTextUserdata((prevState) => ({
...prevState,
[name]: value,
}));
};
//데이터 조회
const handleSelectUserData = async () => {
const body = {
...textUserData,
};
try {
const response = await axiosInstance.post(`/userManagement`, body);
setResponse(response.data);
} catch (error: any) {
console.log(error);
}
};
// console.log(response);
// 데이터 맵핑 start
// data 배열에 데이터 갯수만큼 push 해야 함
let rows: GridRowsProp = [];
rows = response.map((data) => {
//console.log(data);
return {
id: data.userId,
userId: data.userId,
userName: data.userName,
userEmail: data.userEmail,
userPhone: data.userPhone,
userImage: data.userImage,
};
});
const columns: GridColDef[] = [
{ field: "userId", headerName: "아이디", width: 150 },
{ field: "userName", headerName: "이름", width: 150 },
{ field: "userEmail", headerName: "이메일", width: 150 },
{ field: "userPhone", headerName: "폰번호", width: 150 },
{ field: "userImage", headerName: "이미지파일이름", width: 600 },
];
// 데이터 맵핑 end
//데이터 삭제 start
const [rowSelectionModel, setRowSelectionModel] =
useState<GridRowSelectionModel>([]); // checkbox 선택 후 데이터삭제
//console.log(rowSelectionModel);
const deleteUserData = async (
e: React.MouseEvent<HTMLButtonElement, MouseEvent>
) => {
e.preventDefault();
if (rowSelectionModel === null || rowSelectionModel.length <= 0) {
toast.info("삭제할 데이터를 체크해주세요.");
return;
}
const body = {
...rowSelectionModel,
};
// eslint-disable-next-line no-restricted-globals
const dataDeleteYn = confirm("데이터를 삭제하시겠습니까?");
if (dataDeleteYn === true) {
try {
const response = await axiosInstance.post(
`/userManagement/userDataDelete`,
body
);
console.log(response);
if (response.data.deleteSuccess === true) {
toast.success("사용자 데이터가 삭제되었습니다.");
handleSelectUserData(); //데이터 재조회
}
} catch (error: any) {
console.log(error);
}
}
};
//데이터삭제 end
//데이터 추가 start
const insertUserData = () => {
setModalInsertYn(true);
handleInsertOpen();
};
//데이터 추가 end
//더블클릭 이밴트시 팝업창 openn / 한 행의 데이터 불러오는 함수
const getSelectedRows = (object: any) => {
// console.log(object);
// console.log(object.row);
// console.log(response);
response.map((item, idx) => {
if (item.userId === object.row.id) {
// 그리드에 선택한 1개의 row를 찾아낼 수 있다.
console.log(item);
// 조건에 맞는 한행의 데이터만 저장
setModalData(item);
// 모달 open
handleUpdateOpen();
}
});
};
return (
<section>
<div className="m-auto p-4 my-6 ">
<div className="w-full">
<div className="sm:text-right my-4">
<input
className="placeholder:text-sm text-md my-2 mx-2 sm:w-2/12 w-11/12 rounded-md border-[1px] border-gray-200 border-t-[3px] h-[45px] p-3 hover:border-t-[3px] hover:border-t-gray-400 focus:focus:outline-none"
type="text"
placeholder="아이디를 입력해주세요."
id="textUserId"
name="textUserId"
onChange={handleChangeInput}
value={textUserData.textUserId}
/>{" "}
<input
className="placeholder:text-sm my-2 mx-2 sm:w-2/12 w-11/12 rounded-md text-md border-[1px] border-gray-200 border-t-[3px] h-[45px] p-3 hover:border-t-[3px] hover:border-t-gray-400 focus:focus:outline-none"
type="text"
placeholder="성함을 입력해주세요."
id="textUserName"
name="textUserName"
onChange={handleChangeInput}
value={textUserData.textUserName}
/>{" "}
<button
onClick={handleSelectUserData}
className="sm:w-2/12 w-3/12 bg-blue-500 h-[45px] mx-2 my-2 text-white sm:font-bold font-semibold sm:text-sm text-[12px] py-2 px-2 rounded-md hover:bg-blue-600"
>
조회
</button>
<button
onClick={insertUserData}
className="sm:w-2/12 w-3/12 bg-blue-500 h-[45px] mx-2 text-white sm:font-bold font-semibold sm:text-sm text-[12px] py-2 px-2 rounded-md hover:bg-blue-600"
>
추가
</button>
<button
onClick={deleteUserData}
className="sm:w-2/12 w-3/12 bg-blue-500 h-[45px] mx-2 text-white sm:font-bold font-semibold sm:text-sm text-[12px] py-2 px-2 rounded-md hover:bg-blue-600"
>
삭제
</button>
</div>
<p className="text-sm my-3">
- 한 행을 클릭 시 데이터를 수정 할 수 있습니다.
</p>
</div>
<div>
{" "}
{/* grid 사용 */}
<DataGrid
rows={rows}
columns={columns}
onRowClick={getSelectedRows}
checkboxSelection
//onRowDoubleClick={getSelectedRows}
onRowSelectionModelChange={(newRowSelectionModel) => {
setRowSelectionModel(newRowSelectionModel);
}}
rowSelectionModel={rowSelectionModel}
/>
{/* modal update 호출 */}
{modalData && (
<ModalUpdatePage
open={open}
setOpen={setOpen}
modalData={modalData}
handleSelectUserData={handleSelectUserData}
/>
)}
{/* modal insert 호출 */}
{modalInsertYn && (
<ModalInsertPage
insertOpen={insertOpen}
setInsertOpen={setInsertOpen}
handleSelectUserData={handleSelectUserData}
/>
)}
</div>
</div>
</section>
);
};
export default UserManagement;
[ pages / UserManagement / ModalUpdatePage.tsx ]
- modalData 는 관리자가 gird에서 한 행을 클릭시에 ModalUpdatePage.tsx컴포넌트의 props로 보내는 state값으로, 클릭된 한명의 사용자 데이터가 들어있다.
- useEffect를 사용해서 modalData의 값들이 바뀔 때마다(관리자가 다른 행을 클릭한 경우) myData로 새로운 값들이 들어오게 하였음.
- 들어온 값들은 modal 라이브러리를 통해 화면에 출력되게 하였고, 관리자는 출력된 데이터들을 수정할 수 있게 구현함.
import React, { ChangeEvent, useEffect, useState } from "react";
import { Backdrop, Box, Fade, Modal, Typography } from "@mui/material";
import { style } from "./ModalStyle";
import { modalData } from "../../types/userManagementType";
import axiosInstance from "../../utils/axios";
import { toast } from "react-toastify";
interface ownProps {
open: boolean;
setOpen: any;
modalData: modalData;
handleSelectUserData(): void;
}
const ModalUpdatePage = ({
modalData,
open,
setOpen,
handleSelectUserData,
}: ownProps) => {
console.log("111111");
console.log(modalData); //정상작동
const [myData, setMyData] = useState<modalData>({
userId: "",
userName: "",
role: 0,
userEmail: "",
userPhone: "",
userImage: "",
});
useEffect(() => {
setMyData({
userId: modalData.userId,
userName: modalData.userName,
role: modalData.role,
userEmail: modalData.userEmail,
userPhone: modalData.userPhone,
userImage: modalData.userImage,
});
}, [modalData]);
console.log(myData);
// const [open, setOpen] = React.useState(false);
// const handleOpen = () => setOpen(true);
const handleClose = () => setOpen(false);
//공통 onChange함수
const handleChange = (
e: ChangeEvent<HTMLInputElement> | ChangeEvent<HTMLSelectElement>
) => {
console.log("run....>");
e.preventDefault();
console.log(e.target);
const { name, value } = e.target;
setMyData((prevState: modalData) => ({
...prevState,
[name]: value,
}));
};
//데이터 업데이트
const handleUpdate = async (
e: React.MouseEvent<HTMLButtonElement, MouseEvent>
) => {
e.preventDefault();
const body = {
...myData,
};
try {
const response = await axiosInstance.post(
`/userManagement/userDataUpdate`,
body
);
if (response.data.updateSuccess === true) {
toast.success("사용자 데이터가 정상적으로 수정되었습니다.");
handleClose();
handleSelectUserData(); //데이터 재조회
}
} catch (error) {
console.log(error);
}
};
return (
<div>
{/* <Button onClick={handleOpen}>Open modal</Button> */}
<Modal
aria-labelledby="transition-modal-title"
aria-describedby="transition-modal-description"
open={open}
onClose={handleClose}
closeAfterTransition
slots={{ backdrop: Backdrop }}
slotProps={{
backdrop: {
timeout: 500,
},
}}
>
<Fade in={open}>
<Box sx={style} className="rounded-md border-[1px] border-gray-300 ">
<button onClick={handleClose} className="float-right text-md ">
X
</button>
<Typography
id="transition-modal-title"
variant="h6"
component="h2"
className="text-center"
>
사용자관리
</Typography>
<div className="w-full my-3">
<div>
<label className="text-left">아이디</label>
</div>
<div>
<input
className="w-full border-b-[1px] h-9 bg-gray-200 border-gray-300 outline-none "
type="text"
onChange={handleChange}
value={myData.userId}
name="userId"
readOnly
/>
</div>
</div>
<div className="w-full my-3">
<div>
<label className="text-left">이름</label>
</div>
<div>
<input
className="w-full border-b-[1px] h-9 border-gray-300 outline-none "
type="text"
onChange={handleChange}
value={myData.userName}
name="userName"
/>
</div>
</div>
<div className="w-full my-3">
<div>
<label className="text-left">폰번호</label>
</div>
<div>
<input
className="w-full border-b-[1px] h-9 border-gray-300 outline-none "
type="text"
onChange={handleChange}
value={myData.userPhone}
name="userPhone"
/>
</div>
</div>
<div className="w-full my-3">
<div>
<label className="text-left">이메일</label>
</div>
<div>
<input
className="w-full border-b-[1px] h-9 border-gray-300 outline-none "
type="text"
onChange={handleChange}
value={myData.userEmail}
name="userEmail"
/>
</div>
</div>
<div className="w-full my-3">
<div>
<label className="text-left">권한 설정</label>
</div>
<div>
<select
className="border-[1px] border-gray-300 rounded-md w-full h-7 my-2"
value={myData.role}
name="role"
onChange={handleChange}
>
<option value="1">관리자</option>
<option value="0">사용자</option>
</select>
</div>
</div>
<div className="w-full my-3">
<div>
<button
onClick={handleUpdate}
className="bg-blue-600 w-full text-gray-50 rounded-md h-10 hover:bg-blue-700"
>
UPDATE
</button>
</div>
</div>
</Box>
</Fade>
</Modal>
</div>
);
};
export default ModalUpdatePage;
[ pages / UserManagement / ModalInsertPage.tsx ]
- 사용자추가 버튼 클릭했을때 해당되는 컴포넌트로 이부분 역시 modal이 뜨게 하였다.
- 회원가입 로직과 비슷해서 useForm을 사용했고, 추가적으로 mui/material에서 제공해주는 modal라이브러리를 사용했다.
import React, { ChangeEvent, useEffect, useRef, useState } from "react";
import { Backdrop, Box, Fade, Modal, Typography } from "@mui/material";
import { style } from "./ModalStyle";
import { modalInsertData } from "../../types/userManagementType";
import axiosInstance from "../../utils/axios";
import { toast } from "react-toastify";
import { useForm } from "react-hook-form";
interface ownProps {
insertOpen: boolean;
setInsertOpen: any;
handleSelectUserData(): void;
}
const ModalInsertPage = ({
insertOpen,
setInsertOpen,
handleSelectUserData,
}: ownProps) => {
const {
register, //등록함수
handleSubmit,
formState: { errors },
reset,
watch,
} = useForm<modalInsertData>({ mode: "onChange" });
// const [open, setOpen] = React.useState(false);
// const handleOpen = () => setOpen(true);
const handleClose = () => setInsertOpen(false);
const [userEmail, setUserEmail] = useState("");
const [emailList, setEmailList] = useState<string[]>([]);
const [idcheck, setIdCheck] = useState(false);
const domainEmails = [
"@naver.com",
"@gmail.com",
"@daum.net",
"@nate.com",
"@kakao.com",
];
const emailForm =
/^(([^<>()\[\].,;:\s@"]+(\.[^<>()\[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/i;
//이메일 자동완성 함수
const emailHandleChange = (e: ChangeEvent<HTMLInputElement>) => {
e.preventDefault();
const userEmails = domainEmails.map((email) =>
e.target.value.includes("@")
? e.target.value.split("@")[0] + email
: e.target.value + email
);
setUserEmail(e.target.value);
setEmailList(userEmails);
};
useEffect(() => {
reset();
setIdCheck(false);
setUserEmail("");
}, [insertOpen, reset]);
// VALIDATION CHECK START
// register등록함수와 함께 사용한다. {...register("userId", userId)}
const userId = {
required: "필수입력 요소입니다.",
minLength: {
value: 3,
message: "최소 3글자 이상으로 입력해 주세요.",
},
};
const userPassword = {
required: "필수입력 요소입니다.",
pattern: {
value:
/^(?=.*[a-zA-Z])(?=.*[0-9])(?=.*[!@#$%^&*])[a-zA-Z0-9!@#$%^&*]{11,20}$/,
message: "영문, 숫자, 특수문자 포함 11자 이상 20 자 이하로 입력해 주세요",
},
};
const userName = {
required: "필수입력 요소입니다.",
};
const userPhone = {
required: "필수입력 요소입니다.",
minLength: {
value: 11,
message: "숫자 11자리 이상으로 입력해 주세요.",
},
};
//비밀번호 일치여부 체크 - watch사용해서 input의 userPwd의 값을 가져옴
const passwordRef = useRef<string | null>(null);
passwordRef.current = watch("userPassword");
//VALIDATION CHECK END
//아이디 체크
const handleIdCheck = async (
e: React.MouseEvent<HTMLButtonElement, MouseEvent>
) => {
e.preventDefault();
let id = watch("userId");
if (id === "" || id === null) {
toast.info("아이디를 입력해주세요.");
return;
}
const body = {
userId: id,
};
try {
const idCheckValue = await axiosInstance.post(
"/userManagement/idCheck",
body
);
console.log(idCheckValue);
if (idCheckValue.data !== "") {
//값이 있으면
toast.info("이미 등록된 아이디 입니다.");
} else {
toast.info("사용가능한 아이디 입니다.");
setIdCheck(true);
}
} catch (error) {
console.error(error);
}
};
//데이터 추가
const onSubmit = async ({
userId,
userPassword,
userName,
userPhone,
}: modalInsertData) => {
const body = {
userId,
userName,
userPhone,
userEmail,
userPassword: userPassword,
userImage: "",
};
if (idcheck) {
try {
await axiosInstance.post(`/userManagement/userDataInsert`, body);
reset();
setIdCheck(false);
setUserEmail("");
toast.success("사용자 데이터가 저장되었습니다.");
handleClose(); //모달 닫기
handleSelectUserData(); //데이터 재조회
} catch (error) {
console.log(error);
}
} else {
toast.info("아이디 중복확인을 먼저 해주세요.");
}
};
return (
<div>
{/* <Button onClick={handleOpen}>Open modal</Button> */}
<Modal
aria-labelledby="transition-modal-title"
aria-describedby="transition-modal-description"
open={insertOpen}
onClose={handleClose}
closeAfterTransition
slots={{ backdrop: Backdrop }}
slotProps={{
backdrop: {
timeout: 500,
},
}}
>
<Fade in={insertOpen}>
<Box sx={style} className="rounded-md border-[1px] border-gray-300 ">
<button onClick={handleClose} className="float-right text-md ">
X
</button>
<Typography
id="transition-modal-title"
variant="h6"
component="h2"
className="text-center"
>
사용자 추가
</Typography>
<form onSubmit={handleSubmit(onSubmit)}>
<div className="w-full my-3">
<div>
<label className="text-left">아이디</label>
</div>
<div>
<input
className="w-8/12 mr-3 border-b-[1px] h-9 border-gray-300 outline-none "
type="text"
id="userId"
disabled={idcheck}
{...register("userId", userId)}
/>
{idcheck ? (
<button disabled={idcheck}></button>
) : (
<button
onClick={handleIdCheck}
className="float-right sm:w-18 w-3/12 m-auto bg-blue-500 h-[37px] text-white sm:font-bold font-semibold sm:text-sm text-xs px-2 rounded-md hover:bg-blue-600"
>
중복확인
</button>
)}
{errors?.userId && (
<div className="py-1">
<span className="text-red-500 pl-2 text-sm">
{errors.userId.message}
</span>
</div>
)}
</div>
</div>
<div className="w-full my-3">
<div>
<label className="text-left">비밀번호</label>
</div>
<div>
<input
className="w-full border-b-[1px] h-9 border-gray-300 outline-none "
type="password"
id="userPassword"
{...register("userPassword", userPassword)}
/>
{errors?.userPassword && (
<div className="py-1">
<span className="text-red-500 pl-2 text-sm">
{errors.userPassword.message}
</span>
</div>
)}
</div>
</div>
<div className="w-full my-3">
<div>
<label className="text-left">비밀번호 확인</label>
</div>
<div>
<input
className="w-full border-b-[1px] h-9 border-gray-300 outline-none "
type="password"
id="userPasswordCheck"
minLength={11}
{...register("userPasswordCheck", {
required: "필수입력 요소입니다.",
validate: (value) => value === passwordRef.current,
})}
/>
{errors?.userPasswordCheck?.type === "required" && (
<div className="py-1">
<span className="text-red-500 pl-2 text-sm">
{errors.userPasswordCheck.message}
</span>
</div>
)}
{errors?.userPasswordCheck?.type === "validate" && (
<div className="py-1">
<span className="text-red-500 pl-2 text-sm">
비밀번호가 맞지 않습니다. 한번더 확인해 주세요.
</span>
</div>
)}
</div>
</div>
<div className="w-full my-3">
<div>
<label className="text-left">사용자명</label>
</div>
<div>
<input
className="w-full border-b-[1px] h-9 border-gray-300 outline-none "
type="text"
id="userName"
{...register("userName", userName)}
/>
{errors?.userName && (
<div className="py-1">
<span className="text-red-500 pl-2 text-sm">
{errors.userName.message}
</span>
</div>
)}
</div>
</div>
<div className="w-full my-3">
<div>
<label className="text-left">휴대폰번호</label>
</div>
<div>
<input
className="w-full border-b-[1px] h-9 border-gray-300 outline-none "
type="number"
id="userPhone"
{...register("userPhone", userPhone)}
/>
{errors?.userPhone && (
<div className="py-1">
<span className="text-red-500 pl-2 text-sm">
{errors.userPhone.message}
</span>
</div>
)}
</div>
</div>
<div className="w-full my-3">
<div>
<label className="text-left">이메일</label>
</div>
<div>
<input
className="w-full border-b-[1px] h-9 border-gray-300 outline-none "
type="text"
list="email"
value={userEmail}
onChange={emailHandleChange}
/>
<datalist id="email">
{emailList &&
emailList.map((email, idx) => (
<option value={email} key={idx} />
))}
</datalist>
{userEmail && !emailForm.test(userEmail) && (
<p className="p-2 text-red-500 text-sm">
이메일 형식을 확인해주세요.
</p>
)}
</div>
</div>
<div className="w-full my-3">
<div>
<button
type="submit"
className="bg-blue-600 w-full text-gray-50 rounded-md h-10 hover:bg-blue-700"
>
추가
</button>
</div>
</div>
</form>
</Box>
</Fade>
</Modal>
</div>
);
};
export default ModalInsertPage;
[ pages / UserManagement / ModalStyle.tsx ]
export const style = {
position: "absolute" as "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
width: 400,
bgcolor: "background.paper",
//border: "2px solid #000",
//boxShadow: 24,
p: 4,
};
Backend
[ src / index.js ]
- rouet 추가
//route start
app.use("/userManagement", require("./routes/userManagement"));
//route end
[ src / routes / userManagement.js ]
1. 조회
- 관리자가 입력한 사용자 아이디와 이름에 해당되는 데이터를 찾는 로직인데, 특정 데이터를 찾기 위해 $regex 를 사용했다.
- $regex는 mysql의 like연산자와 같은 기능으로 만약 이름으로 th를 써서 조회 버튼을 누를경우에 이름이 th가 들어있는 데이터들을 불러온다.
2. 업데이트
- 관리자로부터 수정된 데이터들을 받아와 업데이트하였음.
3. 추가
- id중복확인을 거쳐야됨. id중복확인은 입력받은 데이터가 db에 있는지 확인하는 것으로, db에 데이터가 있으면 중복되는 것이므로 데이터의 추가가 되지 않도록 구현함.
4. 삭제
- 전달받은 데이터가 mui grid에서 제공해주는 checkbox 기능으로 배열로 들어온다.
- 배열의 데이터가 여러개일수 있으므로, for문을 돌려서 req.body의 인덱스번호를 넣어 데이터를 한개씩 삭제하도록 구현했다.
const express = require("express");
const User = require("../models/User");
const router = express.Router();
//사용자관리 조회
router.post("/", async (req, res, next) => {
console.log(req.body);
let textUserId = req.body.textUserId;
let textUserName = req.body.textUserName;
let userData = {};
try {
//$regex -> mysql의 like연산자
userData = await User.find({
userId: { $regex: textUserId },
userName: { $regex: textUserName },
});
console.log(userData);
return res.status(200).send(userData);
} catch (error) {
next(error);
}
});
//사용자 데이터 업데이트
router.post("/userDataUpdate", async (req, res, next) => {
try {
console.log(req.body);
let commonData = {
userName: req.body.userName,
userPhone: req.body.userPhone,
userEmail: req.body.userEmail,
role: req.body.role,
};
console.log(commonData);
const updateUser = await User.findOneAndUpdate(
{ userId: req.body.userId },
commonData,
{ new: true }
);
console.log(updateUser);
return res.json({
updateSuccess: true,
});
} catch (error) {
next(error);
}
});
//사용자 데이터 추가 - id중복확인
router.post("/idCheck", async (req, res, next) => {
let userId = req.body.userId;
console.log(userId);
try {
const userIdCheck = await User.findOne({ userId: userId });
console.log(userIdCheck);
return res.status(200).send(userIdCheck);
} catch (error) {
next(error);
}
});
//사용자 데이터 추가 - 데이터등록
router.post("/userDataInsert", async (req, res, next) => {
console.log(req.body);
try {
const user = new User(req.body);
await user.save();
return res.sendStatus(200);
} catch (error) {
next(error);
}
});
//사용자 데이터 삭제
router.post("/userDataDelete", async (req, res, next) => {
console.log(req.body);
try {
for (let index in req.body) {
console.log(index);
console.log(req.body[index]);
const delYn = await User.deleteOne({
userId: req.body[index],
});
console.log(delYn);
}
return res.json({ deleteSuccess: true });
} catch (error) {
next(error);
}
});
module.exports = router;
[ 결과화면 ]
- 사용자 데이터 조회
- 데이터 추가
- 데이터 수정
- 데이터 삭제
'React + Node.js' 카테고리의 다른 글
[MySchedule project] 13. 카카오 로그인 구현 (2) - Backend (0) | 2024.02.12 |
---|---|
[MySchedule project] 12. 카카오 로그인 구현 (1) - Frontend (0) | 2024.02.12 |
[MySchedule project] 10. 마이페이지 로직(2) - 이미지(파일) 업로드 (0) | 2024.02.01 |
[MySchedule project] 9. 마이페이지 로직(1) - 조회, 업데이트 (1) | 2024.02.01 |
[MySchedule project] 8. 인증에 의한 header, Route 로직 / 로그아웃 (2) | 2024.01.31 |