React
[고객 관리 시스템] 2탄
1일1코딩
2020. 6. 13. 23:29
www.youtube.com/watch?v=zug4VBcZOrI&list=PLRx0vPvlEmdD1pSqKZiTihy5rplxecNpz&index=8
유튜브 나동빈님의 강의를 듣고 시작한 프로젝트입니다.
이번 시간에는 Database에 데이터를 전송하고, 삭제 하는 기능을 추가하는 시간입니다.
실제 Database를 생성 할 것입니다.
아래의 명령어로 customer라는 table을 생성합니다.
create table customer
이제 사용 될 테이블을 생성합니다.
CREATE TABLE CUSTOMER (
id INT(64),
image VARCHAR(1024),
name VARCHAR(64),
birth VARCHAR(64),
gender VARCHAR(64),
job VARCHAR2(10),
createdDate DATETIME,
isDeleted INT,
PRIMARY KEY id
);
아래 INSERT를 이용하여 값을 넣어줍니다.
INSERT INTO CUSTOMER VALUES ()
select문을 이용하여 Table에 값이 정상적으로 들어갔는지 확인해봅니다.
select * from customer;
1. CustomerAdd
import React from 'react';
import { post } from 'axios';
import Dialog from '@material-ui/core/Dialog';
import DialogActions from '@material-ui/core/DialogActions';
import DialogTitle from '@material-ui/core/DialogTitle';
import DialogContent from '@material-ui/core/DialogContent';
import TextField from '@material-ui/core/TextField';
import Button from '@material-ui/core/Button';
import {withStyles} from '@material-ui/core/styles';
const styles = theme => ({
hidden : {
display :'none'
}
})
class CustomerAdd extends React.Component{
constructor(props) {
super(props);
this.state = {
file : null,
userName : '',
birth : '',
gender : '',
job : '',
fileName : '',
open : false //dialog open 상태
};
this.handleFormSubmit = this.handleFormSubmit.bind(this);
this.addCustomer = this.addCustomer.bind(this);
this.handleFileChange = this.handleFileChange.bind(this);
this.handleValueChange = this.handleValueChange.bind(this);
this.handleClickOpen = this.handleClickOpen.bind(this);
this.handleClickClose = this.handleClickClose.bind(this);
}
handleClickOpen() {
this.setState({
open : true
});
}
handleClickClose() {
this.setState({
file : null,
userName : '',
birth : '',
gender : '',
job : '',
fileName : '',
open : false
});
}
//모든 값이 설정되고 OK버튼을 누를 시 발생하는 Submit이벤트
handleFormSubmit(e) {
e.preventDefault();
this.addCustomer()
.then(function(response) {
console.log(response.data);
//addCustomer가 성공적으로 발생 시 updateCustomer발생
this.props.updateCustomer();
}.bind(this));
this.setState({
file : null,
userName : '',
birth : '',
gender : '',
job : '',
fileName : '',
open : false
});
//window.location.reload();
}
addCustomer(e) {
//form형태로 현재 설정된 state값들을 만들어 post로 전달한다.
const url = '/api/customers';
const formData = new FormData();
formData.append('image', this.state.file);
formData.append('fileName', this.state.fileName);
formData.append('userName', this.state.userName);
formData.append('birth', this.state.birth);
formData.append('gender', this.state.gender);
formData.append('job', this.state.job);
//전송할 데이터에 파일이 있을경우
const config = {
headers : {
'context-type' : 'multipart/form-data'
}
}
return post(url, formData, config);
}
//file선택이 될 경우 file, fileName을 설정
handleFileChange(e) {
e.preventDefault();
this.setState({
file : e.target.files[0],
fileName : e.target.value
});
}
//직업, 생일, 성별, 직업의 onChange 발생 시 setState를 통하여 값을 넣어줌.
handleValueChange(e){
let nextState = {};
nextState[e.target.name] = e.target.value;
this.setState(nextState);
}
render() {
const {classes} = this.props;
return(
<div>
<Button variant="contained" color='primary' onClick={this.handleClickOpen.bind(this)}>
고객 추가하기
</Button>
<Dialog open={this.state.open} onClose={this.handleClickClose}>
<DialogTitle>고객 추가</DialogTitle>
<DialogContent>
<input className={classes.hidden} accept="image/*" id="raised-button-file" type='file' file={this.state.file} value={this.state.fileName} onChange={this.handleFileChange}></input>
<label htmlFor="raised-button-file">
<Button variant="contained" color="primary" component="span" name="file">
{this.state.fileName === "" ? "프로필 이미지 선택" : this.state.fileName}
</Button>
</label>
<br/>
<TextField label="이름" type='text' name='userName' value={this.state.userName} onChange={this.handleValueChange}></TextField><br/>
<TextField label="생년월일" type='text' name='birth' value={this.state.birth} onChange={this.handleValueChange}></TextField><br/>
<TextField label="성별" type='text' name='gender' value={this.state.gender} onChange={this.handleValueChange}></TextField><br/>
<TextField label="직업" type='text' name='job' value={this.state.job} onChange={this.handleValueChange}></TextField><br/>
</DialogContent>
<DialogActions>
<Button variant="contained" color="primary" onClick={this.handleFormSubmit}>
추가
</Button>
<Button variant="outlined" color="primary" onClick={this.handleClickClose}>
닫기
</Button>
</DialogActions>
</Dialog>
</div>
)
}
}
export default withStyles(styles)(CustomerAdd);
2. Server.js
const fs = require('fs');
const express = require("express");
const bodyParse = require("body-parser");
const app = express();
const port = process.env.PORT || 5000;
app.use(bodyParse.json());
app.use(bodyParse.urlencoded({extended : true}));
const data = fs.readFileSync('./database.json');
//npm install --save mysql
//database 연동을 위한 라이브러리
const conf = JSON.parse(data);
const mysql = require('mysql');
const connection = mysql.createConnection({
host : conf.host,
user : conf.user,
password : conf.password,
port : conf.port,
database : conf.database
});
connection.connect();
//파일 처리를 위한 multer 라이브러리
//npm install --save multer
const multer = require('multer');
const upload = multer({dest : './upload'});
//database에서 customers table을 가져온다.
app.get('/api/customers', (req, res) => {
connection.query(
// isDeleted가 0인 것만 가져옴
"SELECT * FROM CUSTOMER WHERE isDeleted = 0",
(err, rows, fields) => {
res.send(rows);
}
);
});
app.use('/image', express.static('./upload')); //사용자는 image 폴더로 확인하고 실제 매핑은 ./upload
//table에 새롭게 추가되는 사용자 정보를 추가한다.
app.post('/api/customers', upload.single('image'), function(req, res) {
let sql = "INSERT INTO CUSTOMER VALUES (null, ?, ?, ?, ?, ?, now(), 0)";
let image = '/image/' + req.file.filename;
let name = req.body.userName;
let birth = req.body.birth;
let gender = req.body.gender;
let job = req.body.job;
let params = [image, name, birth, gender, job];
connection.query(sql, params, function(err, rows, fields) {
res.send(rows);
});
});
//table에서 사용자를 삭제한다. 실제 databases에서 삭제되지는 않지만, isDelete값으로 구분한다.
app.delete('/api/customer/:id', function(req, res) {
let sql = "UPDATE CUSTOMER SET isDeleted = 1 WHERE id = ?";
let params = [req.params.id];
connection.query(sql, params, function(err, rows, fields) {
res.send(rows);
});
});
app.listen(port, () => console.log(`Listening on port ${port}`));
3. CustomerDelete
import React from 'react';
import Button from '@material-ui/core/Button';
import Dialog from '@material-ui/core/Dialog';
import DialogActions from '@material-ui/core/DialogActions';
import DialogTitle from '@material-ui/core/DialogTitle';
import DialogContent from '@material-ui/core/DialogContent';
import TextField from '@material-ui/core/TextField';
import { Typography } from '@material-ui/core';
class CustomerDelete extends React.Component{
constructor(props) {
super(props);
this.state = {
open : false
};
}
deleteCutomer(id) {
const url = '/api/customer/' + id;
fetch(url, {
method : "DELETE"
});
this.props.updateCustomer();
}
handleDeleteClose() {
this.setState({
open : false
});
}
handleDeleteOpen() {
this.setState({
open : true
});
}
render() {
return(
<div>
<Button variant="contained" color="secondary" onClick={this.handleDeleteOpen.bind(this)}>삭제</Button>
<Dialog open={this.state.open} onClose={this.handleDeleteClose.bind(this)}>
<DialogTitle>삭제</DialogTitle>
<DialogContent>
<Typography gutterBottom>
정말 삭제하시겠습니까?
</Typography>
</DialogContent>
<DialogActions>
<Button variant="contained" color="primary" onClick={this.deleteCutomer.bind(this, this.props.id)}>
네
</Button>
<Button variant="outlined" color="primary" onClick={this.handleDeleteClose.bind(this)}>
아니오
</Button>
</DialogActions>
</Dialog>
</div>
)
}
}
export default CustomerDelete;