관리 메뉴

SIMPLE & UNIQUE

3회차_3강 : ALERT 디자인을 수정하고, 삭제기능을 구현한다. Source tree를 사용해 git에 소스를 업로드한다. 본문

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

3회차_3강 : ALERT 디자인을 수정하고, 삭제기능을 구현한다. Source tree를 사용해 git에 소스를 업로드한다.

착한코딩 2020. 1. 16. 01:41

3_3 목표 : 사이트 레이아웃에 어울리는 alert 디자인을 적용하고, 리스트페이지에서 삭제 기능을 구현한다. git 서버에 업로드하지 말아야 하는 파일들을 이해한다.

 

1. sweetalert2 패키지를 설치해 alert 디자인을 수정한다.

react 경로 C:\Users\ljung\OneDrive\문서\taling0102\client 에서 아래 명령어로 패키지를 설치한다.

npm install --save sweetalert2

참고 : https://sweetalert2.github.io/#native_link#

 

SweetAlert2

A beautiful, responsive, customizable and accessible (WAI-ARIA) replacement for JavaScript's popup boxes

sweetalert2.github.io

AdminSoftwareView.js 파일을 아래와 같이 수정한다. 수정이나 저장 버튼을 눌렀을때 하단에
알림창(sweetalertSucc)이 뜨도록 수정했다. (sweetalert으로 검색하면 나오는 부분). 
기본함수인 sweetalert()은 일반적인 스크립트 알림함수인 alert()과 동일하게 사용하면 된다.

 

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() {
                        // datasource 정보 호출
                        window.location.href = 'http://localhost:3000/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;

아래와 같이 알림창이 노출될 것이다.

알림창이 노출되는 시간이 1초가 넘어서, 리스트로 redirect 되는 시간을 timeout 1.5초를 걸어두었다.

2. tool 리스트 페이지에서 삭제버튼에서 호출할 함수를 추가한다.

AdminSoftwareList.js 파일을 아래와 같이 수정한다. 삭제 버튼을 누르면, 삭제할 tool 코드를 node api로 전달하는 코드가 추가되었다. 1. 에서 추가한 sweetalert으로 삭제 alert을 구현했다. 삭제가 완료되면 callSwToolListApi() 를 호출해 삭제 이후의 리스트를 불러온다.

import React, { Component } from 'react';
import {Link} from 'react-router-dom';
import axios from "axios";
import Swal from 'sweetalert2'

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
    }

    // 리스트에서 툴삭제
    deleteSwtool = (e) => {
        //삭제 권한 부여
        var event_target = e.target
        var tmp_this = this
        this.sweetalertDelete('정말 삭제하시겠습니까?', function() {
            axios.post('/api/Swtool?type=delbefore', {
                is_SwtCd : event_target.getAttribute('id')
            })
            .then( response => {
                tmp_this.callSwToolListApi()
            })
        })
    }

    //alert 삭제 함수
    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 () {
        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;

SwtoolModule.js에 삭제 모듈을 추가하고, delete 쿼리가 있는 mapper id를 연결해준다.

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

module.exports = router;

SwToolsMapper.xml에 tool 삭제시에 실행될 delete 쿼리를 추가한다.

<?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>
  
  <select id="selectSwToolsInfo">
      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
      <if test="is_Swtcode != null && is_Swtcode != ''">
        WHERE swt_code = #{is_Swtcode}
      </if>
  </select>
  
  <update id="updateSwToolsInfo"> 
    UPDATE rtrod.rtrod_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 rtrod.rtrod_swtool
    WHERE swt_code = #{is_SwtCd}
  </delete>

</mapper>

 파일을 추가하고 삭제 버튼을 누르면 아래와 같은 단계로 alert이 노출되고, 삭제한 데이터가 반영되어 리스트에 노출된다.

 

3. Source tree를 설치 후, gitignore 파일을 설정하고 소스를 업로드한다.

다운로드 경로 : https://www.sourcetreeapp.com/
설치 방법 참고  : https://jintan.tistory.com/47

 

react, node 코드를 git 레파지토리에 업로드 때 주의사항으로는

gitignore 파일을 꼭 설정한 후에, 파일들을 올려야한다.

 

아래 두 경로에는 npm 패키지들이 설치되는 경로이다.

개발을 진행할수록 로컬 환경에 따라 패키지 파일들이 조금씩 변하는데,

ignore파일에 추가하지 않는다면 이때마다 수백개가 넘은 파일들의 변경점이 추적될 것이다.

그리고 package.json에 필요한 패키지들이 다 적혀있기 때문에, node경로와 react 경로에서 npm install 한 번씩 해주면

package.json파일을 참조해서 웬만한 패키지들은 설치된다.

# Dependency directories
node_modules/
client/node_modules/

추가로 비밀키, AWS 키, DB 연결정보 등 보안상의 이유로 노출되면 안되는 정보가 담긴 파일들을 추가해준다.

 

3-1) github.com 에 접속해 Repository를 생성한다.

3-2)  gitignore 파일을 생성한다.

.gitignore 글자를 클릭한다.

# Dependency directories
node_modules/
client/node_modules/

추적하지 않을 폴더 경로를 작성하고 Commit한다.

Code 탭을 선택하면 아래와 같이 .gitignore 파일이 추가된 것을 확인할 수 있다.

3-3)  Source tree를 실행해 로컬 Repository를 생성한다.

 

로컬 경로(C:\Users\ljung\OneDrive\문서) 사용할 폴더(taling_github2)를 생성하고, 아래와 같이 원격과 로컬 repository 주소를 입력한다.

[클론] 버튼을 누르면 아래 화면과 같이 github.com에서 커밋한  .gitignore 파일만 추적되는 것을 확인할 수 있다.

3-4) git으로 관리할 폴더/파일들을 세팅한다.

왼쪽 : 새로 생성된 repository / 오른쪽 : 기존 workspace

 

repository의 .gitignore 폴더 안에있던 .gitignore 파일을 repository경로로 꺼내고, .gitignore폴더는 삭제한다.

기존 workspace에 있는 폴더, 파일들을 이동시키는데 node_modules 폴더는 옮기지 않는다.

(client 폴더 안에 node_modules도 옮기지 않는다)

 

cmd 창을 열어 node 경로에서 npm install 명령어를

                    react 경로(../client)에서  yarn install 명령어를 실행한다.

// node 경로에서
npm install
// react 경로에서
yarn install

 

install을 마치면 node경로와 react경로에 node_modules 폴더가 생성된다.

3-5) Source tree에서 새로 추가된 파일들을 commit > push 한다.

다시 Source tree로 돌아와서 커밋하지 않는 변경사항을 보면, Repository에 추가된 파일들을 확인할 수 있다.

이때 .gitignore 파일에 작성한 /node_modules 경로와 하위 파일들을 추적되지 않아야 한다.

 

새로 추가된 폴더/파일들을 스테이지에 올려 Commit 한다.

로컬에서 Commit 된 파일들을 원격경로에 Push한다.

github.com 에서 repository를 확인해보면, push한 파일들 리스트를 확인할 수 있다.

 

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

 

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

✔ 수업 목표 ⊙ Font-end(react), back-end(node), mysql 구조를 프레임워크화 한다. ⊙ SELECT, UPDATE, DELETE, INSERT를 각각 1세트씩 구현한다. (CRUD 세트를 참고해서 만들고 싶은 기능을 혼자 개발할 수 있어요) ⊙ AWS EC2 인스턴스를 생성하고, 서버에 접근하여 파일을 컨트롤 할 수 있다. ⊙ AWS RDS(mysql) 인스턴스를 생성하고, 필요한 테이블들을 관리할 수 있다. ✔ 제 수업만의

taling.me

Comments