ABOUT ME

Today
Yesterday
Total
  • [Node & MariaDB] 개발자 지망생 스터디 - 18일차
    스터디/KAKAOCLOUDSCHOOL 2022. 11. 25. 20:54

    17일차 실습 이어서..

     


     

    더보기

    Node.js에서 SQL을 이용해서 Select 구문을 실행했을 때 결과는 무조건 배열

    Select 구문을 사용하게 되면 행과 열이 몇 개가 나올지 알 수 없음.

    [{컬럼이름:값,컬럼이름:값...}, {}, {}]

     

    SELECT count(*) from goods;

    >> 30

    results : [{count(*):30}]

    results[0].count(*)

    5. 데이터 일부분 가져오기

    1) App.js 파일에 요청을 처리하는 메서드를 구현

    //데이터 일부분 가져오기
    //URL은 /item/list 
    //파라미터는 pageno 1개 인데 없으면 1로 설정
    app.get('/item/list', (req, res) => {
        //파라미터 읽어오기
        let pageno = req.query.pageno;
        if(pageno == undefined){
            pageno = 1;
        }
        console.log(pageno);
        //브라우저에서 테스트 - 콘솔 확인
        //localhost:9000/item/list
        //localhost:9000/item/list?pageno=3
    
        //item 테이블에서 itemid 를 가지고 내림차순 정렬해서 
        //페이지 단위로 데이터 가져오기
        //select * from item order by itemid desc limit 시작번호, 5
        //시작번호=(pageno-1)*5
    
        //파라미터는 무조건 문자열입니다.
        //파라미터를 가지고 산술연산을 할 때는 숫자로 변환을 수행
        
        //성공 과 실패 여부를 저장
        let result = true;
        //성공했을 때 데이터를 저장
        let list;
        //데이터 목록 가져오기
        connection.query(
            "select * from goods order by itemid desc limit ?, 5", 
            [(parseInt(pageno)-1)*5], (err, results, fields) => {
                if(err){
                    console.log(err);
                    result = false;
                }else{
                    list = results;
                    //console.log(list);
                }
                
                //goods 테이블의 전체 데이터 개수를 가져오기
                let cnt = 0;
                connection.query("select count(*) cnt from goods",
                    [], (err, results, fields)=>{
                    if(err){
                        //에러가 발생했을 때
                        console.log(err);
                        result = false;
                    }else{
                        //정상적으로 구문이 실행되었을 때
                        //하나의 행만 리턴되므로 0 번째 데이터를 읽어내면 됩니다.
                        cnt = results[0].cnt;
                    }
    
                    //응답 생성해서 전송
                    if(result === false){
                        res.json({"result":false});
                    }else{
                        res.json({"result":true, "list":list, "count":cnt});
                    }
            });
        });
    });

     

     

    2) index.html 파일에 페이지 단위 보기 구현

    • body부분에 페이지 단위 전체 데이터를 가져올 버튼을 생성
    <a href="#" id="listbtn">페이지 단위 전체 데이터 가져오기</a><br/>
    • 스크립트 코드 추가
            let listbtn = document.getElementById("listbtn");
                //현재 페이지 번호를 저장할 변수를 선언
                let pageno = 1;
                listbtn.addEventListener("click", (e) => {
                    let request = new XMLHttpRequest();
                    request.open('GET', '/item/list?pageno=' + pageno);
                    request.send('');
                    request.addEventListener('load', () => {
                        //출력 영역을 초기화
                        content.innerHTML = '';
                        //데이터를 파싱
                        let data = JSON.parse(request.responseText);
                        if(data.result === true){
                            //데이터 개수 와 목록을 가져오기
                            let count = data.count;
                            let list = data.list;
    
                            //출력 내용 만들기
                            let display = "<div align='center' class='body'>";
                            display += "<h2>상품 목록</h2>";
                            display += "<table border='1' id='tbldata'>";
                            //전체 데이터 개수 출력
                            display += "<tr><td colspan='3' align='right'>";
                            display += "전체 데이터 개수:" + count + "개</td></tr>"
                            
                            //항목 헤더 출력
                            display += "<tr class='header'>";
                            display += "<th width='80'>ID</th>";
                            display += "<th width='320'>상품명</th>";
                            display += "<th width='100'>가격</th>";
                            display += "</tr>";
    
                            //데이터 목록 출력
                            for(item of list){
                                display += "<tr class='record'>";
                                display += "<td align='center'>" 
                                    + item.itemid + "</td>";
                                display += "<td align='left'>" 
                                    + item.itemname + "</td>";
                                display += "<td align='right'>" 
                                    + item.price + " 원</td>";
                                display += "</tr>";
                            }
                            display += "</table></div>";
    
                            //더보기 구현
                            //현재 페이지가 마지막 페이지가 아닌 경우만 출력
                            if((pageno-1) * 5 < count){
                                display += "<table align='center'";
                                display += " width='500' id='tblbtn'>";
                                display += "<tr><td align='center' colspan='3'>";
                                display += "<span id='addbtn'><a href='#'>더보기</a></span></td>";
                                display += "</tr></table>";
                            }
                            content.innerHTML = display;
    
                            //더보기 버튼을 눌렀을 때 처리
                            let addbtn = document.getElementById("addbtn");
    
                            //addbtn이 존재하는 경우에만 수행
                            if(addbtn != undefined){
                                addbtn.addEventListener('click', (e) => {
                                    pageno = pageno + 1;
                                    let request = new XMLHttpRequest();
                                    request.open('GET', '/item/list?pageno=' + pageno);
                                    request.send('');
                                    //전체 데이터 개수보다 더 많이 출력하면 더보기 영역을 삭제
                                    if((pageno) * 5 >= data.count){
                                        pageno = pageno - 1;
                                        document.getElementById("tblbtn").remove();
                                    }
    
                                    //데이터를 가져오면
                                    request.addEventListener('load', ()=>{
                                        let data = JSON.parse(request.responseText);
                                        let list = data.list;
                                        //데이터 테이블 출력
                                        const table = document.getElementById('tbldata');
                                        let display = "";
                                        for(item of list){
                                            display += "<tr class='record'>";
                                            display += "<td align='center'>" + item.itemid +
                                                "</td>";
                                            display += "<td align='left'>" + item.itemname +
                                                "</td>";
                                            display += "<td align='right'>" + item.price +
                                                " 원</td>";
                                            display += "</tr>";
                                        }
                                        table.innerHTML += display;
                                    })
                                });
                            }
    
    
                        }else{
                            content.innerHTML = 
                                '<p>데이터를 가져오는데 실패</p>';
                        }
                    });
                });

    6. 데이터 상세보기

    • 데이터 한개의 정보를 전부 가져와서 출력
    • 테이블에서 데이터 1개를 가져오는 방법은 기본키나 unique 속성을 이용한 조회만 가능
    • 기본키나 unique 속성의 값을 받아서 서버에서 처리
    • itemid를 URL에 포함시켜 받아서 처리하는 구조로 구현

    1)App.js 파일에 상세보기를 위한 코드를 추가

    //상세보기 처리를 위한 코드
    app.get('/item/detail/:itemid', (req, res) => {
        //파라미터 읽기
        let itemid = req.params.itemid;
        //itemid를 이용해서 1개의 데이터를 찾아오는 SQL을 실행
        connection.query("select * from goods where itemid=?",
         [itemid], (err, results, fields) => {
            if(err){
                console.log(err);
                res.json({"result":false});
            }else{
                console.log(results);
                res.json({"result":true, "item":results[0]});
            }
        });
    });
    • 테스트는 localhost:9000/item/detail/번호

    2)index.html 파일에서 상세보기를 위한 코드를 작성

    • itemname을 출력하는 코드를 수정 
    display += "<td align='left'>" + "<a href='#' id='item" + item.itemid + "'>" + item.itemname + "</a></td>";
    • itemname을 클릭했을 때 동작할 스크립트 코드를 작성
          //itemname을 눌렀을 때 수행할 코드
                //링크 하나 하나 이벤트 처리를 하는 것은 자원 낭비
                //부모에 이벤트 처리 코드를 작성
                content.addEventListener('click', (e)=>{
                    //클릭한 대상의 id가 item 으로 시작하는 경우만 동작
                    if(e.target.id.startsWith('item')){
                        //클릭한 대상의 아이디에서 item을 제외한 부분 - itemid
                        let itemid = e.target.id.substring(4).trim();
                        //alert(itemid);
                        let request = new XMLHttpRequest();
                        request.open('GET', '/item/detail/' + itemid);
                        request.send('');
                        request.addEventListener('load', () => {
                            let data = JSON.parse(request.responseText);
                            if(data.result == true){
                                //데이터 가져오기
                                let item = data.item;
                                //출력 내용 생성
                                let display = "<div align='center' class='body'>";
                                display += '<h2>상세보기</h2>';
                                display += '<table>';
                                display += "<tr><td><img src='/img/" 
                                    + item.pictureurl + "'/><td>";
                                
                                display += "<td align='center'><table>";
                                
                                display += "<tr height='50'><td width='80'>상품명</td>";
                                display += "<td width='160'>" + item.itemname + "</td>";
                                display += "<tr height='50'><td width='80'>가격</td>";
                                display += "<td width='160'>" + item.price + " 원</td>";
                                display += "<tr height='50'><td width='80'>비고</td>";
                                display += "<td width='160'>" + item.description + 
                                    "</td></tr>";
                                
                                display += "</table></td></tr></table>";
    
                                
                                content.innerHTML = display;
                            }
                        });
                    }
                });

    7. 첨부 파일 (이미지) 다운로드

    • 웹 브라우저에서는 파일에 링크가 걸려있으면 자신이 출력할 수 있는 파일은 출력을 하고 출력할 수 없는 파일은 다운로드를 수행

    1) 이미지 파일 출력하는 부분을 수정

    display += "<tr><td><a href='/img/" + item.pictureurl + "'>"  + "<img src='/img/" + item.pictureurl + "'/></a><td>";

    2)App.js 파일에 /img/이미지파일명 을 처리하는 코드를 추가

     

     

    //이미지 다운로드 처리
    app.get('/img/:pictureurl', (req, res) => {
        let pictureurl = req.params.pictureurl;
        //이미지 파일의 절대경로를 생성
        let file = 
        "C:\Users\Administrator\Documents\node\mariadb\public\img" 
            + "/" + pictureurl; 
        console.log(__dirname);
        //파일 이름을 가지고 타입을 생성
        let mimetype = mime.lookup(pictureurl);
        res.setHeader('Content-disposition', 
            'attachment; filename=' + pictureurl);
        res.setHeader('Content-type', mimetype);
        //파일의 내용을 읽어서 res에 전송
        let filestream = fs.createReadStream(file);
        filestream.pipe(res);
    })

     

    8. API 테스트를 위한 postman 프로그램 다운로드 및 설치

    • 홈페이지 또는 맥의 경우 Brew를 통해 설치 가능함 (설치 방법은 구글을 이용해 쉽게 찾을수 있음)

     

    9. 데이터 삽입

    • 데이터 삽입은 삽입 화면을 먼저 출력하고 데이터를 입력받은 후 데이터를 서버에게 전송하면 처리
    • 데이터 삽입은 get 방식이 post 방식으로 처리
      • post 방식은 화면을 만들지 않고 웹브라우저에서 테스트가 불가능
      • 서버에 post 방식 요청을 만들고 테스트를 할 때는 별도의 프로그램을 설치해서 하게 됨

    1)App.js 파일에 데이터 삽입을 위한 코드를 작성

    • 어떤 데이터를 받아야 하는지 결정

    itemid: 가장 큰 itemid를 찾아서 +1
    itemname, price, description, pictureurl(파일) 은 직접 입력
    updatedate는 현재 날짜를 문자열로 입력

    • 삽입, 삭제, 갱신 작업이 발생하면 updatedate.txt 파일에 발생한 시간을 기록
      • 현재 데이터가 업데이트 된 시간을 알기 위해서 기록
    • App.js 파일에 현재 날짜를 문자열로 리턴하는 함수 와 현재 날짜 및 시간을 문자열로 리턴하는 함수 작성
    더보기

    1월부터 12월까지 날의 숫자를 배열로 생성

    [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]

    //현재 날짜를 문자열로 리턴하는 함수
    //요즈음 등장하는 자바스크립트 라이브러리들의 샘플 예제는 
    //특별한 경우가 아니면 function 을 사용하지 않습니다.
    const getDate = () => {
        let date = new Date();
        let year = date.getFullYear();
        //월은 +1을 해야 우리가 사용하는 월이 됩니다.
        let month = date.getMonth() + 1;
        let day = date.getDate();
        month = month >= 10 ? month : '0' + month;
        day = day >= 10 ? day : '0' + day;
        return year + "-" + month + "-" + day;
    }
    
    //날짜 와 시간을 리턴하는 함수
    const getTime = () => {
        let date = new Date();
        let hour = date.getHours();
        let minute = date.getMinutes();
        let second = date.getSeconds();
    
        hour >= 10 ? hour : '0' + hour;
        minute >= 10 ? minute : '0' + minute;
        second >= 10 ? second : '0' + second;
    
        return getDate() + " " 
            + hour + ":" + minute + ":" + second;
    }
    • 삽입 요청을 처리하는 함수 추가
    //데이터 삽입을 처리해주는 함수
    app.post('/item/insert', upload.single('pictureurl'), 
        (req, res) => {
        //파라미터 읽어오기
        const itemname = req.body.itemname;
        const description = req.body.description;
        const price = req.body.price;
    
        //파일 이름 - 업로드하는 파일이 없으면 default.png
        let pictureurl;
        if(req.file){
            pictureurl = req.file.filename
        }else{
            pictureurl = 'default.jpg';
        }
    
        //가장 큰 itemid 찾기
        connection.query("select max(itemid) maxid from goods",
        [], (err, results, fields) => {
            let itemid;
            //최대값이 있으면 + 1 하고 없으면 1로 설정
            if(results.length > 0 ){
                itemid = results[0].maxid + 1;
            }else{
                itemid = 1;
            }
    
            //데이터 삽입
            connection.query("insert into goods(" + 
                "itemid, itemname, price, description," 
                + "pictureurl, updatedate) values(?, ?, ?, ?, ?, ?)",
                [itemid, itemname, price, description, pictureurl,
                getDate()], (err, results, fields) => {
                if(err){
                    console.log(err);
                    res.json({"result":false});
                }else{
                    //현재 날짜 및 시간을 update.txt에 기록
                    const writeStream = fs.createWriteStream('./update.txt');
                    writeStream.write(getTime());
                    writeStream.end();
    
                    res.json({"result":true});
                }
            })
        });
    })

    2)index.html 파일에 삽입 요청을 위한 코드를 작성

    • 삽입 요청을 위한 DOM을 추가
    <a href="#" id="insertbtn">데이터 삽입</a><br/>
    • DOM을 클릭했을 때 처리하는 코드를 script에 작성
                let insertbtn = document.getElementById("insertbtn");
                insertbtn.addEventListener('click', (e) => {
                    //삽입화면 출력
                    content.innerHTML = '';
                    let html = 
                        `
                            <div>
                                <p></p>
                                <form id='insertform'
                                enctype='multipart/form-data'
                                method='post'>
                                아이템이름<input type='text'
                                name='itemname' id='itemname'/><br/>
                                가격<input type='text'
                                name='price' id='price'/><br/>
                                설명<input type='text'
                                name='description' id='description'/><br/>
                                이미지<input type='file'
                                name='pictureurl' id='pictureurl'/><br/>
                                <input type='submit' value='삽입'/>
                                </form></div>
                        `
                        content.innerHTML = html;
    
                        //폼안에서 삽입 버튼을 눌렀을 때 처리
                        let f = document.getElementById('insertform');
                        if(f != undefined){
                            f.addEventListener('submit', (e) => {
                                //기본 이벤트 제거
                                e.preventDefault();
                                //폼 데이터 찾아오기
                                const formData = new FormData(
                                    document.getElementById("insertform"));
                                //폼 데이터를 전송
                                let request = new XMLHttpRequest();
                                request.open("POST", "/item/insert", true);
                                request.send(formData);
                                request.addEventListener('load', () => {
                                    let data = JSON.parse(request.responseText);
                                    if(data.result){
                                        //데이터 다시 불러오기
                                        document.getElementById("listbtn").click();
                                    }else{
                                        alert("삽입 실패");
                                    }
                                })
                            })
                        }
                });

     

    10. 데이터 삭제

    • 기본키를 매개변수로 받아서 삭제를 합니다.
    • 삭제는 GET 이나 POST 별 상관 없습니다.

    1)App.js 파일에 itemid를 받아서 데이터를 삭제하는 메서드를 구현

    //데이터를 삭제하는 함수
    app.post('/item/delete', (req, res) => {
        //post 방식으로 전송된 데이터 읽기
        let itemid = req.body.itemid;
    
        //itemid를 받아서 goods 테이블에서 삭제하기
        connection.query("delete from goods where itemid=?",
         [itemid], (err, results, fields)=>{
            if(err){
                console.log(err);
                res.json({"result":false});
            }else{
                //현재 날짜 및 시간을 update.txt에 기록
                const writeStream = fs.createWriteStream(
                    './update.txt');
                writeStream.write(getTime());
                writeStream.end();
    
                res.json({"result":true});
            }
        });
    
    });

    2)index.html 파일에서 삭제를 구현

    • 상세보기 출력하는 부분에 삭제를 위한 DOM을 추가
    //삭제를 위한 DOM을 추가
    display += "<tr>" + "<td colspan='2' align='center' width='240'>"
    + "<a href='#' id='deletebtn'>데이터 삭제</a>" + "</td></tr>";
    • 상세보기를 출력하는 하단에 삭제를 위한 스크립트 코드를 추가
    //데이터 삭제를 눌렀을 때 처리
    let deletebtn = document.getElementById("deletebtn");
    if(deletebtn != undefined){
    	deletebtn.addEventListener('click', (e) => {
    	//폼이 없는 경우의 POST 방식 파라미터 만들기
    	let params = 'itemid=' + item.itemid;
    
    	let request = new XMLHttpRequest();
    	request.open('POST', '/item/delete');
    	//폼이 아닌 경우는 form 형식으로 인코딩
    	request.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
    	request.send(params);
    	request.addEventListener('load', () => {
    		let data = JSON.parse(request.responseText);
    		if(data.result){
    			document.getElementById("listbtn").click();
    			alert("삭제 성공");
    		}else{
    			alert("삭제 실패");
    		}
    	})
    })

     

    11. 데이터 수정

    • 현재 데이터를 수정할 수 있도록 화면에 출력하고 데이터를 입력받아서 수정
      • 텍스트는 입력받은 내용을 수정하면 되는데 이전 내용을 화면에 출력한 상태에서 내용을 고치지 않았어도 set으로 출력하면 되는데 파일은 input 에 설정할 수 없음

    1) 수정 화면을 위한 html 파일을 생성 - public 디렉토리에 update.html

    <form method="post" id="updateform" 
        enctype="multipart/form-data">
        아이템 아이디:<input type="text" name="itemid" id="itemid"/>
        <br/>
        아이템 이름:<input type="text" name="itemname" id="itemname"/>
        <br/>
        아이템 가격:<input type="text" name="price" id="price"/>
        <br/>
        설명:<input type="text" name="description" id="description"/>
        <br/>
        새로운 이미지:<input type="file" name="pictureurl" />
        <br/>
        이전 이미지<img width="100" height="100" id="picture">
        <br/>
        <!-- hidden 은 화면에 출력되지는 않지만 가지고 있어야 하는
        데이터가 있을 때 사용-->
        <input type="hidden" name="oldpictureurl" id="oldpictureurl"/>
        <br />
        <input type="submit" value="수정" />
    </form>

    2)App.js 파일에 수정의 get 요청이 오면 처리할 함수를 작성

    //수정을 get으로 요청했을 때 - 수정 화면으로 이동
    app.get('/item/update', (req, res) => {
        //public 디렉토리의 update.html을 읽어내서 리턴
        fs.readFile('./public/update.html', (err, data)=>{
            res.end(data);
        });
    });

    3)index.html 파일에 수정 화면으로 이동하기 위한 코드를 작성

    • 상세보기를 출력하는 부분에 데이터 수정 DOM을 추가(삭제를 추가한 부분의 위나 아래에 추가)
    //수정을 위한 DOM을 추가
    display += "<tr>" + "<td colspan='2' align='center' width='240'>"+ "<a href='#' id='updatebtn'>데이터 수정</a>" + "</td></tr>";
    • 수정 버튼을 눌렀을 때 처리를 위한 스크립트 코드를 추가
    //데이터 수정을 눌렀을 때 처리
    let updatebtn = document.getElementById("updatebtn");
    if(updatebtn != undefined){
        updatebtn.addEventListener('click', (e) => {
            let request = new XMLHttpRequest();
            request.open('GET', '/item/update');
            request.send('');
            request.addEventListener('load', () => {
                let html = request.responseText;
                content.innerHTML = html;
                //수정은 수정하기 위한 원본 데이터를 화면에 출력
                document.getElementById("itemid").value = 
                    item.itemid;
                document.getElementById("itemid").readOnly = 
                    true;
                document.getElementById("itemname").value = 
                    item.itemname;
                document.getElementById("price").value = 
                    item.price;
                document.getElementById("description").value = 
                    item.description;
                //원본의 이름을 숨김
                document.getElementById("oldpictureurl").value = 
                    item.pictureurl;
                //원본을 다른 방식으로 출력
                document.getElementById("picture").src = 
                    "/img/" + item.pictureurl;
    
                //수정 폼을 찾아오기
                let updateform = document.getElementById("updateform");
                if(updateform != undefined){
                    //폼 안의 submit 버튼을 눌렀을 때
                    updateform.addEventListener('submit', (e) => {
                        //기본 이벤트 제거
                        e.preventDefault();
    
                        //전송할 데이터 생성 - 폼 안에 입력한 데이터 생성
                        const formData = new FormData(updateform);
    
                        //서버에게 요청
                        let request = new XMLHttpRequest();
                        request.open("POST", "/item/update");
                        request.send(formData);
    
                        //응답을 받았을 때 처리
                        request.addEventListener('load', () => {
                            let data = JSON.parse(request.responseText);
                            if(data.result === true){
                                document.getElementById("listbtn").click();
                                alert("성공");
                            }else{
                                alert("실패");
                            }
                        });
                    })
                }
            })
        })
    }

    4) App.js 파일에 실제 수정을 처리하는 코드를 작성

    app.post('/item/update', upload.single('pictureurl'), (req, res) => {
       //파라미터 가져오기
       const itemid = req.body.itemid;
       const itemname = req.body.itemname;
       const price = req.body.price;
       const description = req.body.description;
       //예전 파일 이름
       const oldpictureurl = req.body.oldpictureurl;
    
       //수정할 파일 이름 만들기
       let pictureurl;
       //새로 선택한 파일이 있다면
       if(req.file){
            pictureurl = req.file.filename;
       }else{
            pictureurl = oldpictureurl;
       }
       //데이터베이스 작업
       connection.query("update goods set itemname=?, price=?," +  
        "description=?, pictureurl=?, updatedate=? where itemid=?",
         [itemname, price, description, pictureurl, getDate(),
        itemid], 
            (error, results, fields) => {
            //console.log(results);
            //console.log(fields);
            if(error){
                //에러가 발생한 경우
                console.log(error);
                res.json({"result": false});
            }else{
                //성공했을 때 처리
                const writeStream = 
                    fs.createWriteStream("./update.txt");
                writeStream.write(getTime());
                writeStream.end();
                res.json({"result": true});
            }
       })
    });
Designed by Tistory.