관리 메뉴

SIMPLE & UNIQUE

2-5강 : 수정 페이지 구현, UPDATE쿼리 실행 본문

탈잉 강의 자료/2020_비전공자도 가능한 웹 프로젝트

2-5강 : 수정 페이지 구현, UPDATE쿼리 실행

착한코딩 2020. 8. 2. 10:10

2_5강 소스.zip
0.86MB
2_5강 소스_덮어쓰기.zip
0.01MB

2_5 목표 : 기존에 등록한 데이터를 수정 페이지 form에 노출한다. 입력 데이터를 변경하고, update쿼리를 실행해 db를 수정한다.

 

1. 등록페이지로 사용하던 react 파일에 수정 기능을 추가한다.

등록페이지와 수정페이지는 같은 컴포넌트를 사용한다. SoftwareView.js 파일에 아래 소스를 붙여넣는다.

state에 수정에 필요한 변수들을 선언했고, 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 = {
            before_swtcode: props.match.params.swtcode,
            selectedFile: null,
        }
    }

    componentDidMount () {
        if(this.state.before_swtcode == 'register'){
            $('.modifyclass').hide()
        }else{
            this.callSwToolInfoApi()
            $('.saveclass').hide()
        }
    }

    callSwToolInfoApi = async () => {
        axios.post('/api/Swtool?type=list', {
            is_Swtcode: this.state.before_swtcode,
        })
        .then( response => {
            try {
                var data = response.data.json[0]
                $('#is_Swt_toolname').val(data.swt_toolname)
                $('#is_Swt_demo_site').val(data.swt_demo_site)
                $('#is_Giturl').val(data.swt_github_url)
                $('#is_Comments').val(data.swt_comments)
                $('#is_Swt_function').val(data.swt_function)
                var manualName = data.swt_manual_path.replace('/swmanual/','')
                var fileName = data.swt_big_imgpath.replace('/image/','')
                var fileName2 = data.swt_imagepath.replace('/image/','')
                $('#upload_img').prepend('<img id="uploadimg" src="'+data.swt_big_imgpath+'"/>')
                $('#upload_img2').prepend('<img id="uploadimg2" src="'+data.swt_imagepath+'"/>')

                $('#imagefile').val(fileName)
                $('#imagefile2').val(fileName2)
                $('#manualfile').val(manualName)

                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;} );
    }

    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) => {
            if(this.Swt_toolname_checker === '') {
                $('#is_Swt_toolname').addClass('border_validate_err');
                alert('툴 이름을 다시 확인해주세요.')
                return false;
            }
            $('#is_Swt_toolname').removeClass('border_validate_err');

            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');

            if(this.Giturl_checker === '') {
                $('#is_Giturl').addClass('border_validate_err');
                alert('Github URL을 다시 확인해주세요.')
                return false;
            }
            $('#is_Giturl').removeClass('border_validate_err');

            if(this.Comments_checker === '') {
                $('#is_Comments').addClass('border_validate_err');
                alert('설명을 다시 확인해주세요.')
                return false;
            }
            $('#is_Comments').removeClass('border_validate_err');

            if(this.Swt_function_checker === '') {
                $('#is_Swt_function').addClass('border_validate_err');
                alert('상세기능을 다시 확인해주세요.')
                return false;
            }
            $('#is_Swt_function').removeClass('border_validate_err');
            return true;
        }

        if(this.fnValidate()){
            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('Software Tools 수정이 완료되었습니다.')
                    }
                    setTimeout(function() {
                        this.props.history.push('/SoftwareList');
                        }.bind(this),1500
                    );
                }else{
                    alert('작업중 오류가 발생하였습니다.')
                }  
            } catch (error) {
                alert('작업중 오류가 발생하였습니다.')
            }
        }
    };

    handleFileInput(type, e){
        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],
        })
        setTimeout(function() {
            if(type =='manual'){
                this.handlePostMenual()
            }else{
                this.handlePostImage(type)
            }
        }.bind(this),1
        );
    }

    handlePostMenual(){
        const formData = new FormData();
        formData.append('file', this.state.selectedFile);
        return axios.post("/api/upload?type=uploads/swmanual/", formData).then(res => {
            this.setState({menualName : res.data.filename})
            $('#is_MenualName').remove()
            $('#upload_menual').prepend('<input id="is_MenualName" type="hidden"'
            +'name="is_MenualName" value="/swmanual/'+this.state.menualName+'"}/>')
        }).catch(error => {
            alert('작업중 오류가 발생하였습니다.', error, 'error', '닫기')
        })
    }    

    handlePostImage(type){
        const formData = new FormData();
        formData.append('file', this.state.selectedFile);
        return axios.post("/api/upload?type=uploads/image/", formData).then(res => {
            if(type =='file'){
                this.setState({fileName : res.data.filename})
                $('#is_MainImg').remove()
                $('#uploadimg').remove()
                $('#upload_img').prepend('<img id="uploadimg" src="/image/'
                +this.state.fileName+'"/>')
                $('#upload_img').prepend('<input id="is_MainImg" type="hidden"'
                +'name="is_MainImg" value="/image/'+this.state.fileName+'"}/>')
            }else if(type =='file2'){
                this.setState({fileName2 : res.data.filename})
                $('#is_LabelImg').remove()
                $('#uploadimg2').remove()
                $('#upload_img2').prepend('<img id="uploadimg2" src="/image/'
                +this.state.fileName2+'"/>')
                $('#upload_img2').prepend('<input id="is_LabelImg" type="hidden"'
                +'name="is_LabelImg" value="/image/'+this.state.fileName2+'"}/>')
            }
        }).catch(error => {
            alert('작업중 오류가 발생하였습니다.')            
        })
    }

    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_Swtcode" type="hidden" name="is_Swtcode" />
                            <input id="is_Email" type="hidden" name="is_Email" value="guest" />
                            <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">
                                    <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>
                                    <div class="btn_confirm mt20" style={{"margin-bottom": "44px"}}>
                                        <Link to={'/SoftwareList'} 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;

2. 수정된 데이터를 db에 update할 수 있도록, node api를 구현한다. 

수정된 페이지에 기존에 등록된 데이터를 불러오는 api는 기존 리스트페이지에서 사용하던 것과 동일하다.

수정 후 변경된 데이터를 db에 update하도록, 라우터와 쿼리를 추가한다. 아래 코드에서 type이 list로 분기된 코드가, 리스트페이지와 수정페이지에서 초기데이터를 불러올 때 사용하는 부분이다.

var express = require('express');

var router = express.Router();
const bodyParser = require('body-parser');

router.use(bodyParser.urlencoded({ extended: true }));
router.use(bodyParser.json());

router.post('/', (req, res, next) => {
    var type = req.query.type;
    if(type == '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(type == '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(type == '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);      
      }
    }
});

module.exports = router;

SwToolsMapper.xml 파일을 아래와 같이 수정한다. 수정페이지에 초기 데이터를 가져오는 쿼리는 selectSwToolsList인데, 리스트 페이지에서와 달리 is_Swtcode파라미터가 전달된다. is_Swtcode가 있을 경우 일치하는 1개의 데이터만 조회한다. update쿼리 updateSwToolsInfo는 insert문에서 update문으로 바뀌었을 뿐, 사용한 파리미터와 컬럼은 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 react.react_swtool
      <if test="is_Swtcode != null && is_Swtcode != ''">
        WHERE swt_code = #{is_Swtcode}
      </if>
      ORDER BY update_date DESC
  </select>

  <insert id="insertSwToolsInfo"> 
    INSERT INTO react.react_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 (
      CONCAT('USW', DATE_FORMAT(now(), '%Y%m%d%H%i%s'))
      , #{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>

  <update id="updateSwToolsInfo"> 
    UPDATE react.react_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>
</mapper>

3. 수정이 정상적으로 동작하는지 확인한다.

상세페이지에서 데이터 수정 후, 리스트로 돌아와 다시 상세페이지로 진입하면 수정된 데이터를 확인할 수 있다.

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

 

[비전공자도 가능한 웹 프로젝트] 개발부터 배포까지, 배우면서 완성(react,nodejs,mysql,aws)! | 탈잉

✔ 수업 목표 & 5주 뒤 결과물 만들고 싶은 서비스를 직접 구현하게 됩니다. 사람들이 방문하고 액션을 할 수 있는 웹 페이지를 제작 & 배포하는 것까지 진행됩니다. 여러분이 개발 경험이 있든 ��

taling.me:443

강의 문의 : ljung5@naver.com

Comments