일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 스페이스우일
- 보드게임점수
- MBTI
- 스컬킹점수
- mysql
- 옥길그릭요거트
- 광명파티룸
- 부천파티룸
- 옥길동파티룸
- 스컬킹
- 휴식
- 옥길동요거트
- 그릭요거트
- 코딩
- 취미
- 파티룸
- 해외여행
- 구로파티룸
- 스컬킹점수계산
- 착한코딩
- 부천공간대여
- 옥길파티룸
- 옥길요거트
- 보드게임점수계산
- 존경하는위인
- 개발자
- 서울파티룸
- 일
- 가장존경하는인물
- 웹개발
- Today
- Total
SIMPLE & UNIQUE
2회차_1강 : AWS RDS mysql 서버 연결 후, 데이터를 웹으로 가져온다. 본문
2회차_1강 : AWS RDS mysql 서버 연결 후, 데이터를 웹으로 가져온다.
착한코딩 2020. 1. 5. 23:022_1 목표 : 이미 테이블이 세팅되어 있는 mysql 서버에서 데이터를 select 하고, react 페이지에 리스트로 노출시킨다.
다음 2회차 2강에서 각자 AWS 계정을 만들고 RDS mysql 서버를 프리티어(1년무료) 버전으로 인스턴스를 생성한다. 그리고 테이블을 생성해 더미 데이터를 삽입할 것이다.
2회차 1강에서는 이미 세팅이 완료된 작성자의 DB 서버에 접속해 실습한다.
App.js에서 1회차에서 SoftwareTool 관련 부분을 주석처리해 놨었는데, 아래와 같이 주석을 해제한다.
import React, { Component } from 'react';
import { Router, Route, Switch } from "react-router";
import Api_test from './Api_test'
// css
import '../css/new.css';
import '../css/owl.carousel.min.css';
import '../css/owl.theme.default.min.css';
// header
import HeaderAdmin from './Header/Header admin';
// footer
import Footer from './Footer/Footer';
// login
import LoginForm from './LoginForm';
// admin floatingPopulationList
import floatingPopulationList from './Floating_population/floatingPopulationList';
// admin softwareinfo
import AdminSoftwareList from './SoftwareToolsManage/AdminSoftwareList';
import AdminSoftwareView from './SoftwareToolsManage/AdminSoftwareView';
class App extends Component {
constructor (props) {
super(props);
this.state = {
}
}
componentDidMount() {}
render () {
return (
<div className="App">
<HeaderAdmin/>
<Switch>
{/* <Route exact path='/' component={Api_test} /> // root 경로일 경우 라우팅 */}
<Route exact path='/' component={LoginForm} />
<Route path='/Api_test' component={Api_test} />
<Route path='/floatPopulationList' component={floatingPopulationList} />
<Route path='/AdminSoftwareList' component={AdminSoftwareList} />
<Route path='/AdminSoftwareView/:swtcode' component={AdminSoftwareView} />
</Switch>
<Footer
footer_address={this.props.footer_address}
footer_tel={this.props.footer_tel}
footer_email={this.props.footer_email}
footer_mobile={this.props.footer_mobile}
/>
</div>
);
}
}
App.defaultProps = {
// footer value
footer_address: '[34234] 서울특별시 강남구 삼성동 111-114',
footer_tel: '02-1234-5678',
footer_email: 'ljung5@naver.com',
footer_mobile: '010-3288-3398',
};
export default App
1. SoftwareTools 파일을 세팅한다.
react 경로 C:\Users\ljung\OneDrive\문서\taling0102\client\src\components에 SoftwareToolsManage폴더를 생성한다. 생성한 폴더안에 AdminSoftwareList.js와 AdminSoftwareView.js 파일을 만든다.
AdminSoftwareView에 아래 소스를 붙여넣는다.
import React, { Component } from 'react';
import {Link} from 'react-router-dom';
import axios from "axios";
class SoftwareView extends Component {
constructor(props) {
super(props);
this.state = {
responseSwtoolInfo: '',//swtool 정보 response 변수
append_SwtoolInfo: '', //swtool 정보 append 변수
}
}
componentDidMount () {
// SW Tool 정보 호출
this.callSwToolInfoApi()
}
// SW Tool 정보 호출
callSwToolInfoApi = async () => {
this.setState({ append_SwtoolInfo: this.SwToolInfoAppend() });
}
// SW Tool 정보 append
SwToolInfoAppend = () => {
let result = []
result.push(
<table class="table_ty1">
<tr>
<th>
<label for="is_Swt_toolname">툴 이름<span class="red">(*)</span></label>
</th>
<td>
<input type="text" name="is_Swt_toolname" id="is_Swt_toolname" class="" />
</td>
</tr>
<tr>
<th>
<label for="is_Swt_demo_site">데모 URL<span class="red">(*)</span></label>
</th>
<td>
<input type="text" name="is_Swt_demo_site" id="is_Swt_demo_site" class="" />
</td>
</tr>
<tr>
<th>
<label for="is_Giturl">Github URL<span class="red">(*)</span></label>
</th>
<td>
<input type="text" name="is_Giturl" id="is_Giturl" class="" />
</td>
</tr>
<tr>
<th>
<label for="is_Comments">설명<span class="red">(*)</span></label>
</th>
<td>
<textarea name="is_Comments" id="is_Comments" rows="" cols=""></textarea>
</td>
</tr>
<tr class="div_tb_tr fileb">
<th>
메뉴얼 파일 #1
</th>
<td class="fileBox fileBox_w1">
<label for="uploadBtn1" class="btn_file">파일선택</label>
<input type="text" id="manualfile" class="fileName fileName1" readonly="readonly" placeholder="선택된 파일 없음"/>
<input type="file" id="uploadBtn1" class="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 for="is_Swt_function">상세 기능<span class="red">(*)</span></label>
</th>
<td>
<textarea name="is_Swt_function" id="is_Swt_function" rows="" cols=""></textarea>
</td>
</tr>
</table>
)
return result
}
render () {
return (
<section class="sub_wrap">
<article class="s_cnt mp_pro_li ct1">
<div class="li_top">
<h2 class="s_tit1">Software Tools 등록/수정</h2>
</div>
<div class="bo_w re1_wrap re1_wrap_writer">
<form name="frm" id="frm" action="" onsubmit="" method="post" >
<input id="is_Email" type="hidden" name="is_Email" value={this.state.admin_userid} />
<input id="is_Swtcode" type="hidden" name="is_Swtcode" value={this.state.swtcode} />
<input id="is_beforeSwtcode" type="hidden" name="is_beforeSwtcode" value={this.state.before_swtcode} />
<article class="res_w">
<p class="ment" style={{"text-align": "right"}}>
<span class="red">(*)</span>표시는 필수입력사항 입니다.
</p>
<div class="tb_outline">
{this.state.append_SwtoolInfo}
<div class="btn_confirm mt20" style={{"margin-bottom": "44px"}}>
<Link to={'/AdminSoftwareList'} className="bt_ty bt_ty1 cancel_ty1">취소</Link>
<a href="javascript:" className="bt_ty bt_ty2 submit_ty1 saveclass" onClick={(e) => this.submitClick('save', e)}>저장</a>
<a href="javascript:" className="bt_ty bt_ty2 submit_ty1 modifyclass" onClick={(e) => this.submitClick('modify', e)}>수정</a>
</div>
</div>
</article>
</form>
</div>
</article>
</section>
);
}
}
export default SoftwareView;
AdminSoftwareList 에 아래 소스를 붙여넣는다.
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: '',//swtool 리스트 response 변수
append_SwtoolList: '', //swtool 리스트 append 변수
}
}
componentDidMount() {
// SW Tool 리스트 호출
this.callSwToolListApi()
}
// SW Tool 리스트 호출
callSwToolListApi = async () => {
//SW Tool List 호출
axios.post('/api/Swtool?type=list', {
})
.then( response => {
try {
this.setState({ responseSwtoolList: response });
this.setState({ append_SwtoolList: this.SwToolListAppend() });
} catch (error) {
alert('작업중 오류가 발생하였습니다.');
}
})
.catch( error => {alert('작업중 오류가 발생하였습니다.');return false;} );
}
// SW Tool 리스트 append
SwToolListAppend = () => {
let result = []
var SwToolList = this.state.responseSwtoolList.data
for(let i=0; i<SwToolList.json.length; i++){
var data = SwToolList.json[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 class="hidden_type">
<td>{data.swt_toolname}</td>
<td>{data.swt_function}</td>
<td>{reg_date}</td>
<td>
<Link to={'/AdminSoftwareView/'+data.swt_code} className="bt_c1 bt_c2 w50_b">수정</Link>
<a href="#n" class="bt_c1 w50_b" id={data.swt_code} toolname={data.swt_toolname} onClick={(e) => this.deleteSwtool(e)}>삭제</a>
</td>
</tr>
)
}
return result
}
render () {
return (
<section class="sub_wrap" >
<article class="s_cnt mp_pro_li ct1 mp_pro_li_admin">
<div class="li_top">
<h2 class="s_tit1">Software Tools 목록</h2>
<div class="li_top_sch af">
<Link to={'/AdminSoftwareView/register'} className="sch_bt2 wi_au">Tool 등록</Link>
</div>
</div>
<div class="list_cont list_cont_admin">
<table class="table_ty1 ad_tlist">
<tr>
<th>툴 이름</th>
<th>기능</th>
<th>등록일</th>
<th>기능</th>
</tr>
</table>
<table class="table_ty2 ad_tlist">
{this.state.append_SwtoolList}
</table>
</div>
</article>
</section>
);
}
}
export default SoftwareList;
이때 axios.post('/api/Swtool?type=list' 에서 node api를 호출하는데, node코드에 관련 라우터가 없어서 오류가 발생할 것이다.
2. node 코드를 수정하기전에 필요한 패키지들을 다운받는다.
cmd 또는 터미널을 열고 node경로 C:\Users\ljung\OneDrive\문서\taling0102에서 아래 명령어로 패키지들을 설치해 준다. mysql을 사용할 것이고, node 코드와 sql코드를 분리하기 위해 mybatis를 사용할 것이다. body-parser는 http 통신으로 request, response 데이터를 주고 받을 때, body에 있는 데이터를 파싱하기 위해 사용된다.
//node 경로로 이동
CD C:\Users\ljung\OneDrive\문서\taling0102
npm install --save mysql
npm install --save mybatis-mapper
npm install --save body-parser
## 참고 ##
mybatis는 자바 퍼시스턴트 프레임워크이다. persistent란 사용한 프로그램이 종료되어도 데이터는 남는다는 의미로, 쉽게 말해서 데이터베이스를 사용한다는 것이다.
자바에서 ORM(Object Relational Mapping)중 가장 많이 사용한다. 사용하는 가장 큰 이유는 비즈니스 소스와 sql 소스를 분리해서 개발할 수 있다는 점이다.
node도 마찬가지로 node 코드 사이에 sql을 깨워서 개발하는 방법보다는, 따로 분리시켜 개발 편의성과 유지보수 효율성을 높이기 위해 사용했다.
3. node 라우터, 모듈, 모델, db connection 관련 소스를 세팅한다.
node경로 C:\Users\ljung\OneDrive\문서\taling0102 에서 server.js 파일을 열고 아래 소스를 붙여넣는다.
swtool관련 호출을 라우팅하는 코드가 추가되었다.
var express = require('express');
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var swtoolRouter = require("./routes/SwtoolRout");
var app = express();
app.use('/', indexRouter);
app.use('/users', usersRouter);
//sw Tool 조회
app.use("/api/Swtool", swtoolRouter);
const port = process.env.PORT || 5000;
app.listen(port, () => console.log(`Listening on port ${port}`));
node경로 C:\Users\ljung\OneDrive\문서\taling0102\routes 에 SwtoolRout.js 파일을 추가한다.
SwtoolRout.js 파일에 아래 소스를 붙여넣는다.
var express = require('express');
var router = express.Router();
const bodyParser = require('body-parser');
router.use(bodyParser.json());
router.use(bodyParser.urlencoded({ extended: true }));
var usersModule = require('../modules/SwtoolModule');
//post 호출
router.post('/', (req, res, next) => {
router.use('/', usersModule);
next('route')
});
module.exports = router;
node경로 C:\Users\ljung\OneDrive\문서\modules 에 SwtoolModule.js와 dbconnect_Module.js 파일을 추가한다.
SwtoolModule.js 파일에 아래 소스를 붙여넣는다.
var express = require('express');
var router = express.Router();
const bodyParser = require('body-parser');
router.use(bodyParser.json());
router.use(bodyParser.urlencoded({ extended: true }));
router.post('/', (req, res, next) => {
var m_typ = req.query.type;
if(m_typ == 'list'){
//Swtool 리스트 조회
try {
// Mysql Api 모듈(CRUD)
var dbconnect_Module = require('./dbconnect_Module');
//Mysql 쿼리 호출정보 입력
req.body.mapper = 'SwToolsMapper';//mybatis xml 파일명
req.body.crud = 'select';//select, insert, update, delete 중에 입력
req.body.mapper_id = 'selectSwToolsList';
router.use('/', dbconnect_Module);
next('route')
} catch (error) {
console.log("Module > dbconnect error : "+ error);
}
}
});
module.exports = router;
dbconnect_Module.js 파일에 아래 소스를 붙여넣는다.
## 참고 ##
아래 코드에서 createPool()함수는 node서버와 mysql 서버 사이에 connection pool을 생성한 것이다. cp가 없다면 사용자마다 실행하는 쿼리마다 1개씩 커넥션이 생기고, 데이터 호출 후에 연결이 끊긴다. cp는 생성된 연결을 끊지 않고 유지시켜, 연결에 소모되는 자원을 줄일 수 있게 해준다.
connectionLimit : 66은 사용하고 있는 rds 서버 max connection수(동시접속허용수)가 66개 이기 때문에, 최대값으로 세팅했다.
서버가 켜지자마자 66개의 연결이 생기는 건 아니고, 커넥션이 생길 때마다 1개씩 증가해 그때까지 생성된 연결을 유지한다.
var express = require("express");
var router = express.Router();
const mysql = require("mysql");
const bodyParser = require("body-parser");
router.use(bodyParser.json());
router.use(bodyParser.urlencoded({ extended: true }));
// Connection Pool 세팅
const pool = mysql.createPool({
connectionLimit: 66,
waitForConnections: true,
host: "비밀",
port: "3306",
database: 'rtrod',
user: "rtrod_user",
password: "비밀",
});
// pool 동작 확인용 로깅 - 획득
pool.on('acquire', function (connection) {
console.log('Connection %d acquired', connection.threadId);
});
// pool 동작 확인용 로깅 - 연결
pool.on('connection', function (connection) {
console.log('SET SESSION auto_increment_increment=1');
});
// pool 동작 확인용 로깅 - 대기
pool.on('enqueue', function () {
console.log('Waiting for available connection slot');
});
// pool 동작 확인용 로깅 - 해제
pool.on('release', function (connection) {
console.log('Connection %d released', connection.threadId);
});
router.post("/", (req, res) => {
const mybatisMapper = require("mybatis-mapper");
var param = req.body;
var m_typ = req.query.type;
mybatisMapper.createMapper(["./models/" + param.mapper + ".xml"]);
var time1 = new Date();
console.log("## " + time1 + " ##");
console.log("\n Called Mapper Name = " + param.mapper);
//질의문 형식
var format = { language: "sql", indent: " " };
var query = mybatisMapper.getStatement(
param.mapper,
param.mapper_id,
param,
format
);
console.log("\n========= Node Mybatis Query Log Start =========");
console.log(
"* mapper namespce : " + param.mapper + "." + param.mapper_id + " *\n"
);
console.log(query + "\n");
try {
pool.getConnection(function(err,connection){
if (err) throw err;
connection.query(query, function(error, results) {
//조회
var time2 = new Date();
console.log("## " + time2 + " ##");
console.log("## RESULT DATA LIST ## : \n", results);
string = JSON.stringify(results);
var json = JSON.parse(string);
console.log("========= Node Mybatis Query Log End =========\n");
// 조회
try {
if (req.body.crud == "select") {
//로그인 정보 확인
if (param.mapper_id == "selectLoginCheck" && m_typ != "modinfo") {
if (json[0] == undefined) {
res.send(null);
} else {
bcrypt.compare(req.body.is_Password, json[0].userpassword, function(
err,
login_flag
) {
if (login_flag == true) {
res.send({ json });
} else {
res.send(null);
}
});
}
} else {
res.send({ json });
}
//삽입
} else if (req.body.crud == "insert") {
res.send("succ");
//수정
} else if (req.body.crud == "update") {
res.send("succ");
//삭제
} else if (req.body.crud == "delete") {
res.send("succ");
} else {
}
} catch (error) {
// res.send("error");
}
connection.release();
if (error) {
console.log("db error************* : " + error);
throw error
}
});
})
} catch (error) {
console.log("pool error : "+error);
}
});
module.exports = router;
node경로 C:\Users\ljung\OneDrive\문서\taling0102\models 에 SwToolsMapper.xml 파일을 추가한다.
SwToolsMapper.xml 파일에 아래 소스를 붙여넣는다.
<?xml version="1.0" encoding="UTF-8"?>
<!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 rtrod.rtrod_swtool
ORDER BY update_date DESC
<if test="startRow != null && startRow != ''">
limit ${startRow}, ${endRow}
</if>
</select>
</mapper>
yarn dev 명령어로 서버를 다시 시작하고, 헤더에서 software tools 관리를 선택하면 아래와 같은 화면이 나온다.
이때, cmd 또는 터미널에 관련 쿼리와 결과 리스트가 출력된다.
리스트페이지에서 Tool 등록 버튼을 누르면, 아래와 같이 등록페이지가 노출된다.
https://taling.me/Talent/Detail/19341
'탈잉 강의 자료 > react.js(프론트) + node.js(백앤드) 개발에서 배포까지' 카테고리의 다른 글
3회차_1강 : 등록페이지를 구현한다. (text, 이미지, 파일) (2) | 2020.01.16 |
---|---|
2회차_2강 : AWS 가입, RDS mysql 인스턴스를 구동하고 스키마와 테이블을 생성한다. (0) | 2020.01.07 |
1회차_3강 : 퍼블리싱 파일(html, css, img)을 react에 맞게 세팅한다. (0) | 2019.11.05 |
1회차_2강 : node 설치, create react app설치, node express 설치, 디렉터리 구조 설정 (Proxy 정상 동작 확인) (0) | 2019.11.05 |
목차 (0) | 2019.10.31 |