일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 그릭요거트
- 옥길그릭요거트
- 취미
- 옥길동파티룸
- 서울파티룸
- 옥길요거트
- 스컬킹점수계산
- 존경하는위인
- 스컬킹점수
- 휴식
- 구로파티룸
- 개발자
- mysql
- 웹개발
- 코딩
- 부천공간대여
- 옥길동요거트
- 보드게임점수
- MBTI
- 해외여행
- 스컬킹
- 부천파티룸
- 일
- 파티룸
- 스페이스우일
- 보드게임점수계산
- 광명파티룸
- 착한코딩
- 옥길파티룸
- 가장존경하는인물
- Today
- Total
SIMPLE & UNIQUE
4회차_2강 : EC2 서버에 소스파일을 전송 후, 클라우드 서버를 구동한다. 본문
4회차_2강 : EC2 서버에 소스파일을 전송 후, 클라우드 서버를 구동한다.
착한코딩 2020. 1. 27. 01:244_2 목표 : 서버경로에 node를 설치하고, SFTP로 소스파일을 전송해 서버를 구동한다. 그리고 외부 접속 테스트를 한다.
1. ec2 서버에 업로드할 소스파일을 tar 파일로 압축한다.
/node_modules, /client/node_modules 폴더를 제외한 파일들을 새폴더에 복사하고, tar 파일로 압축한다.
2. Sftp를 이용해 tar 파일을 전송한다.
mobaXterm을 열어 Sftp를 연결하고, 소스파일들을 위치시킬 폴더를 생성한다.
새로 생성한 파일에 tar 파일을 드래그해 이동시킨다.
3. SSH로 서버 터미널에 접속해 tar파일이 있는 경로로 진입해 압축을 푼다.
터미널에 접속되면 아래 명령어들을 순서대로 실행한다.
// root 권한으로 전환
sudo su -
// tar 파일 압축을 풀 위치로 이동
cd /home/ubuntu/taling_200127
// tar 파일 압축해제
tar -xvf tarling_200127.tar
4. node.js와 npm을 설치한다.
아래 명령어로 ubuntu 서버에 node.js와 npm을 설치한다.
// apt-get 업데이트
sudo apt-get update
// node.js 설치
sudo apt-get install -y nodejs
// npm 설치
apt install npm
5. node 경로로 진입해, package.json 파일에 작성되어 있는 패키지들을 설치한다.
node 경로에서 아래 순서대로 명령어를 실행한다.
// node 경로로 접근
cd /home/ubuntu/taling_200127/taling0127
// pakage.json 파일에 작성된 패키지 리스트 설치
npm install
Sftp 창을 새로고침해보면 node 경로에, node_modules 폴더가 생성된 것을 확인할 수 있다.
6. react 경로(/client)로 진입해, package.json 파일에 작성되어 있는 패키지들을 설치한다.
react경로에서 아래 순서대로 명령어를 실행한다.
// react 경로로 접근
cd /home/ubuntu/taling_200127/taling0127/client
// yarn 설치
npm install –g yarn
// pakage.json 파일에 작성된 패키지 리스트 설치
yarn install
//서버시간 변경
sudo rm /etc/localtime
sudo ln -s /usr/share/zoneinfo/Asia/Seoul /etc/localtime
//서버시간 확인
date
yarn install로 설치되지 않는다면, npm install을 한다. 이것도 안되면, 아래 명령어로 npm을 최신화한 다음 npm install을 실행한다.
npm install -g npm@latest
sudo npm install
Sftp 창을 새로고침해보면 react 경로에, node_modules 폴더가 생성된 것을 확인할 수 있다.
7. 로컬에서와 동일하게 yarn dev 명령어로 react, node 서버를 동시에 구동한다.
먼저 react 경로의 package.json에서 로컬주소로 되어있는 proxy IP를 EC2서버 IP로 바꿔준다.
// react 경로로 접근
cd /home/ubuntu/taling_200127/taling0127/client
// proxy ip 수정을 위해, vi 명령어로 에디터를 실행한다.
vi package.json
아래와 같이 에디터가 실행되면 i > enter 로 수정모드로 변경하고
커서를 이동해 맨 아래 proxy 부분을 아래와 같이 수정한다.
esc > :wq 로 수정된 사항을 저장한다.
node 경로에서 yarn dev 명령어로 서버를 구동한다.
Software tools 목록에 진입하고, 로컬서버와 동일하게 아래와 같은 페이지가 노출되는지 확인한다.
8. Tool 등록/수정페이지에서 [저장] 버튼을 눌렀을 때, 리스트 페이지로 redirect 되는 부분을 수정한다.
AdminSoftwareView.js 파일을 보면 저장이 성공적으로 완료되면,
로컬 주소인 http://localhost:3000/AdminSoftwareList로 페이지 이동을 시킨다.
if(body == "succ"){
if(type == 'save'){
this.sweetalertSucc('Software Tools 등록이 완료되었습니다.', false)
}else if(type == "modify"){
this.sweetalertSucc('Software Tools 수정이 완료되었습니다.', false)
}
// 저장 후 리스트페이지로 이동
var thiss=this
setTimeout(function() {
// datasource 정보 호출
window.location.href = 'http://localhost:3000/AdminSoftwareList';
}.bind(this),1500
);
}
EC2 서버에서는 IP주소가 로컬과 다르기 때문에 아래 코드처럼 수정해줘야 한다.
## 참고 ##
Route는 컴포넌트에 기본적으로 match, history, location 이라는 것을 넘겨준다. 이때 histroy.push(‘/react 경로’) 함수에 react 경로를 넣어주면 해당 url로 새로고침 없이 이동시켜준다.
if(body == "succ"){
if(type == 'save'){
this.sweetalertSucc('Software Tools 등록이 완료되었습니다.', false)
}else if(type == "modify"){
this.sweetalertSucc('Software Tools 수정이 완료되었습니다.', false)
}
// 저장 후 리스트페이지로 이동
setTimeout(function() {
// datasource 정보 호출
this.props.history.push('/AdminSoftwareList');
}.bind(this),1500
);
}
AdminSoftwareView.js 전체 코드
import React, { Component } from 'react';
import {Link} from 'react-router-dom';
import axios from "axios";
import $ from 'jquery';
import Swal from 'sweetalert2'
class SoftwareView extends Component {
constructor(props) {
super(props);
this.state = {
responseSwtoolInfo: '',//swtool 정보 response 변수
append_SwtoolInfo: '', //swtool 정보 append 변수
before_swtcode: props.match.params.swtcode, //swtool 정보 swtool 코드
swtcode: '', //swtool 저장 swtool 코드
swt_toolname: '', //swtool 정보 sw툴 명
swt_demo_site: '',//swtool 정보 데모사이트
swt_github_url: '',//swtool 정보 깃허브 주소
swt_comments: '',//swtool 정보 설명
swt_function: '',//swtool 정보 기능
//파일, 이미지 업로드
file: '',//메인 이미지 미리보기용 path
file2: '',//라벨 이미지 미리보기용 path
fileName: '',//메인 이미지명
fileName2: '',//라벨 이미지명
menualName: '',//메뉴얼명
selectedFile: null, //업로드 대상 파일
}
}
componentDidMount () {
// SW Tool 정보 호출
this.callSwToolInfoApi()
if(this.state.before_swtcode == 'register'){
$('.modifyclass').hide()
}else{
$('.saveclass').hide()
}
}
//업로드할 파일 세팅
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('작업중 오류가 발생하였습니다.', error, 'error', '닫기')
}
}).catch(error => {
alert('작업중 오류가 발생하였습니다.', error, 'error', '닫기')
})
}
//이미지 업로드
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
$('#uploadimg').show()
$('#is_MainImg').remove()
$('#uploadimg').remove()
$('#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
$('#uploadimg2').show()
$('#is_LabelImg').remove()
$('#uploadimg2').remove()
$('#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 () => {
//SW Tool List 호출
axios.post('/api/Swtool?type=info', {
is_Swtcode: this.state.before_swtcode,
})
.then( response => {
try {
this.setState({ responseSwtoolInfo: response });
this.setState({ append_SwtoolInfo: this.SwToolInfoAppend() });
$('#is_Swt_toolname').val(this.state.swt_toolname)
$('#is_Swt_demo_site').val(this.state.swt_demo_site)
$('#is_Giturl').val(this.state.swt_github_url)
$('#is_Comments').val(this.state.swt_comments)
$('#is_Swt_function').val(this.state.swt_function)
$('#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+'"}/>')
$('#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+'"}/>')
$('#imagefile').val(this.state.fileName)
$('#imagefile2').val(this.state.fileName2)
$('#manualfile').val(this.state.menualName)
if($('#uploadimg').attr('src').indexOf("null") > -1){
$('#uploadimg').hide()
}
if($('#uploadimg2').attr('src').indexOf("null") > -1){
$('#uploadimg2').hide()
}
} catch (error) {
alert('작업중 오류가 발생하였습니다.')
}
})
.catch( error => {alert('작업중 오류가 발생하였습니다.');return false;} );
}
// SW Tool 정보 append
SwToolInfoAppend = () => {
let result = []
var SwToolInfo = this.state.responseSwtoolInfo.data
if(this.state.before_swtcode != 'register'){
var data = SwToolInfo.json[0]
this.state.swt_toolname = data.swt_toolname
this.state.swt_demo_site = data.swt_demo_site
this.state.swt_github_url = data.swt_github_url
this.state.swt_comments = data.swt_comments
this.state.swt_function = data.swt_function
this.state.file = '/image/'+data.swt_big_imgpath
this.state.fileName = data.swt_big_imgpath
$('#imagefile').val(data.swt_big_imgpath)
this.state.file2 = '/image/'+data.swt_imagepath
this.state.fileName2 = data.swt_imagepath
$('#imagefile2').val(data.swt_imagepath)
this.state.menualName = data.swt_manual_path
}
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'){
this.sweetalertSucc('Software Tools 등록이 완료되었습니다.', false)
}else if(type == "modify"){
this.sweetalertSucc('Software Tools 수정이 완료되었습니다.', false)
}
// 저장 후 리스트페이지로 이동
setTimeout(function() {
this.props.history.push('/AdminSoftwareList');
}.bind(this),1500
);
}else{
alert('작업중 오류가 발생하였습니다.')
}
} catch (error) {
alert('작업중 오류가 발생하였습니다.')
}
}//fnValidate end
};
//alert 기본 함수
sweetalert = (title, contents, icon, confirmButtonText) => {
Swal.fire({
title: title,
text: contents,
icon: icon,
confirmButtonText: confirmButtonText
})
}
//alert 성공 함수
sweetalertSucc = (title, showConfirmButton) => {
Swal.fire({
position: 'bottom-end',
icon: 'success',
title: title,
showConfirmButton: showConfirmButton,
timer: 1000
})
}
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;
https://taling.me/Talent/Detail/19341
'탈잉 강의 자료 > react.js(프론트) + node.js(백앤드) 개발에서 배포까지' 카테고리의 다른 글
2회차_3강 : 생성한 스키마에 한국시간, 한국 인코딩 설정을 하고 더미데이터를 삽입한다. (0) | 2020.02.09 |
---|---|
4회차_3강 : NODE 스케줄러를 개발해, EC2 서버에 배포한다. (0) | 2020.01.28 |
4회차_1강 : EC2 서버 인스턴스를 생성하고, SFTP, SSH 을 사용할 수 있게 세팅한다. (0) | 2020.01.22 |
3회차_3강 : ALERT 디자인을 수정하고, 삭제기능을 구현한다. Source tree를 사용해 git에 소스를 업로드한다. (0) | 2020.01.16 |
3회차_2강 : 수정페이지를 구현한다. (text, 이미지, 파일) (0) | 2020.01.16 |