노력이 좋아서
<step75>'react_고객관리 사이트 구현_step1'
zoaseo
2022. 7. 7. 09:57
1)
고객관리
green_customer_client
Header
Footer
CustomerList
Customer
DetailCustomer
라이브러리
axios
react-router-dom
material-ui
https://mui.com/material-ui/getting-started/overview/
Material UI - Overview - Material UI
Material UI is a library of React UI components that implements Google's Material Design.
mui.com
고객관리서버
api 서버
node.js
express
데이터베이스 AWS
https://blog.naver.com/pink_candy02/222624278720
AWS RDS 서비스를 이용하여 MySQL DB 구축하기
https://aws.amazon.com/ko/ 회원가입하기 1) 이메일 주소, 암호, AWS 계정 이름을 선택 후 계속(1/5단...
blog.naver.com
2)
green_customer_client
npm install @mui/material @emotion/react @emotion/styled
npm install router-react-dom@6
npm install axios
npm install react-daum-postcode
npm install react-dom
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 { useNavigate } from 'react-router-dom';
const CreateCustomer = () => {
const navigate = useNavigate(); // 리다이렉션
// 우편번호 관리하기
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 onSubmit = (e) => {
// form에 원래 연결된 이벤트를 제거
e.preventDefault();
console.log(formData);
// input에 값이 있는지 체크하고
// 입력이 다되어있으면 post전송
insertCustomer();
}
function insertCustomer(){
axios.post("http://localhost:3001/customers",formData)
.then((result)=>{
console.log(result);
navigate("/"); // 리다이렉션 추가
})
.catch(e=>{
console.log(e);
})
}
return (
<div>
<h2>신규 고객 등록하기</h2>
<form onSubmit={onSubmit}>
<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;
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}</TableCell>
<TableCell>{customer.add2}</TableCell>
</TableRow>
);
};
export default Customer;
CustomerList.js
import React from 'react';
import { Table, TableBody, TableHead, TableCell, TableRow} from '@mui/material';
import Customer from './Customer';
import axios from 'axios';
import useAsync from '../customHook/useAsync';
async function getCustomers(){
const response = await axios.get("http://localhost:3001/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>
<TableCell>상세주소</TableCell>
</TableRow>
</TableHead>
<TableBody>
{customers.map(customer=>(
<Customer key={customer.no} customer={customer}/>
))}
</TableBody>
</Table>
</div>
);
};
export default CustomerList;
DetailCustomer.js
import React from 'react';
import { Table, TableBody, TableCell, TableRow } from '@mui/material';
import axios from 'axios';
import useAsync from '../customHook/useAsync';
import { useParams } from 'react-router-dom';
async function getCustomers(no){
const response = await axios.get(`http://localhost:3001/detailview/${no}`);
return response.data;
}
const DetailCustomer = () => {
const { no } = useParams();
const [ state ] = useAsync(()=>getCustomers(no),[no]);
const { loading, data:customer, error } = state;
if(loading) return <div>로딩중.....</div>
if(error) return <div>에러가 발생했습니다.</div>
if(!customer) return <div>로딩중입니다.</div>
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.add1}</TableCell>
</TableRow>
<TableRow>
<TableCell>상세주소</TableCell>
<TableCell>{customer.add2}</TableCell>
</TableRow>
</TableBody>
</Table>
</div>
);
};
export default DetailCustomer;
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;
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;
PopupDom.js
import ReactDom from 'react-dom';
const PopupDom = ({children}) => {
const el = document.getElementById('popupDom');
return ReactDom.createPortal(children, el);
};
export default PopupDom;
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;
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 {
...initialState,
loading: true,
}
case "SUCCESS":
return {
...initialState,
data: action.data,
}
case "ERROR":
return {
...initialState,
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;
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;
}
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';
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>
<Route path="/" element={<CustomerList customers={customers}/>}/>
<Route path="/detailview/:no" element={<DetailCustomer/>}/>
<Route path="/write" element={<CreateCustomer/>}/>
</Routes>
<Footer/>
</div>
);
}
export default App;
green_customer_server
npm init
npm install express
npm install cors
npm install mysql
database.json
{
"host": "customer-data.cfwxhfzwpudu.ap-northeast-2.rds.amazonaws.com",
"user": "admin",
"password": "xxxx",
"port": "3306",
"database": "customers"
}
index.js
const express = require("express");
const cors = require("cors");
const app = express();
const port = 3001;
const mysql = require("mysql");
const fs = require("fs");
const dbinfo = fs.readFileSync('./database.json');
// 받아온 json데이터를 객체형태로 변경 JSON.parse
const conf = JSON.parse(dbinfo);
// connection mysql연결 createConnection()
// connection.connect() 연결하기
// connection.end() 연결종료
// connection.query('쿼리문', callback함수)
// callback(error, result, result의 field정보)
const connection = mysql.createConnection({
host: conf.host,
user: conf.user,
password: conf.password,
port: conf.port,
database: conf.database,
})
app.use(express.json());
app.use(cors());
app.get('/customers', async (req,res)=>{
// connection.connect();
connection.query(
"select * from customers_table",
(err, rows, fields)=>{
res.send(rows);
}
)
// connection.end();
})
// 서버실행
app.listen(port, ()=>{
console.log("고객 서버가 돌아가고 있습니다.");
})
app.get('/detailview/:no', async (req,res)=>{
const params = req.params;
const { no } = params;
connection.query(
`select * from customers_table where no=${no}`,
(err, rows, fields)=>{
res.send(rows[0]);
}
)
})
app.post('/customers', async (req,res)=>{
const body = req.body;
const { c_name, c_phone, c_birth, c_gender, c_add, c_adddetail } = body;
if(!c_name || !c_phone || !c_birth || !c_gender || !c_add || !c_adddetail) {
res.send("모든 필드를 입력해주세요");
}
connection.query(
`insert into customers_table (name,phone,birth,gender,add1,add2)
values (
'${c_name}','${c_phone}','${c_birth}','${c_gender}','${c_add}','${c_adddetail}')`,
(err, rows, fields)=>{
res.send(rows);
}
)
})