green_customer_client
Header
Footer
CustomerList
Customer
DetailCustomer
- 라이브러리 설치
axios
react-router-dom
react-daum-postcode
material-ui
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 react-router-dom@6
npm install axios
npm install react-daum-postcode
npm install @mui/material @emotion/react @emotion/styled
app.js
import './App.css';
import CustomerList from './components/CustomerList';
import DetailCustomer from './components/DetailCustomer';
import Footer from './components/Footer';
import Header from './components/Header';
import { Route, Routes } from "react-router-dom";
import CreateCustomer from './components/CreateCustomer';
import EditCustomer from './components/EditCustomer';
const customers = [
{
no: 1,
name: "고객",
phone: "01012345678",
birth: "19920206",
gender: "여성",
add: "울산시 남구"
},
{
no: 2,
name: "그린",
phone: "01012345678",
birth: "19920206",
gender: "남성",
add: "울산시 동구"
},
{
no: 3,
name: "kh",
phone: "01012345678",
birth: "19920206",
gender: "여성",
add: "울산시 남구"
}
]
function App() {
return (
<div className="App">
<Header />
<Routes>
{/* props로 위에 {customers}를 CustomerList로 전달 */}
<Route path="/" element={<CustomerList customers={customers}/>} />
<Route path="/detailview/:no" element={<DetailCustomer/>} />
<Route path="/write" element={<CreateCustomer/>} />
<Route path="/edit/:no" element={<EditCustomer/>} />
</Routes>
<Footer/>
</div>
);
}
export default App;
components/CustomerList.js
// components/Customer에게 props 전달해주기
import React from 'react';
import axios from 'axios';
import { Table, TableBody, TableHead, TableCell, TableRow } from '@mui/material';
import Customer from './Customer';
import { API_URL } from '../config/contansts.js';
import useAsync from '../customHook/useAsync';
async function getCustomers(){
const response = await axios.get(`${API_URL}/customers`);
return response.data;
}
const CustomerList = () => {
const [state] = useAsync(getCustomers, [])
const { loading, data: customers, error } = state;
if(loading) return <div>로딩중 ...</div>
if(error) return <div>에러가 발생했습니다.</div>
if(!customers) return <div>로딩중입니다.</div>
return (
<div>
<h2>고객리스트</h2>
<Table>
<TableHead>
<TableRow>
<TableCell>번호</TableCell>
<TableCell>이름</TableCell>
<TableCell>연락처</TableCell>
<TableCell>생년월일</TableCell>
<TableCell>성별</TableCell>
<TableCell>주소</TableCell>
</TableRow>
</TableHead>
<TableBody>
{customers.map(customer=>(
<Customer key={customer.no} customer={customer}/>
))}
</TableBody>
</Table>
</div>
);
};
export default CustomerList;
components/Customer.js
import React from 'react';
import { TableRow, TableCell } from '@mui/material';
import { Link } from "react-router-dom";
const Customer = ( {customer} ) => {
return (
<TableRow>
<TableCell>{customer.no}</TableCell>
<TableCell><Link to ={`/detailview/${customer.no}`}>{customer.name}</Link></TableCell>
<TableCell>{customer.phone}</TableCell>
<TableCell>{customer.birth}</TableCell>
<TableCell>{customer.gender}</TableCell>
<TableCell>{customer.add1}<br/>{customer.add2}</TableCell>
</TableRow>
);
};
export default Customer;
config/contansts.js
export const API_URL = "http://localhost:3001";
customHook/useAsync.js
import { useReducer, useEffect } from 'react'
const initialState = {
loading: false,
data: null,
error: null
}
function reducer(state, action) {
switch(action.type) {
case "LOADING":
return {
loading: true,
data: null,
error: null
};
case "SUCCESS":
return {
loading: false,
data: action.data,
error: null
}
case "ERROR":
return {
loading: false,
data: null,
error: action.error
}
default:
return state;
}
}
function useAsync(callback, deps=[]) {
const [state, dispatch] = useReducer(reducer, initialState);
const fetchDate = async () => {
dispatch({type: "LOADING"});
try {
const data = await callback();
dispatch({
type: "SUCCESS",
data: data
})
}
catch(e) {
dispatch({
type: "ERROR",
error: e
})
}
}
useEffect(()=>{
fetchDate();
//eslint-disable-next-line
}, deps);
return [state, fetchDate];
}
export default useAsync
components/Header.js
import React from 'react';
import { Link } from 'react-router-dom';
const Header = () => {
return (
<div id="header">
<h1>그린고객센터</h1>
<ul>
<li><Link to="/">고객리스트보기</Link></li>
<li><Link to="/write">신규 고객 등록하기</Link></li>
<li>고객 검색</li>
</ul>
</div>
);
};
export default Header;
components/Footer.js
import React from 'react';
const Footer = () => {
return (
<div id="footer">
<h1>그린 고객 관리</h1>
<p>
대표자: 김그린 | 사업자 등록번호 214-86-12345<br/>
통신판매업신고: 강남 13711호 | 학원등록번호: 강남 제 1104호<br/>
주소: 서울시 강남구 역삼동 강남빌딩 5층<br/>
copyright (c) 2022 gitaAcademy
</p>
</div>
);
};
export default Footer;
components/DetailCustomer.js
import React from 'react';
import { Table, TableBody, TableCell, TableRow} from '@mui/material';
import axios from 'axios';
import { useParams, useNavigate, Link } from 'react-router-dom';
import useAsync from '../customHook/useAsync';
import { API_URL } from '../config/contansts';
async function getCustomer(no) {
const response = await axios.get(`${API_URL}/detailview/${no}`);
return response.data;
}
const DetailCustomer = ( ) => {
const navigate = useNavigate();
const { no } = useParams();
const [state] = useAsync(()=>getCustomer(no), [no]);
const { loading, data: customer, error} = state;
// 삭제하기
const onDelete = () => {
axios.delete(`${API_URL}/detailview/${no}`)
.then(result=> {
console.log('삭제되었습니다.')
navigate('/');
})
.catch(err=> {
console.log(err);
})
}
if(loading) return <div>로딩중입니다...</div>
if(error) return <div>에러가 발생했습니다.</div>
if(!customer) return null;
return (
<div>
<h2>고객 상세정보</h2>
<Table>
<TableBody>
<TableRow>
<TableCell>고객명</TableCell>
<TableCell>{customer.name}</TableCell>
</TableRow>
<TableRow>
<TableCell>연락처</TableCell>
<TableCell>{customer.phone}</TableCell>
</TableRow>
<TableRow>
<TableCell>생년월일</TableCell>
<TableCell>{customer.birth}</TableCell>
</TableRow>
<TableRow>
<TableCell>성별</TableCell>
<TableCell>{customer.gender}</TableCell>
</TableRow>
<TableRow>
<TableCell>주소</TableCell>
<TableCell>{customer.add1}{customer.add2}</TableCell>
</TableRow>
<TableRow>
<TableCell colSpan={2}>
<button onClick={onDelete}>삭제</button>
<button><Link to={`/edit/${no}`}>수정</Link></button>
</TableCell>
</TableRow>
</TableBody>
</Table>
</div>
);
};
export default DetailCustomer;
components/CreateCustomer.js
import React, { useState } from 'react';
import { Table, TableBody, TableRow, TableCell } from '@mui/material';
import PopupDom from "./PopupDom"
import PopupPostCode from "./PopupPostCode"
import axios from "axios";
import { API_URL } from '../config/contansts';
import { useNavigate } from 'react-router-dom';
const CreateCustomer = () => {
// 우편번호 관리하기
const onAddData = (data) => {
console.log(data);
setFormData({
...formData,
c_add: data.address
})
}
// 팝업창 상태 관리
const [ isPopupOpen, setIsPopupOpen ] = useState(false);
// 팝업창 상태 true로 변경
const openPostCode = ()=> {
setIsPopupOpen(true);
}
// 팝업창 상태 false로 변경
const closePostCode = () => {
setIsPopupOpen(false);
}
const [ formData, setFormData ] = useState({
c_name: "",
c_phone: "",
c_birth: "",
c_gender: "",
c_add: "",
c_adddetail: ""
})
const onChange = (e) => {
const { name, value } = e.target;
setFormData({
...formData,
[name]: value
})
}
// 폼 submit 이벤트
const onSumbit = (e) => {
// form에 원래 연결된 이벤트를 제거
e.preventDefault();
console.log(formData);
// 전화번호가 숫자인지 체크하기
if(isNaN(formData.c_phone)){
alert('전화번호는 숫자만 입력해주세요');
setFormData({
...formData,
c_phone: ""
})
}
// input에 값이 있는지 체크하고
// 입력이 다되어있으면 post전송
else if( formData.c_name !== "" && formData.c_phone !== "" &&
formData.c_birth !== "" && formData.c_gender !=="" &&
formData.c_add !== "" && formData.c_adddetail !== "" ){
insertCustomer();
} else {
alert('모든 항목을 기입해주세요');
}
}
const navigate = useNavigate();
function insertCustomer(){
// axios.post(`${API_URL}/customers`, {
// name: formData.c_name,
// phone: formData.c_phone,
// birth: formData.c_birth,
// gender: formData.c_gender,
// add1: formData.c_add,
// add2: formData.c_adddetail
// })
axios.post(`${API_URL}/addCustomer`, formData)
.then((result)=>{
console.log(result)
navigate("/");
})
.catch((e)=> {
console.log(e);
})
}
return (
<div>
<h2>신규 고객 등록하기</h2>
<form onSubmit={onSumbit}>
<Table>
<TableBody>
<TableRow>
<TableCell>이름</TableCell>
<TableCell>
<input name="c_name" type="text"
value={formData.c_name}
onChange={onChange}/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>연락처</TableCell>
<TableCell>
<input name="c_phone" type="text"
value={formData.c_phone}
onChange={onChange}/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>생년월일</TableCell>
<TableCell>
<input name="c_birth" type="date"
value={formData.c_birth}
onChange={onChange}/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>성별</TableCell>
<TableCell>
여성<input name="c_gender" type="radio"
value="여성"
onChange={onChange}/>
남성<input name="c_gender" type="radio"
value="남성"
onChange={onChange}/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>주소</TableCell>
<TableCell>
<input name="c_add" type="text"
value={formData.c_add}
onChange={onChange}/>
<input name="c_adddetail" type="text"
value={formData.c_adddetail}
onChange={onChange}/>
<button type="button" onClick={openPostCode}>우편번호 검색</button>
<div id="popupDom">
{isPopupOpen && (
<PopupDom>
<PopupPostCode onClose={closePostCode}
onAddData={onAddData}
/>
</PopupDom>
)}
</div>
</TableCell>
</TableRow>
<TableRow>
<TableCell colSpan={2}>
<button type="submit">등록</button>
<button type="reset">취소</button>
</TableCell>
</TableRow>
</TableBody>
</Table>
</form>
</div>
);
};
export default CreateCustomer;
components/EditCustomer.js
import React, { useState, useEffect } from 'react';
import { Table, TableBody, TableRow, TableCell } from '@mui/material';
import PopupDom from "./PopupDom"
import PopupPostCode from "./PopupPostCode"
import axios from "axios";
import useAsync from '../customHook/useAsync';
import { API_URL } from '../config/contansts';
import { useParams, useNavigate } from 'react-router-dom';
async function getCustomer(no) {
const response = await axios.get(`${API_URL}/detailview/${no}`);
return response.data;
}
const EditCustomer = () => {
const { no } = useParams();
const [ formData, setFormData ] = useState({
c_name: "",
c_phone: "",
c_birth: "",
c_gender: "",
c_add: "",
c_adddetail: ""
})
// const [state] = useAsync(()=>getCustomer(no), [no]);
const [state] = useAsync(()=>getCustomer(no), [no]);
const { loading, data: customer, error} = state;
useEffect(()=>{
setFormData({
// 앞에는 인풋의 네임값(c_add), 뒤에는 db 컬럼값(customer.add1)
c_name: customer? customer.name : "",
c_phone: customer? customer.phone : "",
c_birth: customer? customer.birth : "",
c_gender: customer? customer.gender : "",
c_add: customer? customer.add1 : "",
c_adddetail: customer? customer.add2 : "",
})
}, [customer]);
const navigate = useNavigate();
// console.log(customer);
// 우편번호 관리하기
const onAddData = (data) => {
console.log(data);
setFormData({
...formData,
c_add: data.address
})
}
// 팝업창 상태 관리
const [ isPopupOpen, setIsPopupOpen ] = useState(false);
// 팝업창 상태 true로 변경
const openPostCode = ()=> {
setIsPopupOpen(true);
}
// 팝업창 상태 false로 변경
const closePostCode = () => {
setIsPopupOpen(false);
}
const onChange = (e) => {
const { name, value } = e.target;
setFormData({
...formData,
[name]: value
})
}
// 폼 submit 이벤트
const onSumbit = (e) => {
// form에 원래 연결된 이벤트를 제거
e.preventDefault();
// console.log(formData);
// 전화번호가 숫자인지 체크하기
if(isNaN(formData.c_phone)){
alert('전화번호는 숫자만 입력해주세요');
setFormData({
...formData,
c_phone: ""
})
}
// input에 값이 있는지 체크하고
// 입력이 다되어있으면 post전송
else if( formData.c_name.value !== "" && formData.c_phone.value !== "" &&
formData.c_birth.value !== "" && formData.c_gender.value !=="" &&
formData.c_add.value !== "" && formData.c_adddetail.value !== "" ){
updateCustomer();
} else {
alert('모든 항목을 기입해주세요');
}
}
// 수정하기
function updateCustomer() {
axios.put(`${API_URL}/edit/${no}`, formData)
.then(result=> {
console.log('수정되었습니다.')
navigate('/');
})
.catch(err=> {
console.log(err);
})
}
if(loading) return <div>로딩중입니다...</div>
if(error) return <div>에러가 발생했습니다.</div>
if(!customer) return null;
// console.log(customer.name);
return (
<div>
<h2>고객 정보 수정하기</h2>
<form onSubmit={onSumbit}>
<Table>
<TableBody>
<TableRow>
<TableCell>이름</TableCell>
<TableCell>
<input name="c_name" type="text"
value={formData.c_name}
onChange={onChange}/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>연락처</TableCell>
<TableCell>
<input name="c_phone" type="text"
value={formData.c_phone}
onChange={onChange}/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>생년월일</TableCell>
<TableCell>
<input name="c_birth" type="date"
value={formData.c_birth}
onChange={onChange}/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>성별</TableCell>
<TableCell>
여성<input name="c_gender" type="radio"
value="여성"
onChange={onChange}
checked={formData.c_gender === "여성" ? true : false }/>
남성<input name="c_gender" type="radio"
value="남성"
onChange={onChange}
checked={formData.c_gender === "남성" ? true : false }/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>주소</TableCell>
<TableCell>
<input name="c_add" type="text"
value={formData.c_add}
onChange={onChange}/>
<input name="c_adddetail" type="text"
value={formData.c_adddetail}
onChange={onChange}/>
<button type="button" onClick={openPostCode}>우편번호 검색</button>
<div id="popupDom">
{isPopupOpen && (
<PopupDom>
<PopupPostCode onClose={closePostCode}
onAddData={onAddData}
/>
</PopupDom>
)}
</div>
</TableCell>
</TableRow>
<TableRow>
<TableCell colSpan={2}>
<button type="submit">등록</button>
<button type="reset">취소</button>
</TableCell>
</TableRow>
</TableBody>
</Table>
</form>
</div>
);
};
export default EditCustomer;
components/PopupDom.js
import ReactDOM from "react-dom";
const PopupDom = ({children}) => {
const el = document.getElementById('popupDom');
return ReactDOM.createPortal(children, el);
};
export default PopupDom;
components/PopupPostCode.js
import React from 'react';
import DaumPostcode from "react-daum-postcode";
const PopupPostCode = (props) => {
// 우편번호 검색 후 주소 클릭 시 실행될 함수, data callback 용
const handlePostCode = (data) => {
let fullAddress = data.address;
let extraAddress = '';
if (data.addressType === 'R') {
if (data.bname !== '') {
extraAddress += data.bname;
}
if (data.buildingName !== '') {
extraAddress += (extraAddress !== '' ? `, ${data.buildingName}` : data.buildingName);
}
fullAddress += (extraAddress !== '' ? ` (${extraAddress})` : '');
}
console.log(data)
console.log(fullAddress)
console.log(data.zonecode)
props.onAddData(data);
}
const postCodeStyle = {
display: "block",
position: "absolute",
top: '50%',
left: '50%',
transform:'translate(-50%,-50%)',
width: "600px",
height: "600px",
padding: "7px",
border: "2px solid #666"
};
return(
<div>
<DaumPostcode style={postCodeStyle} onComplete={handlePostCode} />
<button type='button' onClick={() => {props.onClose()}} className='postCode_btn'>입력</button>
</div>
)
}
export default PopupPostCode;
index.js
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';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>
);
// 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();
App.css
li { list-style: none; }
a { text-decoration: none; color: inherit; }
.App {
width: 100%;
max-width: 1200px;
margin: 0 auto;
}
#header {
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #ccc;
margin-bottom: 50px;
}
#header ul {
display: flex;
}
#header li {
padding: 0 20px;
}
h1 {
font-size: 24px;
}
#footer {
padding-top: 50px;
}
'Stack > React' 카테고리의 다른 글
[React] git clone 한 뒤, 오류 없이 npm start 하는 방법 (0) | 2022.07.09 |
---|---|
[React] 고객관리 사이트 구현 (Server) (0) | 2022.07.07 |
[React] useMemo / useCallback (0) | 2022.07.05 |
[React] Lamp 쇼핑몰 구현하기 8 (깃허브 주소 및 최종 코드) (0) | 2022.07.05 |
[React] Lamp 쇼핑몰 구현하기 7 / 배포 (0) | 2022.07.05 |