비트 컴퓨터 [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>
)
}
}
Author And Source
이 문제에 관하여(비트 컴퓨터 [Node.js] - ③), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@corone_hi/비트-컴퓨터-Node.js-zm5cy72x저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)