관리 메뉴

SIMPLE & UNIQUE

2회차_2강 : AWS 가입, RDS mysql 인스턴스를 구동하고 스키마와 테이블을 생성한다. 본문

탈잉 강의 자료/react.js(프론트) + node.js(백앤드) 개발에서 배포까지

2회차_2강 : AWS 가입, RDS mysql 인스턴스를 구동하고 스키마와 테이블을 생성한다.

착한코딩 2020. 1. 7. 22:13

2_2 목표 : AWS가입(결제카드입력), RDS t2.micro(1년 프리티어) 인스턴스를 세팅하고, workbench에서 필요한 테이블 생성과 더미데이터 삽입을 한다. 등록페이지를 구현한다. 

 

2회차 2강에는 각자 AWS 계정을 만들고 RDS mysql 서버를 프리티어(1년무료) 버전으로 인스턴스를 생성한다. 그리고 테이블을 생성해 2_1강에서 설저한 node 소스에 host url와 계정 정보를 바꿔준다. 정상적으로 DB연결이 되는지 확인한다.

 

1. AWS 계정 생성은 aws 공식 홈페이지 가이드를 참고한다.

  아래 링크를 참고해 가입하고, 결제수단을 등록한다.

https://aws.amazon.com/ko/premiumsupport/knowledge-center/create-and-activate-aws-account/

 

AWS 계정 생성 및 활성화

계정 생성 Amazon Web Services 홈 페이지로 이동합니다. [AWS 계정 생성]을 선택합니다. 참고: 최근에 AWS에 로그인한 경우 버튼에 [콘솔에 로그인]이라고 표시될 수 있습니다. 계정 정보를 입력한 다음 계속을 선택합니다. 중요: 사용자 계정 정보, 특히 이메일 주소를 올바르게 입력했는지 확인하십시오. 이메일 주소를 잘못 입력할 경우 계정에 액세스할 수 없습니다. AWS 계정 새로 만들기가 표시되지 않으면 우선 다른 계정에 로그인을 선택

aws.amazon.com

2. RDS 인스턴스를 생성한다.

  아래 링크를 참고해 생성한다. 오프라인으로 구체적인 가이드 예정.

https://docs.aws.amazon.com/ko_kr/AmazonRDS/latest/UserGuide/CHAP_Tutorials.WebServerDB.CreateDBInstance.html

 

## 참고 ##

  퍼블릭 액세스를 허용해야 외부 서비스에서 RDS 서버에 접속할 수 있다.

## 참고 ##

  보안 그룹에서 사용할 포트를 열어준다. (인바운드 포트)

 

 

 

 

3. mysql을 설치한다.

  아래 링크에서 다운로드한다. 로컬에서 mysql을 사용하지 않고, AWS와 같은 외부서버에 접속해서만 사용할 경우 only client로 설치한다. 용량이 작다. workbench를 설치에서 제외하지 않는다.

https://dev.mysql.com/downloads/installer/

 

MySQL :: Download MySQL Installer

Select Operating System: Select Operating System… Microsoft Windows Select OS Version: All Windows (x86, 32-bit) Windows (x86, 32-bit), MSI Installer 8.0.18 18.6M (mysql-installer-web-community-8.0.18.0.msi) MD5: c509966c1033462027a009cc51a98c74 | Signatur

dev.mysql.com

설치방법 참고

https://help.iwinv.kr/manual/read.html?idx=349

 

 

설치 후, taling이라는 스키마를 생성하고 아래 쿼리를 참고해 테이블을 생성한다.

use rtrod;

CREATE TABLE `rtrod_swtool` (
  `swt_code` varchar(100) NOT NULL COMMENT ' SW툴 코드',
  `swt_toolname` varchar(100) DEFAULT NULL COMMENT ' SW툴 명',
  `swt_function` text COMMENT '기능 요약',
  `swt_imagepath` varchar(100) DEFAULT NULL COMMENT ' 이미지경로',
  `swt_big_imgpath` varchar(100) DEFAULT NULL COMMENT '큰 이미지경로',
  `swt_comments` text COMMENT '설명',
  `swt_demo_site` varchar(100) DEFAULT NULL COMMENT '데모사이트',
  `swt_manual_path` varchar(100) DEFAULT NULL COMMENT '메뉴얼 파일 경로',
  `swt_github_url` varchar(100) DEFAULT NULL COMMENT '깃허브 URL',
  `reg_date` varchar(100) DEFAULT NULL COMMENT ' 등록날짜',
  `reg_user` varchar(100) DEFAULT NULL COMMENT ' 등록자',
  `update_date` varchar(100) DEFAULT NULL COMMENT ' 수정날짜',
  `update_user` varchar(100) DEFAULT NULL COMMENT ' 수정자',
  PRIMARY KEY (`swt_code`)
);

INSERT INTO `rtrod_swtool` VALUES ('USW20191016154820','ICARUS WINGS',' 코호트 특성 분석 및 장기 관찰 연구,  예측 모델 개발.','20191030152436_이카루스 라벨이미지.png','20191030152433_이카루스 메인이미지.png','Immune/Inflammatory Disease Common Data Model Augmentation for Research Union System (ICARUS) Web-based Infographic Service (WINGS), 다중 코호트 특성 분석 및 장기 관찰 연구, 그리고 예측 모델 개발을 통한 중증 난치성 면역 질환의 기저-진행-예측의 전반적인 프로세스 분석/시각화 툴.','http://github.com/ABMI/ICARUSviewer','20191030152430_ICARUS및 ICARUSWINGS 설명.docx','http://github.com/ABMI/ICARUSviewer','20191016155224','admin','20191224083918','')
,('USW20191018102833','AEGIS','질병 위험도 및 군집 지도 시각화','20191030152400_aegis 라벨이미지.png','20191030152356_aegis 메인이미지.png','Application for Epidemiological Geographic Information System, 코호트 기반의 공간 역학 모델링 및 지리정보 시각화 도구','https://github.com/ohdsi/aegis','20191030152345_ICARUS및 ICARUSWINGS 설명.docx','https://github.com/ohdsi/aegis','20191018102819','ljung99@naver.com','20191030062402','admin')
,('USW20191018103243','ATLAS','코호트 생성, 예측 분석 등을 지원','20191030152419_th_i1.png','20191030152416_sw_icn.png','OHDSI의 CDM 기반 분석 포털로 코호트 생성, 예측 분석 등을 지원하는 분석 도구 ','https://github.com/OHDSI/Atlas','20191030152413_ATLAS_manual.xlsx','https://github.com/OHDSI/Atlas','20191018103232','ljung99@naver.com','20191030062421','admin');

select * from rtrod.rtrod_swtool;
delete from rtrod_swtool;

-- delete 안된다면 safy모드 해제
SET SQL_SAFE_UPDATES = 0;

-- insert 할때 한글 깨짐 방지
ALTER TABLE rtrod.rtrod_swtool convert to charset utf8;

-- 테이블 구조조회
DESC rtrod_swtool;
-- 테이블 인텍스 추가1
CREATE INDEX IDX_rtrod_swtool1 ON rtrod_swtool 
(swt_toolname);

CREATE INDEX IDX_rtrod_swtool2 ON rtrod_swtool 
(reg_user, reg_date);

-- 유일 인덱스 추가
ALTER TABLE rtrod_swtool ADD UNIQUE INDEX (update_date);
-- 인덱스삭제
DROP INDEX IDX_rtrod_swtool ON rtrod_swtool ;
DROP INDEX update_date ON rtrod_swtool ;

-- 테이블 인텍스 조회
SHOW INDEX FROM rtrod_swtool;
-- 테이블 comment까지 조회
SHOW FULL COLUMNS FROM rtrod_swtool;

-- 전체 테이블 comment 조회
SELECT TABLE_SCHEMA,TABLE_NAME,AUTO_INCREMENT,TABLE_COMMENT
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME LIKE '%rtrod%'
AND TABLE_SCHEMA LIKE '%rtrod%';

-- 전체테이블 컬럼 조회
SELECT 
  A.TABLE_SCHEMA, 
  A.TABLE_NAME, 
  A.COLUMN_NAME, 
  CONCAT(A.DATA_TYPE,'(',IFNULL(CHARACTER_MAXIMUM_LENGTH,IFNULL(NUMERIC_PRECISION,'')),')') TYPE, 
  A.IS_NULLABLE,
  B.TABLE_COMMENT,
  A.COLUMN_COMMENT
FROM INFORMATION_SCHEMA.COLUMNS A
INNER JOIN INFORMATION_SCHEMA.TABLES B
ON(A.TABLE_NAME = B.TABLE_NAME)
WHERE A.TABLE_SCHEMA NOT IN ('information_schema', 'performance_schema', 'isb_stg', 'isb_prd')
AND A.TABLE_SCHEMA LIKE '%rtrod%'  
ORDER BY A.TABLE_SCHEMA, A.TABLE_NAME, ORDINAL_POSITION;

dbconnect_Module.js 에서 아래 코드 부분을 3_3강에서 생성한 DB서버 정보로 교체한다.

// Connection Pool 세팅
const pool  = mysql.createPool({
  connectionLimit: 66,
  waitForConnections: true,
  host: "database-1.c9k비밀2.rds.amazonaws.com",
  port: "3306",
  database: 'rtrod',
  user: "rtrod_user",
  password: "ajo비밀5",
});

교체된 테이블 데이터로 페이지가 노출된다. 

4. tools 등록페이지에서 입력한 데이터가 DB에 삽입될 수 있게, 저장함수를 추가한다. (파일, 이미지 업로드 제외)

 

cmd 또는 터미널을 열고 react 경로 C:\Users\ljung\OneDrive\문서\taling0102/client에서 아래 명령어로 jquery를 설치해 준다. 등록페이지에서 폼에 입력한 값들을 가공하거나 파라미터로 넘길때 jquery를 사용할 것이다.

//react 경로로 이동
CD C:\Users\ljung\OneDrive\문서\taling0102\client

npm install --save jquery

AdminSoftwareView에 아래 소스를 붙여넣는다.
기존에 소스에서 submitClick(저장버튼)을 눌렀을 때, 데이터를 insert 하는 node 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 변수
        }
    }
    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
    }

    // 저장 버튼 클릭시 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;

SwtoolModule.js에 아래와 같이 insert 쿼리를 호출하는 mapper명을 작성한다.

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);      
    }
  }else if(m_typ == 'save'){
    //Swtool 관리자 저장
    try {
      // Mysql Api 모듈(CRUD)
      var dbconnect_Module = require('./dbconnect_Module');
  
      //Mysql 쿼리 호출정보 입력
      req.body.mapper = 'SwToolsMapper';//mybatis xml 파일명
      req.body.crud = 'insert';//select, insert, update, delete 중에 입력
      req.body.mapper_id = 'insertSwToolsInfo';
      
      router.use('/', dbconnect_Module);
      next('route')
    } catch (error) {
      console.log("Module > dbconnect error : "+ error);      
    }
  }
});

module.exports = router;

SwToolsMapper.xml 소스에 insert 쿼리를 추가한다.

<?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>

  <insert id="insertSwToolsInfo"> 
    INSERT INTO rtrod.rtrod_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 (
      #{is_Swtcode}
      , #{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>

 소스 저장 후, 등록페이지에서 컬럼 값들을 등록하고 저장버튼을 누른다.

완료 alert 창의 '확인' 버튼을 누르면, 리스트 페이지로 리다이렉트 된다.

등록한 데이터가 정상적으로 insert 된 것을 확인할 수 있다.

cmd 나 터미널에서 수행된 insert 쿼리를 확인할 수 있다.

 

https://taling.me/Talent/Detail/19341

 

[2월/주말] REACT, NODE, MYSQL, AWS 개발부터 배포까지/ 따라하면 완성되는 웹프로젝트. | 탈잉

# 수업의 최종목표 1. Font-end(react) <> back-end(node) <> mysql 구조를 프레임워크화 한다. 2. select, update, delete, insert를 각각 1세트씩 구현한다.(이 세트를 확장해서 하고 싶은 것을 자유롭게 만드시면 됩니다.) 3. AWS EC2 서버에 접근하여 파일을 컨트롤 할 수 있다. 4. AWS RDS(mysql) 인스턴스를 생성하고, 필요한 테이블들을 관리할 수 있다. CRUD 이외에 일반적인

taling.me

 

Comments