Node.js - MySQL

MySQL로 상세보기 구현

수업소개

MySQL을 이용해서 글 상세보기 기능을 구현하는 방법을 살펴보겠습니다. 

 

 

 

강의

 

 

 

소스코드

main.js (변경사항)

var http = require('http');
var fs = require('fs');
var url = require('url');
var qs = require('querystring');
var template = require('./lib/template.js');
var path = require('path');
var sanitizeHtml = require('sanitize-html');
var mysql = require('mysql');
var db = mysql.createConnection({
  host:'localhost',
  user:'root',
  password:'111111',
  database:'opentutorials'
});
db.connect();


var app = http.createServer(function(request,response){
    var _url = request.url;
    var queryData = url.parse(_url, true).query;
    var pathname = url.parse(_url, true).pathname;
    if(pathname === '/'){
      if(queryData.id === undefined){
        db.query(`SELECT * FROM topic`, function(error,topics){
          var title = 'Welcome';
          var description = 'Hello, Node.js';
          var list = template.list(topics);
          var html = template.HTML(title, list,
            `<h2>${title}</h2>${description}`,
            `<a href="/create">create</a>`
          );
          response.writeHead(200);
          response.end(html);
        });
      } else {
        db.query(`SELECT * FROM topic`, function(error,topics){
         if(error){
           throw error;
         }
         db.query(`SELECT * FROM topic WHERE id=?`,[queryData.id], function(error2, topic){
           if(error2){
             throw error2;
           }
          var title = topic[0].title;
          var description = topic[0].description;
          var list = template.list(topics);
          var html = template.HTML(title, list,
            `<h2>${title}</h2>${description}`,
            ` <a href="/create">create</a>
                <a href="/update?id=${queryData.id}">update</a>
                <form action="delete_process" method="post">
                  <input type="hidden" name="id" value="${queryData.id}">
                  <input type="submit" value="delete">
                </form>`
          );
          response.writeHead(200);
          response.end(html);
         })
      });
      }
    } else if(pathname === '/create'){
      fs.readdir('./data', function(error, filelist){
        var title = 'WEB - create';
        var list = template.list(filelist);
        var html = template.HTML(title, list, `
          <form action="/create_process" method="post">
            <p><input type="text" name="title" placeholder="title"></p>
            <p>
              <textarea name="description" placeholder="description"></textarea>
            </p>
            <p>
              <input type="submit">
            </p>
          </form>
        `, '');
        response.writeHead(200);
        response.end(html);
      });
    } else if(pathname === '/create_process'){
      var body = '';
      request.on('data', function(data){
          body = body + data;
      });
      request.on('end', function(){
          var post = qs.parse(body);
          var title = post.title;
          var description = post.description;
          fs.writeFile(`data/${title}`, description, 'utf8', function(err){
            response.writeHead(302, {Location: `/?id=${title}`});
            response.end();
          })
      });
    } else if(pathname === '/update'){
      fs.readdir('./data', function(error, filelist){
        var filteredId = path.parse(queryData.id).base;
        fs.readFile(`data/${filteredId}`, 'utf8', function(err, description){
          var title = queryData.id;
          var list = template.list(filelist);
          var html = template.HTML(title, list,
            `
            <form action="/update_process" method="post">
              <input type="hidden" name="id" value="${title}">
              <p><input type="text" name="title" placeholder="title" value="${title}"></p>
              <p>
                <textarea name="description" placeholder="description">${description}</textarea>
              </p>
              <p>
                <input type="submit">
              </p>
            </form>
            `,
            `<a href="/create">create</a> <a href="/update?id=${title}">update</a>`
          );
          response.writeHead(200);
          response.end(html);
        });
      });
    } else if(pathname === '/update_process'){
      var body = '';
      request.on('data', function(data){
          body = body + data;
      });
      request.on('end', function(){
          var post = qs.parse(body);
          var id = post.id;
          var title = post.title;
          var description = post.description;
          fs.rename(`data/${id}`, `data/${title}`, function(error){
            fs.writeFile(`data/${title}`, description, 'utf8', function(err){
              response.writeHead(302, {Location: `/?id=${title}`});
              response.end();
            })
          });
      });
    } else if(pathname === '/delete_process'){
      var body = '';
      request.on('data', function(data){
          body = body + data;
      });
      request.on('end', function(){
          var post = qs.parse(body);
          var id = post.id;
          var filteredId = path.parse(id).base;
          fs.unlink(`data/${filteredId}`, function(error){
            response.writeHead(302, {Location: `/`});
            response.end();
          })
      });
    } else {
      response.writeHead(404);
      response.end('Not found');
    }
});
app.listen(3000);

 

댓글

댓글 본문
  1. 감자
    22.12.19
  2. 당당
    2022.11.08
  3. toonfac
    220715 오전 11시 31분 완료
  4. xogk1128
    22.07.15 완료
  5. 화려하게간다
    ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ
    대화보기
    • 화려하게간다
      난 왜 에러뜨는데 저 코드 복사하면 에러가 안뜨냐;;
    • synodic
      2021-12-30
    • 케굴
      2021-12-29
    • 쏠티슈가
      코드 그래도 복붙해도 update하고 delete 버튼이 안생기네요... 왜지...

      ==============================

      제 잘못이었네요...ㅋㅋㅋ....버튼을 눌러 들어가야 보이는것을 바보짓 했네요
    • labis98
      20210801 completed!
    • hanel_
      21.3.23
    • jeisyoon
      2021.3.10 MySQL 상세보기 구현 - OK
    • Jay_
      21.01.12
    • 김지호
      21 01 03
    • 생활둘기
      2021 1 2
    • 콜라
      20201021 완료
    • Jonghwo Lee
      완료
    • 가톨릭대 컴공
      완료
    • 준바이
      good
      대화보기
      • 소종원
        코드 자체는 돌아가는 것처럼 보입니다. 하지만 접근 방식 자체가 수업의 의도와는 다른 것 같습니다. 지금 작성하신 코드는 SELECT * FROM topic 로 받아오는 모든 리스트에서 받아 처리하는 그 리스트에서 돌려가며 보여주고 있는 코드입니다. 그러나 수업에서는 해당하는 id값에 따라서 새로운 쿼리를 날려 해당하는 데이터를 받아와 보여주고 있는 방식입니다. 이쪽이 더 능동적이고 좋은 코드가 맞습니다. 가령. 작성하신 코드의 경우에서 만약 데이터 삭제가 이루어질 경우. db의 저장된 id값들이 [1, 2, 4, 5] 이런식으로 됐다고 생각해보세요. 3이 비어 있지만, 작성하신 코드의 경우네는 배열의 순서 값에 따라서 데이터를 보여주고 있기때문에 db에서 3이 비어있다고 해도 [0, 1, 2, 3] 이런식의 배열 순서값을 그대로 가지고 있을겁니다. 그렇게 되면 다른 결과를 보여주거나 에러를 뱉어내게 되겠죠.
        대화보기
        • 김 지홍
          안녕하세요. 이고잉 선생님 강의 너무 잘 듣고 있습니다.

          상세페이지 구현 부분을 영상을 보지 않고 해보았는데요.
          코드가 이고잉 선생님과 조금 다르게 작성되었습니다.
          -- db.query를 한번만 사용 --

          혹시 db.query를 한번만 써서 구현하면 문제가 있을까요??
          두번 써야 하는 이유가 있는지 궁금합니다.

          고수 분들의 의견 부탁드려요


          db.query(`SELECT * FROM topic`, function(error, topics){
          if(error){
          throw error;
          }
          var title = topics[queryData.id-1].title;
          var description = topics[queryData.id-1].description;
          var list = template.list(topics);
          var html = template.HTML(title, list,
          `<h2>${title}</h2>${description}`,
          `<a href="/create">create</a>
          <a href="/update?id=${queryData.id-1}">update</a>
          <form action="delete_process" method="post">
          <input type="hidden" name="id" value="${title}">
          <input type="submit" value="delete">
          </form>
          `
          );
          response.writeHead(200);
          response.end(html);
          });
        • Pyo_Mh1nitt
          가장 대표적인 예로는 SQL인젝션을 찾아보시면 될듯합니다.

          이번과 같은경우를 예시로 들자면
          select * from topic where id = ${queryData.id} 일경우 queryData.id 가 id 값과 일치하는 값들을 반환하게 됩니다.
          현재와 같이 1,2,3... 이러한 값을 입력하면
          [select * from topic where id = 1] 이라는 SQL문으로 실행되겠지만
          만에하나 악의적인 의도를 갖고 id값을 1 or 1=1 같은 값으로 변경,혹은 입력하게 된다면
          [select * from topic where id = 1 or 1 = 1] 로서 실행되어 id 값이 1인 값뿐만이 아닌 1=1은 참이기에
          조건에 관계없이 모든 값들을 얻어오게 됩니다.

          SQL을 입력한다는 단순한 공격이지만 무언가를 제한된 출력(권한이나 입력에 따른 정보출력제한)을 무시하고 정보의 취득을 가능하게 하거나 로그인시 아이디 비밀번호의 값이 일치하다 판단하는 식의 SQL을 입력하여 일치여부을 무시하고 접근을 가능하게하는 등 큰 문제를 야기할 우려가 있습니다.
          대화보기
          • JustDoIt
            id=? 를 하는 이유가, 사용자 입력에 대한 공격에 대응하기 위해서 라고 설명하셨는데

            id=${queryData.id} 를 기입했을 때 어떤 공격을 받을 수 있다는 건지 알려주실 수 있나요?
          • dave
            조금 간단히 설명 드리자면 MySQL server에서 데이터를 '배열'형태로 가져오기 때문입니다.
            배열은 값을 담을 수 있습니다. 값은 자바스크립트에서 함수가 될 수 있으며 객체 또한 될 수 있습니다.
            즉, 이 맥락에서는 배열에 담긴 첫번 째 객체(topic[0])에서 title 값을 변수 title로 가리키고 있는 것이죠.
            대화보기
            • codinginpain
              완료
            • 굼벵이
              완료
            • ssshh
              template.js 고칠부분 있습니다

              저만 옛날 소스코드로 본건가요?

              while(i < filelist.length){
              list = list + `<li><a href="/?id=${filelist[i].id}">${filelist[i].title}</a></li>`;
              i = i + 1;
              }

              부분 고치지않으면 리스트가 [object object] 로 나오더라구요
            • ahnppeng
              저 질문이 있는데요. 상세보기로 else문에 작성된 코드가 console로 찍어가면서 확인 했는데 else문 안으로 들어가지를 안더라고요 왜그럴까요? 제 코드에서 url에 id값이 /?id%20=%201이런식으로 출력되는데 이 문제가 혹시 else문 안으로 안들어가는 이유가 되나요? 만약에 그렇다면 어떻게 수정해야될까요?
            • goyacommae
              vsCode 에디터에서 말씀이신가요? 그렇다면 view > terminal 클릭하시면 나옵니다.
              대화보기
              • ㅇㅇ
                터미널 실행 하는 버튼은 무엇인가요
              • jo_onc
                감사합니다^^
                점점 더 cs에 매력을 느끼네요!
              • moon
                감사합니다.
              • INCE
                아.... 감사합니다! 덕분에 궁금했던점이 풀렸습니다
                대화보기
                • 박찬울
                  [ RowDataPacket {
                  id: 1,
                  title: 'MySQL',
                  description: 'MySQL is...',
                  created: 2018-01-01T03:10:11.000Z,
                  author_id: 1 } ]

                  위는 console.log(topic)의 결과입니다. 여기서 [] (대괄호)를 주목해주세요, [] 안에 RowDataPacket{...생략...}이 들어있습니다. 즉 [] 안의 RowDataPacket{} 은 첫 번째 객체입니다. 따라서 topic[0] 으로 RowDataPacket{...} 을호출할 수 있는 것입니다. topic[0].title 은 topic 객체의 0번째 인덱스 RowDataPacket{}의 title을 가져오게 되는 것입니다.

                  만약 다음과 같은 SQL 으로 처리한다면 topic 의 결과는 아래와 같습니다.

                  SQL :
                  SELECT * FROM topic WHERE id = 1 OR id = 3;


                  결과 :
                  [ RowDataPacket {
                  id: 1,
                  title: 'MySQL',
                  description: 'MySQL is...',
                  created: 2018-01-01T03:10:11.000Z,
                  author_id: 1 },
                  RowDataPacket {
                  id: 3,
                  title: 'SQL Server',
                  description: 'SQL Server is ...',
                  created: 2018-01-20T02:01:10.000Z,
                  author_id: 2 } ]

                  만약 topic 의 두 번째 객체의 "SQL Server is ..."이라는 description 를 가져오고 싶다면 어떻게 해야 할까요?
                  topic[1].description
                  이라고 하면 될 것입니다. 이해가 되셨으면 좋겠습니다.
                  대화보기
                  • INCE
                    갑자기 궁금해서 그런데요...
                    db.query(`SELECT * FROM topic`, function(error,topics){
                    if(error){
                    throw error;
                    }
                    db.query(`SELECT * FROM topic WHERE id=?`,[queryData.id], function(error2, topic){
                    if(error2){
                    throw error2;
                    }
                    var title = topic[0].title;
                    var description = topic[0].description;

                    저기를 0으로 넣는건 topic에 id 값중 0이 없기 때문에 그냥 0으로 입력하면
                    db.query(`SELECT * FROM topic WHERE id=?`,[queryData.id], function(error2, topic){ 여기서
                    정해진 id값을 알아서 가져오는건가요?
                  • 3344
                  버전 관리
                  egoing
                  현재 버전
                  선택 버전
                  graphittie 자세히 보기