비트 컴퓨터 [Node.js] - ③

402082 단어 node.js교육node.js

[실습] insert 기능 추가하기



[Back] app.js



var express = require('express');
const { is } = require('express/lib/request');
const res = require('express/lib/response');
var app = express();

app.use(express.json());
app.use(express.urlencoded({ extended: false }));

var router = express.Router();
// const { route } = require('./routes/tiger');

app.use('/api/Swtool',
    router.post('/', function (req, res, next) {
        console.log(req.query.type);

        if (req.query.type == 'list') {

            req.body.mapper = 'SwToolsMapper';
            req.body.crud = 'select';
            req.body.mapper_id = 'selectSwtool';

            router.use('/', require('./dbconnect_Module'));
            next('route');

        } else if (req.query.type == 'save') {

            req.body.mapper = 'SwToolsMapper';
            req.body.crud = 'insert';
            req.body.mapper_id = 'insertSwToolsInfo';

            router.use('/', require('./dbconnect_Module'));
            next('route');

        }

    }),

);

var port = process.env.PORT || '5000';
app.listen(port, () => { console.log('listen'); });


module.exports = app;



[Back] dbconnect_Module.js



var express = require('express');
var router = express.Router();

const mysql = require('mysql');
// const con = mysql.createConnection( {
//     host: "database-1.cziyl6rwn9rl.ap-northeast-2.rds.amazonaws.com",
//     port: 3306,
//     database: 'ahhyun',
//     user: 'admin',
//     password: '12345678',

// } );

// 풀 만듬
const pool = mysql.createPool({
    // 풀 환경설정
    connectionLimit: 66, // 커넥션 풀에 저장할 수 있는 최대 커넥션 개수 (최대 연결 수)
    waitForConnections: true, // 풀이 꽉 찼다면, 여유가 생길 때까지 새 연결 대기 여부

    // 연결할 디비 설정
    host: "database-1.cziyl6rwn9rl.ap-northeast-2.rds.amazonaws.com",
    port: "3306",
    database: 'ahhyun',
    user: 'admin',
    password: '12345678',
})

router.post('/', function (req, res, next) {
    try {

        let param = req.body;

        const myBatisMapper = require('mybatis-mapper');
        myBatisMapper.createMapper(['SwToolsMapper.xml']);

        let query = myBatisMapper.getStatement(
            param.mapper, // 'SwToolsMapper', 
            param.mapper_id, // 'selectSwtool', 
            param,
            { language: 'sql', indent: '  ' },
        );

        pool.getConnection(function (err, con) {
            con.query(
                query,
                (error, rows, fields) => {
                    if (error) throw error;

                    if (req.body.crud == "select") {
                        res.send(rows)
                    } else {
                        res.send("succ");
                    }

                }
            );

            // 쿼리2

            // 쿼리3

            // 커넥션 풀에서 연결 제거
            con.release();
        })

    } catch (error) {
        console.log('error', error);

    }
})

module.exports = router;



[Back] SwToolsMapper.xml



<?xml version="1.0" encoding="UTF-8"?>
<!-- mybatis 버전을 명시한다. -->
<!DOCTYPE mapper PUBLIC 
  "-//mybatis.org//DTD Mapper 3.0//EN" 
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

  <mapper namespace='SwToolsMapper'>
    <select id='selectSwToolsList'>
        select 
        *
        from
        table02
    </select>

    <!-- 외부 변수를 사용할 땐 #을 사용한다. -->
    <select id='selectOne'>
        select 
        *
        from table02
        where id = #{id} 
    </select>

    <select id='selectSwtool'>
        select 
        *
        from react_swtool
    </select>

     <insert id="insertSwToolsInfo"> 
    INSERT INTO react_swtool
    (
        swt_code
        , swt_toolname
        , swt_function
        , swt_comments
        , swt_demo_site
        , swt_github_url
        <if test="is_LabelImg != null && is_LabelImg != ''">
          , swt_imagepath
        </if>
        <if test="is_MainImg != null && is_MainImg != ''">
          , swt_big_imgpath
        </if>
        <if test="is_MenualName != null && is_MenualName != ''">
          , swt_manual_path
        </if>
        , reg_date
        , reg_user
        , update_date
        , update_user
    )
    VALUES (
      CONCAT('USW', DATE_FORMAT(now(), '%Y%m%d%H%i%s'))
      , #{is_Swt_toolname}
      , #{is_Swt_function}      
      , #{is_Comments}
      , #{is_Swt_demo_site}
      , #{is_Giturl}
      <if test="is_LabelImg != null && is_LabelImg != ''">
        , #{is_LabelImg}
      </if>
      <if test="is_MainImg != null && is_MainImg != ''">
        , #{is_MainImg}
      </if>
      <if test="is_MenualName != null && is_MenualName != ''">
        , #{is_MenualName}
      </if>
      , DATE_FORMAT(now(), '%Y%m%d%H%i%s')
      , #{is_Email}
      , DATE_FORMAT(now(), '%Y%m%d%H%i%s')
      , #{is_Email}
    )
  </insert>  
  </mapper>



[Front] App.js



import axios from 'axios';
import './App.css';
import React, { Component } from 'react';
import { Route, Routes } from 'react-router-dom';
import SoftwareList from './SoftwareList';
import SoftwareView from './SoftwareView';

class App extends Component {

    render() {
        return (
            <div className='App'>
                <Routes>
                    {/* <Route path='/' element={<LoginForm />} />  */}
                    <Route path='/' element={<SoftwareList />} />
                    <Route path='/SoftwareView' element={<SoftwareView />} />
                </Routes>
            </div>
        );
    }
}

export default App;



[Front] App.css





@font-face {
  font-family: 'KOHIBaeumOTF';
  src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/[email protected]/KOHIBaeumOTF.woff') format('woff');
  font-weight: normal;
  font-style: normal;
}


@font-face {
  font-family: 'GmarketSansMedium';
  src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/[email protected]/GmarketSansMedium.woff') format('woff');
  font-weight: normal;
  font-style: normal;
}




/**** Common ************************************************/
* {max-height:1000000px;margin:0;padding:0;}
img, fieldset, button {border:none;}
hr, button img {display:none;}
input, select, button, textarea {font-family:'Noto Sans Korean', 'Noto Sans KR', 'Malgun Gothic','맑은 고딕','돋움',Dotum,'굴림',Gulim,Tahoma,Verdana,Geneva,sans-serif,Apple Gothic,AppleGothic;font-size:15px;font-weight:400;color:#505050;line-height:30px;letter-spacing:-0.03em;box-sizing:border-box;vertical-align:middle;}
input[type=submit] {cursor:pointer;appearance:none;-moz-appearance:none;-webkit-appearance:none;border-radius:0;-moz-border-radius:0;-webkit-border-radius:0;}
select::-ms-expand {display: none;}
ul, li {list-style:none;}
a {display:block;font-weight:400;color:#505050;text-decoration:none;vertical-align:top;}
a:hover, a:active, a:focus, a:visited {text-decoration:none;}
textarea {overflow:auto;border-radius:5px;border-color:#bdbdbd;}
table {width:100%;table-layout:fixed;border-collapse:collapse;border-spacing:0;text-align:center;}
table img {vertical-align:top;}
th, td {vertical-align:middle;word-wrap:break-word;word-break:break-all;}
form  {margin:0;padding:0;}
img {width:auto;max-width:100%;vertical-align:top;}
table img {width:auto;vertical-align:middle;}
legend  {position:absolute;left:0;top:0;width:0;height:0;overflow:hidden;visibility:hidden;font-size:0;line-height:0;} /* For Screen Reader */
caption {width:0;height:0;margin:0;padding:0;font-size:0;line-height:0;text-indent:-9999px;overflow:hidden;visibility:hidden;}
em {font-style:normal;}
p {display:block;}
.blind {position:absolute;left:-9999px;top:-9999px;}
.fl {float:left;}
.fr {float:right;}
.mc {margin:0 auto;}
.clear:after {content:"";display:block;clear:both;}
.fawb:before, .fawa:after {font-family:FontAwesome;text-rendering:auto;-webkit-font-smoothing:antialiased;}
body {font-family:'Noto Sans Korean','Noto Sans KR', 'Malgun Gothic','맑은 고딕','돋움',Dotum,'굴림',Gulim,Tahoma,Verdana,Geneva,sans-serif,Apple Gothic,AppleGothic;font-size:13px;font-weight:400;color:#505050;line-height:30px; letter-spacing:-0.03em; -webkit-text-size-adjust:none;font-smoothing:antialiased;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;}
body{min-width:1280px; }


*{
  list-style:none;
  font-family: 'GmarketSansMedium';
  /* font-family: 'Yeongdo-Rg'; */
 
}

html{
  background-color: #EFFFFD;
}




h2{
  font-size: 1rem;
  border-radius: 25px;
  color: white;
  background-color: #85F4FF;
  padding: 10px;
}

h2 span{
  color: #0043b0;
  
}

div{
  border-radius: 25px;
  background-color: #B8FFF9;
  /* color: rgb(255,255,255); */
  padding: 40px;
  text-align: center;
}
  
/* 
h1{
  font-family: 'KOHIBaeumOTF';
  border-radius: 30px;
  color: #064420;
  background-color: #E4EFE7;
  padding: 10px;
  text-align: center;
} */


button {
  background: none;
  border: 3px solid #fff;
  border-radius: 5px;
  color: #fff;
  /* display: block; */
  /* font-size: 1.6em; */
  font-weight: bold;
  margin: 1em 2em;
  padding: 1em 2em;
  /* position: relative; */
  text-transform: uppercase;
}

button a{
  text-decoration-line: none;
  color: #42C2FF;
}

button::before,
button::after {
  background: #fff;
  content: '';
  /* position: absolute; */
  z-index: -1;
}

button:hover {
  color: #0043b0;
}



/*테이블*/
.table_ty1{border-top: 2px solid #42C2FF; table-layout: fixed; position: relative;}

.table_ty1 tr th{border: 1px solid #ddd; background: #fff; font-size: 17px; color: #303030;	font-weight: 500; height: 59px; line-height: 59px; box-sizing: border-box;}
.table_ty1 tr th a{font-size: 17px; color: #303030;	font-weight: 500;}
/* .table_ty1 tr th a:after{content:''; display: inline-block; width: 10px; height: 6px;  background: url(./img/sub/ta_b.png) center no-repeat;vertical-align: top; margin-top: 27px; margin-left: 8px;} */
/* .table_ty1 tr th.on a:after{content:''; display: inline-block; width: 10px; height: 6px;  background: url(./img/sub/ta_t.png) center no-repeat;vertical-align: top; margin-top: 27px; margin-left: 8px;} */

.table_ty1 tr th:after{position: absolute; content:''; width: 100%; height: 2px; background: #42C2FF; left: 0; top: 0;}

.table_ty2 {border: 1px solid #ddd; margin-top: 9px;}
.table_ty2 tr{background: #fff; }
.table_ty2 tr:nth-child(2n+2){background: #f4f7f9;}
.table_ty2 tr td{border-right: 1px solid #ddd; padding: 16px 0px 14px; position: relative; font-size: 16px; color: #606060;}
/* .table_ty2 tr td:after{position: absolute; content:''; width: 100%; height: 1px; background: url(./img/sub/table_ddot.png) left center; left: 0; bottom: 0; z-index: 10;} */
.table_ty2 tr:last-child td:after{display: none;}
.table_ty2 tr td:last-child{border-right: 0;}
.table_ty2 tr td h4{font-size: 16px; line-height: 16px; color: #606060; font-weight: 400; margin-bottom: 7px;}
.table_ty2 tr td h4 span{display: inline-block; width: 35px; height: 15px; background: #e90000; color: #fff; font-size: 12px; color: #fff; line-height: 15px; font-weight: 400;; text-align: center; vertical-align: middle; margin-top: -2px; margin-left: 9px;}
.table_ty2 tr td p{font-size: 15px; line-height: 15px; color: #a1a1a1; font-weight: 400;; text-overflow:ellipsis;white-space:nowrap;word-wrap:normal;overflow:hidden;}
.table_ty2 tr td:first-child{text-align: left; }
.table_ty3{text-align: left; background: #fff; position: relative; border-top: 2px solid #42C2FF;  }
.table_ty3 tr th,
.table_ty3 tr td{font-size: 16px; color: #303030; line-height: 20px; font-weight: 400; padding: 16px 0 21px; vertical-align: top; letter-spacing: -0.03em;}
.table_ty3 tr{position: relative;}
.table_ty3 tr th{width: 174px; position: relative; box-sizing: border-box; padding-left: 62px; text-align: left;}

.table_ty06 tr th{width: 120px;}

.table_ty3 tr th:before{position: absolute; content:''; width: 8px; height: 3px; left: 39px; top: 27px; background: #22223e;}
/* .table_ty3 tr th:after{position: absolute; content:''; width: 631px; height: 1px; background: url(./img/sub/table_ddot.png) center; left: 0;  bottom: 0;} */
.table_ty3 tr th.last_th:after{display: none;}
.table_ty3 tr td{color: #606060; font-weight: 400; 	}
.table_ty3 tr td span{color: #e90000;}
.table_ty4{position: relative; }
.table_ty4 tr:first-child td:before,
.table_ty7 tr:first-child td:before{position: absolute; content:''; width: 3px; height: 100%; right: 0 ; top: 0; background: #ededed; }
.table_ty4 tr:first-child td:after,
.table_ty7 tr:first-child td:after{position: absolute; content:''; width: 3px; height: 50%; right: 0 ; top: 0; background: #bcbcbc; }
.table_ty4 tr th, .table_ty4 tr td{height: 57px; line-height: 57px; padding: 0; vertical-align: middle;}
.table_ty4 tr td a{vertical-align: top !important; margin-top: 17px;}

.table_ty4 tr th{width: 120px; text-align: center; padding: 0;}
.table_ty4 tr th:before{display: none;}
.table_ty5 tr th, .table_ty5 tr td{height: 57px; line-height: 57px;}
.table_ty5 tr td a{vertical-align: top !important; margin-top: 17px;}

.table_ty6 tr th, .table_ty6 tr td{height: 57px; line-height: 57px; padding: 0;}
.table_ty6 tr th{padding-left: 62px;}
.table_ty6 tr td{padding: 0;}

.table_ty8{border-top: 2px solid #42C2FF; font-size: 16px; }
.table_ty8 tr th{background: #f4f7f9; line-height: 44px; color: #303030; font-weight: 400; position: relative;}
.table_ty8 tr td{font-size: 16px; line-height: 20px; color: #606060; padding: 12px 0; position: relative; text-align: center; background: #fff;}
/* .table_ty8 tr td:after{position: absolute; content:''; width: 631px; height: 1px; background: url(./img/sub/table_ddot.png) center; left: 0;  bottom: 0;} */




/*공통*/

.ct1{max-width:1280px; margin: 0 auto;}
.ct2{max-width:724px; margin: 0 auto; }
.af:after{display: block; clear: both; content:'';}
.mt20{margin-top: 20px;}
.mb20{margin-bottom: 20px;}

/*마진*/
.mt31{margin-top: 31px;}
.mt40{margin-top: 40px;}
.mb100{margin-bottom: 100px;}

/*파일박스*/
/* .fileBox .btn_file {box-sizing: border-box;display:inline-block;border:1px solid #ddd; font-size: 15px; color: #a1a1a1; background: url(../img/sub/file_bg.png) left center; border-radius: 5px;width:82px;height:30px; line-height:28px;text-align:center;vertical-align:middle; cursor: pointer;} */
.fileBox .fileName {display:inline-block;width:283px; height:30px; line-height:30px; font-size: 15px; color: #303030; vertical-align:middle; border: 0; border-bottom: 1px solid #ddd; margin-left: 18px;}
.fileBox input[type="file"] {position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}
.fileBox input::placeholder {font-size: 15px; color: #cdcdcd;}
.fileBox input:-ms-input-placeholder {color:#cdcdcd;} /* M$ */

/*sub 공통*/
/* .sub_wrap{background: #f8f8f8; padding: 56px 0;} */
.s_tit1{font-size: 30px; line-height: 38px; color: #333; font-weight: 500; letter-spacing: -0.03em; margin-bottom: 27px; margin-left: -3px;}
.input_ty1{font-size: 15px; color: #333; height: 45px; line-height: 43px; background: #fff; border: 1px solid #dedede; outline:none; text-indent: 20px; box-sizing: border-box; padding-right: 15px;}
.input_ty1::placeholder{color: #cdcdcd;}



[Front] SoftwareList.js



import React, { Component } from 'react';
import {Link} from 'react-router-dom';
import axios from "axios";

class SoftwareList extends Component {
    constructor(props) {
        super(props);

        this.state = {
            responseSwtoolList: [],
            append_SwtoolList: [],
        }
    }

    componentDidMount() {
        this.callSwToolListApi()
    }

    callSwToolListApi = async () => {
        axios.post('/api/Swtool?type=list', {
        })
        .then( response => {
            try {
                this.setState({ responseSwtoolList: response });
                this.setState({ append_SwtoolList: this.SwToolListAppend() });
            } catch (error) {
                console.log('작업중 오류가 발생하였습니다.1', error);
            }
        })
        .catch( error => {alert(error); return false;} );
    }

    SwToolListAppend = () => {
        
        let result = []
        var SwToolList = this.state.responseSwtoolList.data
        
        for(let i=0; i<SwToolList.length; i++){
            var data = SwToolList[i]

            var date = data.reg_date
            var year = date.substr(0,4)
            var month = date.substr(4,2)
            var day = date.substr(6,2)
            var reg_date = year +'.'+month+'.'+day

            result.push(
                <tr className="hidden_type" key={i}>
                    <td>{data.swt_toolname}</td>
                    <td>{data.swt_function}</td>
                    <td>{reg_date}</td>
                    <td>
                        <Link to={'/SoftwareView/'+data.swt_code} 
                        className="bt_c1 bt_c2 w50_b">수정</Link>
                        <a href="#n" className="bt_c1 w50_b" >삭제</a>
                    </td>
                </tr>
            )
        }

        console.log(result);
        
        return result
    }

    render () {
        console.log('render()');
        
        return (
            <>
            <h3>Software</h3>
            <section className="sub_wrap" >
                <article className="s_cnt mp_pro_li ct1 mp_pro_li_admin">
                    <div className="li_top">
                        <h2 className="s_tit1">Software Tools 목록</h2>
                        <div className="li_top_sch af">
                        <button type='submit'>
                            <Link to={'/SoftwareView'} className="sch_bt2 wi_au">Tool 등록</Link>
                        </button>
                        </div>
                    </div>

                    <div className="list_cont list_cont_admin">
                        <table className="table_ty1 ad_tlist">
                            <tbody>
                            <tr>
                                <th>툴 이름</th>
                                <th>기능</th>
                                <th>등록일</th>
                                <th>기능</th>
                            </tr>
                            </tbody>
                        </table>   
                        <table className="table_ty2 ad_tlist">
                            <tbody>
                            {this.state.append_SwtoolList}
                            </tbody>
                        </table>
                    </div>
                </article>
            </section>
            </>

        );
    }
}

export default SoftwareList;



[Front] SortwareView.js





import React, { Component, useEffect } from "react";
import { Link, useNavigate } from "react-router-dom";
import $ from "jquery";
import Swal from "sweetalert2";
import axios from "axios";

function SoftwareView ({match}){
  const navigate = useNavigate();
  

  const sweetalertSucc = (title, showConfirmButton) => {
    Swal.fire({
      position: "bottom-end",
      icon: "success",
      title: title,
      showConfirmButton: showConfirmButton,
      timer: 1000,
    });
  };

  //componentDidMount
  // useEffect(()=>{
  //   console.log('useEffect');
    
  //   if(this.state.before_swtcode == 'register'){
  //     $('.modifyclass').hide()
  // }else{
  //     callSwToolInfoApi()
  //     $('.saveclass').hide()
  // }
  // })

  const callSwToolInfoApi = async () => {
    axios.post('/api/Swtool?type=list', {
        is_Swtcode: this.state.before_swtcode,
    })
    .then( response => {
        try {
            var data = response.data[0]
            $('#is_Swt_toolname').val(data.swt_toolname)
            $('#is_Swt_demo_site').val(data.swt_demo_site)
            $('#is_Giturl').val(data.swt_github_url)
            $('#is_Comments').val(data.swt_comments)
            $('#is_Swt_function').val(data.swt_function)
        } catch (error) {
            alert('작업중 오류가 발생하였습니다')
        }
    })
    .catch( error => {alert(error); return false;} );
}


  const submitClick = async (type, e) => {

      var jsonstr = $("form[name='frm']").serialize();
      jsonstr = decodeURIComponent(jsonstr);
      var Json_form = JSON.stringify(jsonstr).replace(/\"/gi, "");
      Json_form =
        '{"' + Json_form.replace(/\&/g, '","').replace(/=/gi, '":"') + '"}';

      try {
        console.log("response22222222222222222");
        const response2 = await axios.post("/api/Swtool?type=" + type, jsonstr);

        if (response2.data === "succ") {
          if (type === "save") {
            sweetalertSucc("Software Tools 등록이 완료되었습니다.", false);
          }
          setTimeout( ()=> {
            navigate('/');
            // this.props.navigate('/SoftwareList');
           
          }, 1500);
        } else {
          
          alert("작업중 오류가 발생하였습니다.");
        }
      } catch (error) {
        console.log(12341234);
        alert("작업중 오류가 발생하였습니다.");
      }
    
  };


    return (
      <section className="sub_wrap">
        <article className="s_cnt mp_pro_li ct1">
          <div className="li_top">
            <h2 className="s_tit1">Software Tools 등록/수정</h2>
          </div>
          <div className="bo_w re1_wrap re1_wrap_writer">
            <form name="frm" id="frm" action="" method="post">
              <input id="is_Swtcode" type="hidden" name="is_Swtcode" />
              <input
                id="is_Email"
                type="hidden"
                name="is_Email"
                value="guest"
              />
              <article className="res_w">
                <p className="ment" style={{ textAlign: "right" }}>
                  <span className="red">(*)</span>표시는 필수입력사항 입니다.
                </p>
                <div className="tb_outline">
                  <table className="table_ty1">
                    <tbody>
                      <tr>
                        <th>
                          <label htmlFor="is_Swt_toolname">
                            툴 이름<span className="red">(*)</span>
                          </label>
                        </th>
                        <td>
                          <input
                            type="text"
                            name="is_Swt_toolname"
                            id="is_Swt_toolname"
                            className=""
                          />
                        </td>
                      </tr>
                      <tr>
                        <th>
                          <label htmlFor="is_Swt_demo_site">
                            데모 URL<span className="red">(*)</span>
                          </label>
                        </th>
                        <td>
                          <input
                            type="text"
                            name="is_Swt_demo_site"
                            id="is_Swt_demo_site"
                            className=""
                          />
                        </td>
                      </tr>
                      <tr>
                        <th>
                          <label htmlFor="is_Giturl">
                            Github URL<span className="red">(*)</span>
                          </label>
                        </th>
                        <td>
                          <input
                            type="text"
                            name="is_Giturl"
                            id="is_Giturl"
                            className=""
                          />
                        </td>
                      </tr>
                      <tr>
                        <th>
                          <label htmlFor="is_Comments">
                            설명<span className="red">(*)</span>
                          </label>
                        </th>
                        <td>
                          <textarea
                            name="is_Comments"
                            id="is_Comments"
                            rows=""
                            cols=""
                          ></textarea>
                        </td>
                      </tr>
                      <tr className="div_tb_tr fileb">
                        <th>메뉴얼 파일 #1</th>
                        <td className="fileBox fileBox_w1">
                          <label htmlFor="uploadBtn1" className="btn_file">
                            파일선택
                          </label>
                          <input
                            type="text"
                            id="manualfile"
                            className="fileName fileName1"
                            readOnly="readonly"
                            placeholder="선택된 파일 없음"
                          />
                          <input
                            type="file"
                            id="uploadBtn1"
                            className="uploadBtn uploadBtn1"
                            onChange={(e) => this.handleFileInput("manual", e)}
                          />
                          <div id="upload_menual"></div>
                        </td>
                      </tr>
                      <tr>
                        <th>메인 이미지</th>
                        <td className="fileBox fileBox1">
                          <label htmlFor="imageSelect" className="btn_file">
                            파일선택
                          </label>
                          <input
                            type="text"
                            id="imagefile"
                            className="fileName fileName1"
                            readOnly="readonly"
                            placeholder="선택된 파일 없음"
                          />
                          <input
                            type="file"
                            id="imageSelect"
                            className="uploadBtn uploadBtn1"
                            onChange={(e) => this.handleFileInput("file", e)}
                          />
                          <div id="upload_img"></div>
                        </td>
                      </tr>
                      <tr>
                        <th>라벨 이미지</th>
                        <td className="fileBox fileBox2">
                          <label htmlFor="imageSelect2" className="btn_file">
                            파일선택
                          </label>
                          <input
                            type="text"
                            id="imagefile2"
                            className="fileName fileName1"
                            readOnly="readonly"
                            placeholder="선택된 파일 없음"
                          />
                          <input
                            type="file"
                            id="imageSelect2"
                            className="uploadBtn uploadBtn1"
                            onChange={(e) => this.handleFileInput("file2", e)}
                          />
                          <div id="upload_img2"></div>
                        </td>
                      </tr>
                      <tr>
                        <th>
                          <label htmlFor="is_Swt_function">
                            상세 기능<span className="red">(*)</span>
                          </label>
                        </th>
                        <td>
                          <textarea
                            name="is_Swt_function"
                            id="is_Swt_function"
                            rows=""
                            cols=""
                          ></textarea>
                        </td>
                      </tr>
                    </tbody>
                  </table>
                  <div
                    className="btn_confirm mt20"
                    style={{ marginBottom: "44px" }}
                  >
                    <button type='submit'>
                      <Link
                        to={"/SoftwareList"}
                        className="bt_ty bt_ty1 cancel_ty1"
                      >
                        취소
                      </Link>
                    </button>
                    <button type='submit'>
                      <a
                        href="#"
                        className="bt_ty bt_ty2 submit_ty1 saveclass"
                        onClick={(e) => {
                          submitClick("save", e);
                          e.preventDefault();
                        }}
                      > 저장
                      </a>
                    </button>
                     
                    <button type='submit'>
                      <a href="javascript:" className="bt_ty bt_ty2 submit_ty1 modifyclass" 
                                        onClick={(e) => this.submitClick('modify', e)}>수정</a>
                    </button>
                  </div>
                </div>
              </article>
            </form>
          </div>
        </article>
      </section>
    );
  }

export default SoftwareView;



package.json



{
  "name": "client",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^5.16.4",
    "@testing-library/react": "^13.0.1",
    "@testing-library/user-event": "^13.5.0",
    "axios": "^0.26.1",
    "jquery": "^3.6.0",
    "react": "^17.0.1",
    "react-dom": "^17.0.1",
    "react-router": "^6.3.0",
    "react-router-dom": "^6.3.0",
    "react-scripts": "^2.1.3",
    "sweetalert2": "^11.4.8",
    "web-vitals": "^2.1.4",
    "yarn": "^1.22.18"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "proxy":"http://127.0.0.1:5000"
}




[실습] update 기능 추가하기



[Back] app.js



var express = require('express');
const { is } = require('express/lib/request');
const res = require('express/lib/response');
var app = express();

app.use(express.json());
app.use(express.urlencoded({ extended: false }));

var router = express.Router();
const { route } = require('./routes/tiger');

app.use('/api/Swtool',
    router.post('/', function (req, res, next) {
        console.log(req.query.type);
        
        if (req.query.type == 'list') {

            req.body.mapper = 'SwToolsMapper';
            req.body.crud = 'select';
            req.body.mapper_id = 'selectSwtool';

            router.use('/', require('./dbconnect_Module'));
            next('route');

        } else if (req.query.type == 'save') {

            req.body.mapper = 'SwToolsMapper';
            req.body.crud = 'insert';
            req.body.mapper_id = 'insertSwToolsInfo';

            router.use('/', require('./dbconnect_Module'));
            next('route');

        } else if (req.query.type == 'modify') {

            req.body.mapper = 'SwToolsMapper';
            req.body.crud = 'update';
            req.body.mapper_id = 'updateSwToolsInfo';

            router.use('/', require('./dbconnect_Module'));
            next('route');

        } 

    }),

);

var port = process.env.PORT || '5000';
app.listen(port, () => { console.log('listen'); });


module.exports = app;



[Back] dbconnect_Module.js



var express = require('express');
var router = express.Router();

const mysql = require('mysql');
// const con = mysql.createConnection( {
//     host: "database-1.cziyl6rwn9rl.ap-northeast-2.rds.amazonaws.com",
//     port: 3306,
//     database: 'ahhyun',
//     user: 'admin',
//     password: '12345678',

// } );

// 풀 만듬
const pool = mysql.createPool({
    // 풀 환경설정
    connectionLimit: 66, // 커넥션 풀에 저장할 수 있는 최대 커넥션 개수 (최대 연결 수)
    waitForConnections: true, // 풀이 꽉 찼다면, 여유가 생길 때까지 새 연결 대기 여부

    // 연결할 디비 설정
    host: "database-1.cziyl6rwn9rl.ap-northeast-2.rds.amazonaws.com",
    port: "3306",
    database: 'ahhyun',
    user: 'admin',
    password: '12345678',
})

router.post('/', function (req, res, next) {
    try {

        let param = req.body;

        const myBatisMapper = require('mybatis-mapper');
        myBatisMapper.createMapper(['SwToolsMapper.xml']);

        let query = myBatisMapper.getStatement(
            param.mapper, // 'SwToolsMapper', 
            param.mapper_id, // 'selectSwtool', 
            param,
            { language: 'sql', indent: '  ' },
        );

        pool.getConnection(function (err, con) {
            con.query(
                query,
                (error, rows, fields) => {
                    if (error) throw error;

                    if (req.body.crud == "select") {
                        res.send(rows)
                    } else {
                        res.send("succ");
                    }

                }
            );

            // 쿼리2

            // 쿼리3

            // 커넥션 풀에서 연결 제거
            con.release();
        })

    } catch (error) {
        console.log('error', error);

    }
})

module.exports = router;



[Back] SwToolsMapper.xml



<?xml version="1.0" encoding="UTF-8"?>
<!-- mybatis 버전을 명시한다. -->
<!DOCTYPE mapper PUBLIC 
  "-//mybatis.org//DTD Mapper 3.0//EN" 
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

  <mapper namespace='SwToolsMapper'>
    <select id="selectSwToolsList">
      SELECT 
        swt_code
        , swt_toolname
        , swt_function
        , swt_imagepath
        , swt_big_imgpath
        , swt_comments
        , swt_demo_site
        , swt_manual_path
        , swt_github_url
        , reg_date
      FROM react.react_swtool
      <if test="is_Swtcode != null && is_Swtcode != ''">
        WHERE swt_code = #{is_Swtcode}
      </if>
      ORDER BY update_date DESC
  </select>


    <!-- 외부 변수를 사용할 땐 #을 사용한다. -->
    <select id='selectOne'>
        select 
        *
        from table02
        where id = #{id} 
    </select>

    <select id='selectSwtool'>
        select 
        *
        from react_swtool
    </select>

     <insert id="insertSwToolsInfo"> 
    INSERT INTO react_swtool
    (
        swt_code
        , swt_toolname
        , swt_function
        , swt_comments
        , swt_demo_site
        , swt_github_url
        <if test="is_LabelImg != null && is_LabelImg != ''">
          , swt_imagepath
        </if>
        <if test="is_MainImg != null && is_MainImg != ''">
          , swt_big_imgpath
        </if>
        <if test="is_MenualName != null && is_MenualName != ''">
          , swt_manual_path
        </if>
        , reg_date
        , reg_user
        , update_date
        , update_user
    )
    VALUES (
      CONCAT('USW', DATE_FORMAT(now(), '%Y%m%d%H%i%s'))
      , #{is_Swt_toolname}
      , #{is_Swt_function}      
      , #{is_Comments}
      , #{is_Swt_demo_site}
      , #{is_Giturl}
      <if test="is_LabelImg != null && is_LabelImg != ''">
        , #{is_LabelImg}
      </if>
      <if test="is_MainImg != null && is_MainImg != ''">
        , #{is_MainImg}
      </if>
      <if test="is_MenualName != null && is_MenualName != ''">
        , #{is_MenualName}
      </if>
      , DATE_FORMAT(now(), '%Y%m%d%H%i%s')
      , #{is_Email}
      , DATE_FORMAT(now(), '%Y%m%d%H%i%s')
      , #{is_Email}
    )
  </insert>  

  <update id="updateSwToolsInfo"> 
    UPDATE react_swtool
    SET
        swt_toolname = #{is_Swt_toolname}
        , swt_function = #{is_Swt_function}
        , swt_comments = #{is_Comments}
        , swt_demo_site = #{is_Swt_demo_site}
        , swt_github_url = #{is_Giturl}
        <if test="is_LabelImg != null && is_LabelImg != ''">
          , swt_imagepath = #{is_LabelImg}
        </if>
        <if test="is_MainImg != null && is_MainImg != ''">
          , swt_big_imgpath = #{is_MainImg}
        </if>
        <if test="is_MenualName != null && is_MenualName != ''">
          , swt_manual_path = #{is_MenualName}
        </if>
        , update_date = DATE_FORMAT(now(), '%Y%m%d%H%i%s')
        , update_user = #{is_Email}
    WHERE swt_code = #{is_beforeSwtcode}
  </update>

  </mapper>



[Front] App.css





@font-face {
  font-family: 'KOHIBaeumOTF';
  src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/[email protected]/KOHIBaeumOTF.woff') format('woff');
  font-weight: normal;
  font-style: normal;
}


@font-face {
  font-family: 'GmarketSansMedium';
  src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/[email protected]/GmarketSansMedium.woff') format('woff');
  font-weight: normal;
  font-style: normal;
}




/**** Common ************************************************/
* {max-height:1000000px;margin:0;padding:0;}
img, fieldset, button {border:none;}
hr, button img {display:none;}
input, select, button, textarea {font-family:'Noto Sans Korean', 'Noto Sans KR', 'Malgun Gothic','맑은 고딕','돋움',Dotum,'굴림',Gulim,Tahoma,Verdana,Geneva,sans-serif,Apple Gothic,AppleGothic;font-size:15px;font-weight:400;color:#505050;line-height:30px;letter-spacing:-0.03em;box-sizing:border-box;vertical-align:middle;}
input[type=submit] {cursor:pointer;appearance:none;-moz-appearance:none;-webkit-appearance:none;border-radius:0;-moz-border-radius:0;-webkit-border-radius:0;}
select::-ms-expand {display: none;}
ul, li {list-style:none;}
a {display:block;font-weight:400;color:#505050;text-decoration:none;vertical-align:top;}
a:hover, a:active, a:focus, a:visited {text-decoration:none;}
textarea {overflow:auto;border-radius:5px;border-color:#bdbdbd;}
table {width:100%;table-layout:fixed;border-collapse:collapse;border-spacing:0;text-align:center;}
table img {vertical-align:top;}
th, td {vertical-align:middle;word-wrap:break-word;word-break:break-all;}
form  {margin:0;padding:0;}
img {width:auto;max-width:100%;vertical-align:top;}
table img {width:auto;vertical-align:middle;}
legend  {position:absolute;left:0;top:0;width:0;height:0;overflow:hidden;visibility:hidden;font-size:0;line-height:0;} /* For Screen Reader */
caption {width:0;height:0;margin:0;padding:0;font-size:0;line-height:0;text-indent:-9999px;overflow:hidden;visibility:hidden;}
em {font-style:normal;}
p {display:block;}
.blind {position:absolute;left:-9999px;top:-9999px;}
.fl {float:left;}
.fr {float:right;}
.mc {margin:0 auto;}
.clear:after {content:"";display:block;clear:both;}
.fawb:before, .fawa:after {font-family:FontAwesome;text-rendering:auto;-webkit-font-smoothing:antialiased;}
body {font-family:'Noto Sans Korean','Noto Sans KR', 'Malgun Gothic','맑은 고딕','돋움',Dotum,'굴림',Gulim,Tahoma,Verdana,Geneva,sans-serif,Apple Gothic,AppleGothic;font-size:13px;font-weight:400;color:#505050;line-height:30px; letter-spacing:-0.03em; -webkit-text-size-adjust:none;font-smoothing:antialiased;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;}
body{min-width:1280px; }


*{
  list-style:none;
  font-family: 'GmarketSansMedium';
  /* font-family: 'Yeongdo-Rg'; */
 
}

html{
  background-color: #EFFFFD;
}




h2{
  font-size: 1rem;
  border-radius: 25px;
  color: white;
  background-color: #85F4FF;
  padding: 10px;
}

h2 span{
  color: #0043b0;
  
}

div{
  border-radius: 25px;
  background-color: #B8FFF9;
  /* color: rgb(255,255,255); */
  padding: 40px;
  text-align: center;
}
  
/* 
h1{
  font-family: 'KOHIBaeumOTF';
  border-radius: 30px;
  color: #064420;
  background-color: #E4EFE7;
  padding: 10px;
  text-align: center;
} */


button {
  background: none;
  border: 3px solid #fff;
  border-radius: 5px;
  color: #fff;
  /* display: block; */
  /* font-size: 1.6em; */
  font-weight: bold;
  margin: 1em 2em;
  padding: 1em 2em;
  /* position: relative; */
  text-transform: uppercase;
}

button a{
  text-decoration-line: none;
  color: #42C2FF;
}

button::before,
button::after {
  background: #fff;
  content: '';
  /* position: absolute; */
  z-index: -1;
}

button:hover {
  color: #0043b0;
}



/*테이블*/
.table_ty1{border-top: 2px solid #42C2FF; table-layout: fixed; position: relative;}

.table_ty1 tr th{border: 1px solid #ddd; background: #fff; font-size: 17px; color: #303030;	font-weight: 500; height: 59px; line-height: 59px; box-sizing: border-box;}
.table_ty1 tr th a{font-size: 17px; color: #303030;	font-weight: 500;}
/* .table_ty1 tr th a:after{content:''; display: inline-block; width: 10px; height: 6px;  background: url(./img/sub/ta_b.png) center no-repeat;vertical-align: top; margin-top: 27px; margin-left: 8px;} */
/* .table_ty1 tr th.on a:after{content:''; display: inline-block; width: 10px; height: 6px;  background: url(./img/sub/ta_t.png) center no-repeat;vertical-align: top; margin-top: 27px; margin-left: 8px;} */

.table_ty1 tr th:after{position: absolute; content:''; width: 100%; height: 2px; background: #42C2FF; left: 0; top: 0;}

.table_ty2 {border: 1px solid #ddd; margin-top: 9px;}
.table_ty2 tr{background: #fff; }
.table_ty2 tr:nth-child(2n+2){background: #f4f7f9;}
.table_ty2 tr td{border-right: 1px solid #ddd; padding: 16px 0px 14px; position: relative; font-size: 16px; color: #606060;}
/* .table_ty2 tr td:after{position: absolute; content:''; width: 100%; height: 1px; background: url(./img/sub/table_ddot.png) left center; left: 0; bottom: 0; z-index: 10;} */
.table_ty2 tr:last-child td:after{display: none;}
.table_ty2 tr td:last-child{border-right: 0;}
.table_ty2 tr td h4{font-size: 16px; line-height: 16px; color: #606060; font-weight: 400; margin-bottom: 7px;}
.table_ty2 tr td h4 span{display: inline-block; width: 35px; height: 15px; background: #e90000; color: #fff; font-size: 12px; color: #fff; line-height: 15px; font-weight: 400;; text-align: center; vertical-align: middle; margin-top: -2px; margin-left: 9px;}
.table_ty2 tr td p{font-size: 15px; line-height: 15px; color: #a1a1a1; font-weight: 400;; text-overflow:ellipsis;white-space:nowrap;word-wrap:normal;overflow:hidden;}
.table_ty2 tr td:first-child{text-align: left; }
.table_ty3{text-align: left; background: #fff; position: relative; border-top: 2px solid #42C2FF;  }
.table_ty3 tr th,
.table_ty3 tr td{font-size: 16px; color: #303030; line-height: 20px; font-weight: 400; padding: 16px 0 21px; vertical-align: top; letter-spacing: -0.03em;}
.table_ty3 tr{position: relative;}
.table_ty3 tr th{width: 174px; position: relative; box-sizing: border-box; padding-left: 62px; text-align: left;}

.table_ty06 tr th{width: 120px;}

.table_ty3 tr th:before{position: absolute; content:''; width: 8px; height: 3px; left: 39px; top: 27px; background: #22223e;}
/* .table_ty3 tr th:after{position: absolute; content:''; width: 631px; height: 1px; background: url(./img/sub/table_ddot.png) center; left: 0;  bottom: 0;} */
.table_ty3 tr th.last_th:after{display: none;}
.table_ty3 tr td{color: #606060; font-weight: 400; 	}
.table_ty3 tr td span{color: #e90000;}
.table_ty4{position: relative; }
.table_ty4 tr:first-child td:before,
.table_ty7 tr:first-child td:before{position: absolute; content:''; width: 3px; height: 100%; right: 0 ; top: 0; background: #ededed; }
.table_ty4 tr:first-child td:after,
.table_ty7 tr:first-child td:after{position: absolute; content:''; width: 3px; height: 50%; right: 0 ; top: 0; background: #bcbcbc; }
.table_ty4 tr th, .table_ty4 tr td{height: 57px; line-height: 57px; padding: 0; vertical-align: middle;}
.table_ty4 tr td a{vertical-align: top !important; margin-top: 17px;}

.table_ty4 tr th{width: 120px; text-align: center; padding: 0;}
.table_ty4 tr th:before{display: none;}
.table_ty5 tr th, .table_ty5 tr td{height: 57px; line-height: 57px;}
.table_ty5 tr td a{vertical-align: top !important; margin-top: 17px;}

.table_ty6 tr th, .table_ty6 tr td{height: 57px; line-height: 57px; padding: 0;}
.table_ty6 tr th{padding-left: 62px;}
.table_ty6 tr td{padding: 0;}

.table_ty8{border-top: 2px solid #42C2FF; font-size: 16px; }
.table_ty8 tr th{background: #f4f7f9; line-height: 44px; color: #303030; font-weight: 400; position: relative;}
.table_ty8 tr td{font-size: 16px; line-height: 20px; color: #606060; padding: 12px 0; position: relative; text-align: center; background: #fff;}
/* .table_ty8 tr td:after{position: absolute; content:''; width: 631px; height: 1px; background: url(./img/sub/table_ddot.png) center; left: 0;  bottom: 0;} */




/*공통*/

.ct1{max-width:1280px; margin: 0 auto;}
.ct2{max-width:724px; margin: 0 auto; }
.af:after{display: block; clear: both; content:'';}
.mt20{margin-top: 20px;}
.mb20{margin-bottom: 20px;}

/*마진*/
.mt31{margin-top: 31px;}
.mt40{margin-top: 40px;}
.mb100{margin-bottom: 100px;}

/*파일박스*/
/* .fileBox .btn_file {box-sizing: border-box;display:inline-block;border:1px solid #ddd; font-size: 15px; color: #a1a1a1; background: url(../img/sub/file_bg.png) left center; border-radius: 5px;width:82px;height:30px; line-height:28px;text-align:center;vertical-align:middle; cursor: pointer;} */
.fileBox .fileName {display:inline-block;width:283px; height:30px; line-height:30px; font-size: 15px; color: #303030; vertical-align:middle; border: 0; border-bottom: 1px solid #ddd; margin-left: 18px;}
.fileBox input[type="file"] {position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}
.fileBox input::placeholder {font-size: 15px; color: #cdcdcd;}
.fileBox input:-ms-input-placeholder {color:#cdcdcd;} /* M$ */

/*sub 공통*/
/* .sub_wrap{background: #f8f8f8; padding: 56px 0;} */
.s_tit1{font-size: 30px; line-height: 38px; color: #333; font-weight: 500; letter-spacing: -0.03em; margin-bottom: 27px; margin-left: -3px;}
.input_ty1{font-size: 15px; color: #333; height: 45px; line-height: 43px; background: #fff; border: 1px solid #dedede; outline:none; text-indent: 20px; box-sizing: border-box; padding-right: 15px;}
.input_ty1::placeholder{color: #cdcdcd;}



[Front] SoftwareList.js



import React, { Component } from 'react';
import {Link} from 'react-router-dom';
import axios from "axios";
import './App.css'

class SoftwareList extends Component {
    constructor(props) {
        super(props);

        this.state = {
            responseSwtoolList: [],
            append_SwtoolList: [],
        }
    }

    componentDidMount() {
        this.callSwToolListApi()
    }

    callSwToolListApi = async () => {
        axios.post('/api/Swtool?type=list', {
        })
        .then( response => {
            try {
                this.setState({ responseSwtoolList: response });
                this.setState({ append_SwtoolList: this.SwToolListAppend() });
            } catch (error) {
                console.log('작업중 오류가 발생하였습니다.1', error);
            }
        })
        .catch( error => {alert(error); return false;} );
    }

    SwToolListAppend = () => {
        
        let result = []
        var SwToolList = this.state.responseSwtoolList.data
        
        for(let i=0; i<SwToolList.length; i++){
            var data = SwToolList[i]

            var date = data.reg_date
            var year = date.substr(0,4)
            var month = date.substr(4,2)
            var day = date.substr(6,2)
            var reg_date = year +'.'+month+'.'+day

            result.push(
                <tr className="hidden_type" key={i}>
                    <td>{data.swt_toolname}</td>
                    <td>{data.swt_function}</td>
                    <td>{reg_date}</td>
                    <td>
                        <Link to={'/SoftwareView/'+data.swt_code} 
                        className="bt_c1 bt_c2 w50_b">수정</Link>
                        <a href="#n" className="bt_c1 w50_b" >삭제</a>
                    </td>
                </tr>
            )
        }

        console.log(result);
        
        return result
    }

    render () {
        console.log('render()');
        
        return (
            <>
            <h3>Software</h3>
            <section className="sub_wrap" >
                <article className="s_cnt mp_pro_li ct1 mp_pro_li_admin">
                    <div className="li_top">
                        <h2 className="s_tit1">Software Tools 목록</h2>
                        <div className="li_top_sch af">
                        <button type='submit'>
                            <Link to={'/SoftwareView/register'} className="sch_bt2 wi_au">Tool 등록</Link>
                        </button>
                        </div>
                    </div>

                    <div className="list_cont list_cont_admin">
                        <table className="table_ty1 ad_tlist">
                            <tbody>
                            <tr>
                                <th>툴 이름</th>
                                <th>기능</th>
                                <th>등록일</th>
                                <th>기능</th>
                            </tr>
                            </tbody>
                        </table>   
                        <table className="table_ty2 ad_tlist">
                            <tbody>
                            {this.state.append_SwtoolList}
                            </tbody>
                        </table>
                    </div>
                </article>
            </section>
            </>

        );
    }
}

export default SoftwareList;



[Front] SortwareView.js



import React, { Component, useEffect } from "react";
import { Link, useNavigate, useParams } from "react-router-dom";
import $ from "jquery";
import Swal from "sweetalert2";
import axios from "axios";

const SoftwareView = ()=>{
  const navigate = useNavigate();
  const match = useParams();

  const sweetalertSucc = (title, showConfirmButton) => {
    Swal.fire({
      position: "bottom-end",
      icon: "success",
      title: title,
      showConfirmButton: showConfirmButton,
      timer: 1000,
    });
  };

  //componentDidMount
  useEffect(()=>{
    console.log('useEffect', match.swtcode);
    
    
    if(match.swtcode == 'register'){
      $('.modifyclass').hide()
  }else{
      callSwToolInfoApi()
      $('.saveclass').hide()
  }
  })

  const callSwToolInfoApi = async () => {
    
    axios.post('/api/Swtool?type=list', {
        is_Swtcode: match.swtcode,
    })
    .then( response => {
        try {
            var data = response.data[0]
            $('#is_Swt_toolname').val(data.swt_toolname)
            $('#is_Swt_demo_site').val(data.swt_demo_site)
            $('#is_Giturl').val(data.swt_github_url)
            $('#is_Comments').val(data.swt_comments)
            $('#is_Swt_function').val(data.swt_function)
        } catch (error) {
            alert('작업중 오류가 발생하였습니다')
        }
    })
    .catch( error => {alert(error); return false;} );
}


  const submitClick = async (type, e) => {

      var jsonstr = $("form[name='frm']").serialize();
      jsonstr = decodeURIComponent(jsonstr);
      
      var Json_form = JSON.stringify(jsonstr).replace(/\"/gi, "");
      Json_form =
        '{"' + Json_form.replace(/\&/g, '","').replace(/=/gi, '":"') + '"}';

         

      try {
        const response2 = await axios.post("/api/Swtool?type=" + type, jsonstr);

        if (response2.data === "succ") {
          if (type === "save") {
            sweetalertSucc("Software Tools 등록이 완료되었습니다.", false);
          } else if (type === "save") {
            sweetalertSucc("Software Tools 수정이 완료되었습니다.", false);
          }
          setTimeout( ()=> {
            navigate('/SoftwareList');
            // this.props.navigate('/SoftwareList');
           
          }, 1500);
        } else {
          
          alert("작업중 오류가 발생하였습니다.");
        }
      } catch (error) {
        alert(error);
      }
    
  };


    return (
      <section className="sub_wrap">
        <article className="s_cnt mp_pro_li ct1">
          <div className="li_top">
            <h2 className="s_tit1">Software Tools 등록/수정</h2>
          </div>
          <div className="bo_w re1_wrap re1_wrap_writer">
            <form name="frm" id="frm" action="" method="post">
              <input id="is_Swtcode" type="hidden" name="is_Swtcode" />
              <input
                id="is_Email"
                type="hidden"
                name="is_Email"
                value="guest"
              />
               <input id="is_beforeSwtcode" type="hidden" name="is_beforeSwtcode" value={match.swtcode} />

              <article className="res_w">
                <p className="ment" style={{ textAlign: "right" }}>
                  <span className="red">(*)</span>표시는 필수입력사항 입니다.
                </p>
                <div className="tb_outline">
                  <table className="table_ty1">
                    <tbody>
                      <tr>
                        <th>
                          <label htmlFor="is_Swt_toolname">
                            툴 이름<span className="red">(*)</span>
                          </label>
                        </th>
                        <td>
                          <input
                            type="text"
                            name="is_Swt_toolname"
                            id="is_Swt_toolname"
                            className=""
                          />
                        </td>
                      </tr>
                      <tr>
                        <th>
                          <label htmlFor="is_Swt_demo_site">
                            데모 URL<span className="red">(*)</span>
                          </label>
                        </th>
                        <td>
                          <input
                            type="text"
                            name="is_Swt_demo_site"
                            id="is_Swt_demo_site"
                            className=""
                          />
                        </td>
                      </tr>
                      <tr>
                        <th>
                          <label htmlFor="is_Giturl">
                            Github URL<span className="red">(*)</span>
                          </label>
                        </th>
                        <td>
                          <input
                            type="text"
                            name="is_Giturl"
                            id="is_Giturl"
                            className=""
                          />
                        </td>
                      </tr>
                      <tr>
                        <th>
                          <label htmlFor="is_Comments">
                            설명<span className="red">(*)</span>
                          </label>
                        </th>
                        <td>
                          <textarea
                            name="is_Comments"
                            id="is_Comments"
                            rows=""
                            cols=""
                          ></textarea>
                        </td>
                      </tr>
                      <tr className="div_tb_tr fileb">
                        <th>메뉴얼 파일 #1</th>
                        <td className="fileBox fileBox_w1">
                          <label htmlFor="uploadBtn1" className="btn_file">
                            파일선택
                          </label>
                          <input
                            type="text"
                            id="manualfile"
                            className="fileName fileName1"
                            readOnly="readonly"
                            placeholder="선택된 파일 없음"
                          />
                          <input
                            type="file"
                            id="uploadBtn1"
                            className="uploadBtn uploadBtn1"
                            onChange={(e) => this.handleFileInput("manual", e)}
                          />
                          <div id="upload_menual"></div>
                        </td>
                      </tr>
                      <tr>
                        <th>메인 이미지</th>
                        <td className="fileBox fileBox1">
                          <label htmlFor="imageSelect" className="btn_file">
                            파일선택
                          </label>
                          <input
                            type="text"
                            id="imagefile"
                            className="fileName fileName1"
                            readOnly="readonly"
                            placeholder="선택된 파일 없음"
                          />
                          <input
                            type="file"
                            id="imageSelect"
                            className="uploadBtn uploadBtn1"
                            onChange={(e) => this.handleFileInput("file", e)}
                          />
                          <div id="upload_img"></div>
                        </td>
                      </tr>
                      <tr>
                        <th>라벨 이미지</th>
                        <td className="fileBox fileBox2">
                          <label htmlFor="imageSelect2" className="btn_file">
                            파일선택
                          </label>
                          <input
                            type="text"
                            id="imagefile2"
                            className="fileName fileName1"
                            readOnly="readonly"
                            placeholder="선택된 파일 없음"
                          />
                          <input
                            type="file"
                            id="imageSelect2"
                            className="uploadBtn uploadBtn1"
                            onChange={(e) => this.handleFileInput("file2", e)}
                          />
                          <div id="upload_img2"></div>
                        </td>
                      </tr>
                      <tr>
                        <th>
                          <label htmlFor="is_Swt_function">
                            상세 기능<span className="red">(*)</span>
                          </label>
                        </th>
                        <td>
                          <textarea
                            name="is_Swt_function"
                            id="is_Swt_function"
                            rows=""
                            cols=""
                          ></textarea>
                        </td>
                      </tr>
                    </tbody>
                  </table>
                  <div
                    className="btn_confirm mt20"
                    style={{ marginBottom: "44px" }}
                  >
                   <button type='submit'>
                      <Link
                        to={"/"}
                        className="bt_ty bt_ty1 cancel_ty1"
                      >
                        취소
                      </Link>
                    </button>
                    <button className='saveclass' type='submit'>
                      <a
                        href="#"
                        className="bt_ty bt_ty2 submit_ty1 "
                        onClick={(e) => {
                          submitClick("save", e);
                          e.preventDefault();
                        }}
                      > 저장
                      </a>
                    </button>
                     
                    <button className='modifyclass' type='submit'>
                      <a href="#" className="bt_ty bt_ty2 submit_ty1 " 
                                        onClick={(e) => this.submitClick('modify', e)}>수정</a>
                    </button>

                  </div>
                </div>
              </article>
            </form>
          </div>
        </article>
      </section>
    );
  }

export default SoftwareView;



package.json



{
  "name": "client",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^5.16.4",
    "@testing-library/react": "^13.0.1",
    "@testing-library/user-event": "^13.5.0",
    "axios": "^0.26.1",
    "jquery": "^3.6.0",
    "react": "^17.0.1",
    "react-dom": "^17.0.1",
    "react-router": "^6.3.0",
    "react-router-dom": "^6.3.0",
    "react-scripts": "^2.1.3",
    "sweetalert2": "^11.4.8",
    "web-vitals": "^2.1.4",
    "yarn": "^1.22.18"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "proxy":"http://127.0.0.1:5000"
}





[실습] delete 추가하기



[Back] app.js



var express = require('express');
const { is } = require('express/lib/request');
const res = require('express/lib/response');
var app = express();

app.use(express.json());
app.use(express.urlencoded({ extended: false }));

var router = express.Router();
const { route } = require('./routes/tiger');

app.use('/api/Swtool',
    router.post('/', function (req, res, next) {
        console.log(req.query.type);
        
        if (req.query.type == 'list') {

            req.body.mapper = 'SwToolsMapper';
            req.body.crud = 'select';
            req.body.mapper_id = 'selectSwtool';

            router.use('/', require('./dbconnect_Module'));
            next('route');

        } else if (req.query.type == 'save') {

            req.body.mapper = 'SwToolsMapper';
            req.body.crud = 'insert';
            req.body.mapper_id = 'insertSwToolsInfo';

            router.use('/', require('./dbconnect_Module'));
            next('route');

        } else if (req.query.type == 'modify') {

            req.body.mapper = 'SwToolsMapper';
            req.body.crud = 'update';
            req.body.mapper_id = 'updateSwToolsInfo';

            router.use('/', require('./dbconnect_Module'));
            next('route');

        } else if (req.query.type == 'delete') {

            req.body.mapper = 'SwToolsMapper';
            req.body.crud = 'delete';
            req.body.mapper_id = 'deleteSwToolsInfo';

            router.use('/', require('./dbconnect_Module'));
            next('route');

        }

    }),

);

var port = process.env.PORT || '5000';
app.listen(port, () => { console.log('listen'); });


module.exports = app;



[Back] SwToolsMapper.xml



<?xml version="1.0" encoding="UTF-8"?>
<!-- mybatis 버전을 명시한다. -->
<!DOCTYPE mapper PUBLIC 
  "-//mybatis.org//DTD Mapper 3.0//EN" 
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

  <mapper namespace='SwToolsMapper'>
    <select id="selectSwToolsList">
      SELECT 
        swt_code
        , swt_toolname
        , swt_function
        , swt_imagepath
        , swt_big_imgpath
        , swt_comments
        , swt_demo_site
        , swt_manual_path
        , swt_github_url
        , reg_date
      FROM react.react_swtool
      <if test="is_Swtcode != null && is_Swtcode != ''">
        WHERE swt_code = #{is_Swtcode}
      </if>
      ORDER BY update_date DESC
  </select>


    <!-- 외부 변수를 사용할 땐 #을 사용한다. -->
    <select id='selectOne'>
        select 
        *
        from table02
        where id = #{id} 
    </select>

    <select id='selectSwtool'>
        select 
        *
        from react_swtool
    </select>

     <insert id="insertSwToolsInfo"> 
    INSERT INTO react_swtool
    (
        swt_code
        , swt_toolname
        , swt_function
        , swt_comments
        , swt_demo_site
        , swt_github_url
        <if test="is_LabelImg != null && is_LabelImg != ''">
          , swt_imagepath
        </if>
        <if test="is_MainImg != null && is_MainImg != ''">
          , swt_big_imgpath
        </if>
        <if test="is_MenualName != null && is_MenualName != ''">
          , swt_manual_path
        </if>
        , reg_date
        , reg_user
        , update_date
        , update_user
    )
    VALUES (
      CONCAT('USW', DATE_FORMAT(now(), '%Y%m%d%H%i%s'))
      , #{is_Swt_toolname}
      , #{is_Swt_function}      
      , #{is_Comments}
      , #{is_Swt_demo_site}
      , #{is_Giturl}
      <if test="is_LabelImg != null && is_LabelImg != ''">
        , #{is_LabelImg}
      </if>
      <if test="is_MainImg != null && is_MainImg != ''">
        , #{is_MainImg}
      </if>
      <if test="is_MenualName != null && is_MenualName != ''">
        , #{is_MenualName}
      </if>
      , DATE_FORMAT(now(), '%Y%m%d%H%i%s')
      , #{is_Email}
      , DATE_FORMAT(now(), '%Y%m%d%H%i%s')
      , #{is_Email}
    )
  </insert>  

  <update id="updateSwToolsInfo"> 
    UPDATE react_swtool
    SET
        swt_toolname = #{is_Swt_toolname}
        , swt_function = #{is_Swt_function}
        , swt_comments = #{is_Comments}
        , swt_demo_site = #{is_Swt_demo_site}
        , swt_github_url = #{is_Giturl}
        <if test="is_LabelImg != null && is_LabelImg != ''">
          , swt_imagepath = #{is_LabelImg}
        </if>
        <if test="is_MainImg != null && is_MainImg != ''">
          , swt_big_imgpath = #{is_MainImg}
        </if>
        <if test="is_MenualName != null && is_MenualName != ''">
          , swt_manual_path = #{is_MenualName}
        </if>
        , update_date = DATE_FORMAT(now(), '%Y%m%d%H%i%s')
        , update_user = #{is_Email}
    WHERE swt_code = #{is_beforeSwtcode}
  </update>

    <delete id="deleteSwToolsInfo">
      DELETE FROM react_swtool
      WHERE swt_code = #{is_SwtCd}
    </delete>
  </mapper>



[Front] SoftwareList.js



import React, { Component } from 'react';
import {Link} from 'react-router-dom';
import axios from "axios";
import Swal from 'sweetalert2'
import './App.css'

class SoftwareList extends Component {
    constructor(props) {
        super(props);

        this.state = {
            responseSwtoolList: [],
            append_SwtoolList: [],
        }
    }

    componentDidMount() {
        this.callSwToolListApi()
    }

    callSwToolListApi = async () => {
        axios.post('/api/Swtool?type=list', {
        })
        .then( response => {
            try {
                this.setState({ responseSwtoolList: response });
                this.setState({ append_SwtoolList: this.SwToolListAppend() });
            } catch (error) {
                console.log('작업중 오류가 발생하였습니다.1', error);
            }
        })
        .catch( error => {alert(error); return false;} );
    }

    SwToolListAppend = () => {
        
        let result = []
        var SwToolList = this.state.responseSwtoolList.data
        
        for(let i=0; i<SwToolList.length; i++){
            var data = SwToolList[i]

            var date = data.reg_date
            var year = date.substr(0,4)
            var month = date.substr(4,2)
            var day = date.substr(6,2)
            var reg_date = year +'.'+month+'.'+day

            result.push(
                <tr className="hidden_type" key={i}>
                    <td>{data.swt_toolname}</td>
                    <td>{data.swt_function}</td>
                    <td>{reg_date}</td>
                    <td>
                        <Link to={'/SoftwareView/'+data.swt_code} 
                        className="bt_c1 bt_c2 w50_b">수정</Link>
                        <a href="#n" className="bt_c1 w50_b" id={data.swt_code}
                        onClick={(e) => this.deleteSwtool(e)}>삭제</a>
                    </td>
                </tr>
            )
        }

        console.log(result);
        
        return result
    }


    deleteSwtool = (e) => {
        var event_target = e.target
        this.sweetalertDelete('정말 삭제하시겠습니까?', function() {
            axios.post('/api/Swtool?type=delete', {
                is_SwtCd : event_target.getAttribute('id')
            })
            .then( response => {
                this.callSwToolListApi()
            }).catch( error => {alert('작업중 오류가 발생하였습니다.');return false;} );
        }.bind(this))
    }

    sweetalertDelete = (title, callbackFunc) => {
        Swal.fire({
            title: title,
            text: "",
            icon: 'warning',
            showCancelButton: true,
            confirmButtonColor: '#3085d6',
            cancelButtonColor: '#d33',
            confirmButtonText: 'Yes'
          }).then((result) => {
            if (result.value) {
              Swal.fire(
                'Deleted!',
                '삭제되었습니다.',
                'success'
              )
            }else{
                return false;
            }
            callbackFunc()
          })
    }

    render () {
        console.log('render()');
        
        return (
            <>
            <h3>Software</h3>
            <section className="sub_wrap" >
                <article className="s_cnt mp_pro_li ct1 mp_pro_li_admin">
                    <div className="li_top">
                        <h2 className="s_tit1">Software Tools 목록</h2>
                        <div className="li_top_sch af">
                        <Link to={'/SoftwareView/register'} className="sch_bt2 wi_au">Tool 등록</Link>
                        </div>
                    </div>

                    <div className="list_cont list_cont_admin">
                        <table className="table_ty1 ad_tlist">
                            <tbody>
                            <tr>
                                <th>툴 이름</th>
                                <th>기능</th>
                                <th>등록일</th>
                                <th>기능</th>
                            </tr>
                            </tbody>
                        </table>   
                        <table className="table_ty2 ad_tlist">
                            <tbody>
                            {this.state.append_SwtoolList}
                            </tbody>
                        </table>
                    </div>
                </article>
            </section>
            </>

        );
    }
}

export default SoftwareList;



✅ input 데이터 전송 state로 변경하기


참고



import React from 'react';
import axios from 'axios';

export default class PersonList extends React.Component {
  state = {
    persons: []
  }

  componentDidMount() {
    axios.get(`https://jsonplaceholder.typicode.com/users`)
      .then(res => {
        const persons = res.data;
        this.setState({ persons });
      })
  }

  render() {
    return (
      <ul>
        {
          this.state.persons
            .map(person =>
              <li key={person.id}>{person.name}</li>
            )
        }
      </ul>
    )
  }
}



좋은 웹페이지 즐겨찾기