-
[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 에 설정할 수 없음
- 텍스트는 입력받은 내용을 수정하면 되는데 이전 내용을 화면에 출력한 상태에서 내용을 고치지 않았어도 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}); } }) });
'스터디 > KAKAOCLOUDSCHOOL' 카테고리의 다른 글
[MongoDB] 개발자 지망생 스터디 - 20일차 (2) 2022.11.29 [Node + JavaScript] 개발자 지망생 스터디 - 19일차 (0) 2022.11.28 [Node & MariaDB] 개발자 지망생 스터티 - 17일차 (0) 2022.11.24 [DB-MariaDB] 개발자 지망생 스터디 - 16일차 (0) 2022.11.23 [DB-MariaDB] 개발자 지망생 스터디 - 15일차 (0) 2022.11.22