일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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
3회차_1강 : 등록페이지를 구현한다. (text, 이미지, 파일) 본문
3회차_1강 : 등록페이지를 구현한다. (text, 이미지, 파일)
착한코딩 2020. 1. 16. 00:383_1 목표 : 정합성 높은 데이터를 DB에 Insert 한다. 이미지 및 파일 업로드를 구현하고 흐름을 이해한다.
2회차 2강에서 text 데이터 삽입을 구현했다. 3회차 1강에서는 이미지와 파일을 node 경로에 업로드하고, 파일명만
text로 DB table에 저장할 것이다.
1. 등록페이지 react 파일을 수정한다.
AdminSoftwareView.js 파일에 아래 소스를 붙여넣는다.
state에 업로드 관련 변수가 추가되었고,
메뉴얼 파일과 메인/라벨 이미지를 선택하는 순간 node 서버의 파일 upload api를 호출하는 코드가 추가됐다.
import React, { Component } from 'react';
import {Link} from 'react-router-dom';
import axios from "axios";
import $ from 'jquery';
class SoftwareView extends Component {
constructor(props) {
super(props);
this.state = {
responseSwtoolInfo: '',//swtool 정보 response 변수
append_SwtoolInfo: '', //swtool 정보 append 변수
//파일, 이미지 업로드
file: '',//메인 이미지 미리보기용 path
file2: '',//라벨 이미지 미리보기용 path
fileName: '',//메인 이미지명
fileName2: '',//라벨 이미지명
menualName: '',//메뉴얼명
selectedFile: null, //업로드 대상 파일
}
}
componentDidMount () {
// SW Tool 정보 호출
this.callSwToolInfoApi()
}
//업로드할 파일 세팅
handleFileInput(type, e){
var id = e.target.id
if(type =='file'){
$('#imagefile').val(e.target.files[0].name)
}else if(type =='file2'){
$('#imagefile2').val(e.target.files[0].name)
}else if(type =='manual'){
$('#manualfile').val(e.target.files[0].name)
}
this.setState({
selectedFile : e.target.files[0],
})
if(type =='manual'){
setTimeout(function() {
this.handlePostMenual(type, id ,e)
}.bind(this),1
);
}else{
setTimeout(function() {
this.handlePostImage(type, id ,e)
}.bind(this),1
);
}
}
//메뉴얼 업로드
handlePostMenual(type, id, e){
const formData = new FormData();
formData.append('file', this.state.selectedFile);
return axios.post("/api/upload?type=uploads/swmanual/", formData).then(res => {
try {
this.state.menualName = res.data.filename
$('#upload_menual').prepend('<input id="is_MenualName" type="hidden" name="is_MenualName" value="'+this.state.menualName+'"}/>')
} catch (error) {
alert('작업중 오류가 발생하였습니다.')
}
}).catch(error => {
alert('작업중 오류가 발생하였습니다.')
})
}
//이미지 업로드
handlePostImage(type, id, e){
const formData = new FormData();
formData.append('file', this.state.selectedFile);
return axios.post("/api/upload?type=uploads/image/", formData).then(res => {
try {
setTimeout(function() {
if(type =='file'){
this.state.file = '/image/'+res.data.filename
this.state.fileName = res.data.filename
$('#upload_img').prepend('<img id="uploadimg" src="'+this.state.file+'"/>')
$('#upload_img').prepend('<input id="is_MainImg" type="hidden" name="is_MainImg" value="'+this.state.fileName+'"}/>')
}else if(type =='file2'){
this.state.file2 = '/image/'+res.data.filename
this.state.fileName2 = res.data.filename
$('#upload_img2').prepend('<img id="uploadimg2" src="'+this.state.file2+'"/>')
$('#upload_img2').prepend('<input id="is_LabelImg" type="hidden" name="is_LabelImg" value="'+this.state.fileName2+'"}/>')
}
}.bind(this),1000
);
} catch (error) {
alert('작업중 오류가 발생하였습니다.')
}
}).catch(error => {
alert('작업중 오류가 발생하였습니다.')
})
}
// 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
}
// 저장 버튼 클릭시 validate check
submitClick = async (type, e) => {
this.Swt_toolname_checker = $('#is_Swt_toolname').val();
this.Swt_demo_site_checker = $('#is_Swt_demo_site').val();
this.Giturl_checker = $('#is_Giturl').val();
this.Comments_checker = $('#is_Comments').val();
this.Swt_function_checker = $('#is_Swt_function').val();
this.fnValidate = (e) => {
// ## Swt_toolname check start
if(this.Swt_toolname_checker === '') {
$('#is_Swt_toolname').addClass('border_validate_err');
alert('툴 이름을 다시 확인해주세요.')
return false;
}
$('#is_Swt_toolname').removeClass('border_validate_err');
// ## Swt_demo_site check start
if(this.Swt_demo_site_checker === '') {
$('#is_Swt_demo_site').addClass('border_validate_err');
alert('데모 URL을 다시 확인해주세요.')
return false;
}
$('#is_Swt_demo_site').removeClass('border_validate_err');
// ## Giturl check start
if(this.Giturl_checker === '') {
$('#is_Giturl').addClass('border_validate_err');
alert('Github URL을 다시 확인해주세요.')
return false;
}
$('#is_Giturl').removeClass('border_validate_err');
// ## Comments check start
if(this.Comments_checker === '') {
$('#is_Comments').addClass('border_validate_err');
alert('설명을 다시 확인해주세요.')
return false;
}
$('#is_Comments').removeClass('border_validate_err');
// ## Swt_function check start
if(this.Swt_function_checker === '') {
$('#is_Swt_function').addClass('border_validate_err');
alert('상세기능을 다시 확인해주세요.')
return false;
}
$('#is_Swt_function').removeClass('border_validate_err');
var date = new Date()
var y_str = date.getFullYear().toString();
var month = date.getMonth()+1
var m_str = month.toString();
var day = date.getDate()
var d_str = day.toString();
var hour = date.getHours()
var min = date.getMinutes()
var sec = date.getSeconds()
// 프로젝트 코드생성
this.state.swtcode = 'USW'+y_str+m_str+d_str+hour+min+sec
$('#is_Swtcode').val(this.state.swtcode)
return true;
}
//유효성 체크
if(this.fnValidate()){
//software Tools 저장
//form type To Json
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 response = await fetch('/api/Swtool?type='+type, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
//한글 디코딩
body: Json_form,
});
const body = await response.text();
if(body == "succ"){
if(type == 'save'){
alert('Software Tools 등록이 완료되었습니다.')
}else if(type == "modify"){
alert('DSoftware Tools 수정이 완료되었습니다.')
}
// 저장 후 리스트페이지로 이동
window.location.href = 'http://localhost:3000/AdminSoftwareList';
}else{
alert('작업중 오류가 발생하였습니다.')
}
} catch (error) {
alert('작업중 오류가 발생하였습니다.')
}
}//fnValidate end
};
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;
node 경로 C:\Users\ljung\OneDrive\문서\taling0102 에 uploads폴더를 생성하고, uploads폴더에 image폴더와 swmanual폴더를 추가해준다. 메뉴얼 파일은 swmanual폴더에, 이미지 파일은 image폴더에 업로드된다.
2. node 경로의 파일들을 수정하고, 업로드 파일이 저장될 경로를 생성한다.
node 경로 C:\Users\ljung\OneDrive\문서\taling0102의 server.js를 아래와 같이 수정한다.
이미지, 파일과 같은 정적 파일을 제공할 때 Express의 기본 제공 미들웨어 함수인 express.static을 사용하면 편리하다.
정적 파일이 포함된 디렉토리의 이름을 express.static 미들웨어 함수에 전달하면, 해당 디렉토리에서 파일의 직접적인 제공받을 수 있다.
## 참고 ##
미들웨어 : 구조 내에서 중간 처리를 위한 함수
var express = require('express');
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var swtoolRouter = require("./routes/SwtoolRout");
var fileuploadRouter = require("./routes/UploadRout");
var app = express();
app.use('/', indexRouter);
app.use('/users', usersRouter);
//sw Tool 조회
app.use("/api/Swtool", swtoolRouter);
//파일 업로드
app.use("/api/upload", fileuploadRouter);
//파일 업로드 경로 설정
app.use(express.static("./uploads"));
const port = process.env.PORT || 5000;
app.listen(port, () => console.log(`Listening on port ${port}`));
node 경로 C:\Users\ljung\OneDrive\문서\taling0102 에서
파일업로드에 필요한 패키지들을 설치해준다.
파일을 api 호출을 통해 react 페이지에서 node 서버로 이동시키려면 데이터 타입이 "multipart/form-data" 이어야 한다.
multer라는 패키지가 "multipart/form-data" 를 지원해준다.
moment는 업로드된 파일명 앞에 시간정보를 붙여서 새로운 파일명을 생성해 주기위한 목적으로 사용했다.
시간 포맷을 지원해주는 패키지이다.
npm install --save multer
npm install --save moment
C:\Users\ljung\OneDrive\문서\taling0102\routes 경로에서
UploadRout.js 파일을 만들고 아래 내용을 붙여넣는다.
var express = require('express');
var router = express.Router();
var upload = require('../modules/fileupload');
var multer = require('multer');
router.post("/", (req, res, next) => {
// FormData의 경우 req로 부터 데이터를 얻을수 없다.
// upload 핸들러(multer)를 통해서 데이터를 읽을 수 있다
upload(req, res, function(err) {
if (err instanceof multer.MulterError) {
return next(err);
} else if (err) {
return next(err);
}
console.log('원본파일명 : ' + req.file.originalname)
console.log('저장파일명 : ' + req.file.filename)
console.log('크기 : ' + req.file.size)
console.log('경로 : ' + req.file.location)
return res.json({success:1, filename:req.file.filename});
});
});
module.exports = router;
C:\Users\ljung\OneDrive\문서\taling0102\modules 경로에 fileupload.js 파일을 만들고 아래 내용을 붙여넣는다.
const multer = require('multer');
const moment = require('moment');
const storage = multer.diskStorage({
destination: function(req, file, cb) {
try {
var m_typ = req.query.type;
cb(null, m_typ); // 파일이 저장되는 경로입니다.
} catch (error) {
console.log(error)
}
},
filename: function(req, file, cb) {
cb(null, moment().format('YYYYMMDDHHmmss') + "_" + file.originalname); // 저장되는 파일명
}
});
const upload = multer({ storage: storage }).single("file"); // single : 하나의 파일업로드 할때
module.exports = upload;
node 경로 C:\Users\ljung\OneDrive\문서\taling0102에
파일과 이미지가 저장될 폴더를 생성해준다.
3. 실제 파일을 업로드 해보고, 등록페이지 form을 저장한다.
아래와 같이 text 데이터와 메뉴얼파일, 이미지파일을 등록해준다.
업로드 파일은 선택되는 순간, 미리보기 이미지가 보이며
node 서버 경로에실제 파일이 저장된 것을 확인한다.
등록페이지에서 저장버튼을 누르면 아래 쿼리 로그와 같이
업로드된 파일명이 저장된다.
https://taling.me/Talent/Detail/19341
'탈잉 강의 자료 > react.js(프론트) + node.js(백앤드) 개발에서 배포까지' 카테고리의 다른 글
3회차_3강 : ALERT 디자인을 수정하고, 삭제기능을 구현한다. Source tree를 사용해 git에 소스를 업로드한다. (0) | 2020.01.16 |
---|---|
3회차_2강 : 수정페이지를 구현한다. (text, 이미지, 파일) (0) | 2020.01.16 |
2회차_2강 : AWS 가입, RDS mysql 인스턴스를 구동하고 스키마와 테이블을 생성한다. (0) | 2020.01.07 |
2회차_1강 : AWS RDS mysql 서버 연결 후, 데이터를 웹으로 가져온다. (0) | 2020.01.05 |
1회차_3강 : 퍼블리싱 파일(html, css, img)을 react에 맞게 세팅한다. (0) | 2019.11.05 |