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;