-
[Node.js & MySQL] 데이터 처리방식: 파일 -> MySQL 이식생활코딩/WEBn 2020. 12. 9. 18:03
[강의 출처] opentutorials.org/course/3347
데이터 리스트 얻기
기존에는 파일 시스템을 이용했었기 때문에, 데이터 리스트를 얻으려면 아래처럼 파일 시스템을 읽어와야 했다.
fs.readdir('./data', function(err, dataList){ /* code */ };
이제 데이터는 MySQL에 저장되므로 이 부분은 이렇게 대체해야 한다.
/* var db = mysql.createConnection({ host : 'localhost', user : 'nodejs', password : 'password', database : 'data' }); db.connect(); 여기까지 MySQL 연동을 위한 객체 설정 */ db.query('SELECT title FROM topic', function(err, result){ callback() };
데이터가 가져와지긴 하는데 아래처럼 RowDataPacket 형태로 저장된다.
[ RowDataPacket { title: 'Amazon' }, RowDataPacket { title: 'Apple' } ]
객체와 똑같이 for문으로 가져오면 된다.
db.query('SELECT title FROM topic', function(err, result){ var dataList = []; for (var data of result){ dataList.push(data.title); }; console.log(dataList); }
CRUD 연산
Read
데이터를 읽어오는 것이기 때문에 위의 SELECT 구문을 그대로 사용하면 된다.
file
fs.readdir('./data', function(err, dataList){ /* code */ });
mysql
db.query('SELECT * FROM topic', function(err, result){ /* code */ });
페이지 출력하는 단의 Read 명령은 다음과 같이 처리했다. 읽어온 데이터는 template.get()으로 html 파일로 만든다.
file
var template = templates.get(title, `data/${title}`, dataList); response.writeHead(200); response.end(template); // 그리고 리스폰스
mysql
db.query('SELECT * FROM topic WHERE id = ', [topicId], function(err, result2){ // 구분자가 파일명에서 id로 바뀌면서 title -> topicId var template = templates.get(topicId, result2[0], dataList); // 결과가 result2가 array라서 [0]처럼 인덱싱 필요 response.writeHead(200); // 리스폰스쪽은 mysql 쿼리문 전송 이후 살행되는 response.end(template); // callback 함수 안쪽으로 들어갔다 });
Create
유저가 데이터를 전송하면, 해당 데이터를 파일리스트에 추가한 다음, 해당 데이터 페이지로 유저를 리다이렉트했다.
file
request_fmt.getData(request, function(post){ // request_fmt이 데이터 받아오는 함수 fs.writeFile(`data/${topicId}`, post, 'utf8', function(err){ // 그대로 write response_fmt.redirect(response, encodeURI(`/?id=${topicId}`)); }); });
mysql
request_fmt.getData(request, function(post){ var keys = Object.keys(post); // INSERT INTO table (col1, col2) VALUES (val1, val2); var values = []; // 형태로 만들기 위해 키/값을 따로 분리하고 아래에서 쉼표로 조인했다 for (var idx of keys){ values.push(post[idx]); }; db.query(`INSERT INTO topic (${keys.join(", ")}) VALUES (${"'" + values.join("', '") + "'"})`, function(err, result3){ response_fmt.redirect(response, encodeURI(`/?id=${result3.insertId}`)); }); });
?id=에 들어간 result.insertId는 쿼리에 INSERT INTO 구문을 넣어주면 result로 들어오는 OkPacket에서 확인할 수 있다.
OkPacket { fieldCount: 0, affectedRows: 1, insertId: 4, // 인덱스 4로 들어간 것 serverStatus: 2, warningCount: 0, message: '', protocol41: true, changedRows: 0 }
Update
파일로 관리하면 파일명 중복의 문제가 있어서 file에서는 해당 부분을 따로 처리했었다.
file
request_fmt.getData(request, function(post){ if (post.id !== post.title){ // 이름이 바뀐 경우 if (dataList.includes(post.title) >= 0){ // 중복된 이름이면 response_fmt.denied(response); // 갱신거절 return 0; } else { fs.rename(`data/${topicId}`, `data/${topicId}`, function(err){ post.id = post.title; }); } });
인덱스 id는 유저가 변경할 수 없기 때문에 위의 이슈는 없지만, 각각이 웹페이지 명이 되기 때문에 title의 중복은 없는 편이 좋겠다.
mysql (중복 처리 없는 경우)
위의 Create 연산에서 데이터 포맷 처리하는 부분과 INSERT INTO 구문 -> UPDATE 구문인 점만 다르다.
request_fmt.getData(request, function(post){ var keys = Object.keys(post); var inputData = ''; for (var idx of keys){ // SET (col1 = 'data1', col2 = 'data2' if (post[idx] !== undefined){ // 형태로 데이터 정리 inputData = inputData + idx + `='${post[idx]}', `; }; }; // 여기부터 DB처리 부분 db.query(`UPDATE topic SET ${inputData.slice(0,-2)} WHERE id = ${post.id}`, function(err, result3){ response_fmt.redirect(response, encodeURI(`/?id=${post.id}`)); }); });
mysql (중복 처리하는 경우)
타이틀이 존재하는지 여부를 확인해서, 중복된 타이틀이면 response_fmt.denied() 함수로 처리,
'Already used title ...' 페이지로 리다이렉트한다.
같은 로직을 Create 구문에 적용하면 생성시의 중복도 막을 수 있다. Create 시에는 WHERE문의 not id 부분은 필요없겠다.
request_fmt.getData(request, function(post){ var keys = Object.keys(post); var inputData = ''; for (var idx of keys){ if (post[idx] !== undefined){ inputData = inputData + idx + `='${post[idx]}', `; }; }; // 여기부터 DB처리 부분 db.query(`SELECT EXISTS(SELECT * FROM topic WHERE title = '${post.title.toLowerCase()}' AND NOT id = ${post.id}) AS dup;`, // 중복된 타이틀 있는지 확인 function(err, result4){ if (result4[0].dup === 0){ // 중복된 타이틀 발견 개수가 0이면 db.query(`UPDATE topic SET ${inputData.slice(0,-2)} WHERE id = ${post.id}`, function(err, result3){ response_fmt.redirect(response, encodeURI(`/?id=${post.id}`)); }); } else { response_fmt.denied(response); }; }); });
Delete
실제로는 데이터 보관, 인덱스 등의 여러 가지 이슈로 데이터 삭제보다는 원문을 비활성화처리하는 경우가 많다고 한다.
아래에서는 백업용 테이블을 만들어서 데이터를 거기에 옮긴 후 삭제하는 방식으로 바꾸었다.
삭제 후 Undo? 페이지로 보낸 다음, 버튼 입력에서 YES를 받으면 데이터를 되돌린다.
file
request_fmt.getData(request, function(post){ fs.unlink(`data/${topicId}`, function(err){ // unlink함수로 data 디렉토리의 파일을 삭제 response_fmt.redirect(response, `/`); // 삭제 후 리다이렉트 응답을 보내는 함수 }); });
mysql (실행취소 없는 경우)
request_fmt.getData(request, function(post){ db.query('DELETE FROM topic WHERE id = ', post.id, function(err, result2){ response_fmt.redirect(response, `/`); return 0; }); });
mysql (실행취소 하는 경우)
백업용 테이블 생성 및 데이터 복사는 이쪽에서 처리했다: hayjo.tistory.com/52
undo page html, location값으로 ?id=아이디 값을 주었다.
<!doctype html> <html> <h2>Really Delete?</h2> <form action="process_undo" method="post" onsubmit=""> <input type="hidden" name="id" value="${location}"> <input type="submit" value="Undo" class="edits"> </form> <input type="button" value="Delete" onclick="location.href='/'"> </html>
삭제 작업페이지를 /process_delete, /process_undo 둘로 구분해서,
if (pathname === "/process_delete") { request_fmt.getData(request, function(post){ db.query('INSERT INTO topic_del SELECT * FROM topic WHERE id = ',[post.id], function(err, result34){ db.query('DELETE FROM topic WHERE id = ',[post.id], function(err, result2){ response_fmt.redirect(response, `/`); return 0; }); }); response_fmt.undo(response, post.id); }); } else if (pathname === "/process_undo") { request_fmt.getData(request, function(post){ db.query('INSERT INTO topic SELECT * FROM topic_del WHERE id =',[post.id], function(err, result2){ response_fmt.redirect(response, `/`); return 0; }); }); }
생각해보니 이쪽이 편할 듯 싶다. 삭제+복구 말고 undo 했을 때만 삭제하는 버전. 위 html의 href 부분은 바꿔야되겠다:
if (pathname === "/process_delete") { request_fmt.getData(request, function(post){ response_fmt.undo(response, post.id); }); } else if (pathname === "/delete") { request_fmt.getData(request, function(post){ db.query('DELETE FROM topic WHERE id = ',[post.id], function(err, result2){ response_fmt.redirect(response, `/`); return 0; }); }); }
'생활코딩 > WEBn' 카테고리의 다른 글
[Node.js & MySQL] 목록 콤보박스 생성 / 디폴트값 설정 (2) 2021.01.05 [Node.js & MySQL] LEFT JOIN으로 가져온 데이터 출력 (0) 2020.12.09 [Node.js & MySQL] Intro, 환경설정 (0) 2020.11.03 [OAuth2.0] OAuth2.0 개요 (0) 2020.09.30 [DNS] Domain Name System 개요 (0) 2020.09.28