생활코딩

Coding Everybody

App - 글수정 - 수정된 내용 저장

토픽 생활코딩 > WEB > WEB2 - Node.js

수업소개

전송된 수정 내용을 받아서 파일명을 변경하고, 내용을 저장하는 방법을 알아봅니다.

 

 

 

강의

 

 

 

소스코드

main.js (변경사항)

var http = require('http');
var fs = require('fs');
var url = require('url');
var qs = require('querystring');

function templateHTML(title, list, body, control){
  return `
  <!doctype html>
  <html>
  <head>
    <title>WEB1 - ${title}</title>
    <meta charset="utf-8">
  </head>
  <body>
    <h1><a href="/">WEB</a></h1>
    ${list}
    ${control}
    ${body}
  </body>
  </html>
  `;
}
function templateList(filelist){
  var list = '<ul>';
  var i = 0;
  while(i < filelist.length){
    list = list + `<li><a href="/?id=${filelist[i]}">${filelist[i]}</a></li>`;
    i = i + 1;
  }
  list = list+'</ul>';
  return list;
}

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){
        fs.readdir('./data', function(error, filelist){
          var title = 'Welcome';
          var description = 'Hello, Node.js';
          var list = templateList(filelist);
          var template = templateHTML(title, list,
            `<h2>${title}</h2>${description}`,
            `<a href="/create">create</a>`
          );
          response.writeHead(200);
          response.end(template);
        });
      } else {
        fs.readdir('./data', function(error, filelist){
          fs.readFile(`data/${queryData.id}`, 'utf8', function(err, description){
            var title = queryData.id;
            var list = templateList(filelist);
            var template = templateHTML(title, list,
              `<h2>${title}</h2>${description}`,
              `<a href="/create">create</a> <a href="/update?id=${title}">update</a>`
            );
            response.writeHead(200);
            response.end(template);
          });
        });
      }
    } else if(pathname === '/create'){
      fs.readdir('./data', function(error, filelist){
        var title = 'WEB - create';
        var list = templateList(filelist);
        var template = templateHTML(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(template);
      });
    } 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){
        fs.readFile(`data/${queryData.id}`, 'utf8', function(err, description){
          var title = queryData.id;
          var list = templateList(filelist);
          var template = templateHTML(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(template);
        });
      });
    } 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 {
      response.writeHead(404);
      response.end('Not found');
    }
});
app.listen(3000);

 

 

 

 

수업에서 다루지 못한 이야기

pm2를 실행할 때 --watch 옵션을 주면 파일이 변경되었을 때 앱을 리로드하게 됩니다. 즉 data 디렉토리의 파일이 수정되었을 때 리로드가 일어나게 되는 것이죠. 이런 문제를 방지하기 위해서는 data 디렉토리에 대해서는 watch를 하지 않도록 설정해야 합니다. 아래의 방법이 도움이 될 것입니다. 

pm2 delete main
pm2 start main.js --watch --ignore-watch="data/*"

 

댓글

댓글 본문
  1. 비전공자
    오후 2:59 2024-05-11
  2. 김철흥
    2024.01.12
    완료!
  3. 어흥
    230702
  4. Hojun Song
    2023-04-16
  5. 감자
    22.12.04 완료
  6. 감자
    22.12.04 완료
  7. enty
    너무 재밌어요
  8. 당당
    2022.10.26
  9. 아캔두잇
    20220805 완료
  10. 키다리아저씨
    220720 완
  11. 키다리아저씨
    저도 Location에서 자꾸 유효하지 않은 값을 넣었다고 해서 위 방식대로 하니까 한글 입력후 문제없이 화면을 띄어줍니다!
    대화보기
    • toonfac
      220714 오후 3시 51분 완료
    • 뽀롱보더
      감탄 최고
    • kimkk
      <input type="hidden" name="id" value="${title}">

      name property value is used at prased body to distiguish element in node.js
    • 케굴
      <input type="hidden" name="id" value="${title}"> 을 실수로 <input type="hidden" name="id" placeholder=${title}> 로 해서 제목이 수정이 안되고, 새 글이 생성됐네요 저와 같은 실수를 하는 분이 있을까 하여 공유합니다.
    • 초딩 개발자
      2021/12/25
    • 일억개
      위 코드 그대로 복붙해도 저는 잘 되네요..
      아마도 파일 위치가 다르거나 encodeURI() 안 하신거 아닌가요?
      대화보기
      • 일억개
        파일 이름이 똑같으니까, 건너뛰고 description만 덮어쓰기 하겠져?
        대화보기
        • 일억개
          4:10 "아 기분 좋아"
        • 만추
          이번 강의에서 막히네요. 예제 코드를 그대로 붙여 넣기 하여도 /data 가 수정이 안되는데 이유를 알 수 있을까요?
        • Edis
          어렵네요 제가 따라 하면서 하는 실수를 찾으면서 잘 따라가고는 있지만 이해하고있는지는 조금 헷갈리네요
          이번 챕터까지 끈내고 지금까지 쓴 코드를 읽으면서 스스로 설명하는 시간이 필요한걸 느끼고 있네요
        • pdpd
          21.10.11
        • 야옹스
          20211010
        • 졸작완성하자
          211008 완료
        • TODOa
          윈도우 파일 탐색기에서 파일의 이름을 바꿔보시면 아시겠지만
          ? 는 파일 이름에 쓸 수 없습니다.(\ / : * " < > | ? 을 사용할 수 없습니다. 아마 내부적으로 사용하는 키워드 같습니다.)

          그래서 파일이 생성되지 않습니다. 그런데도 페이지로 넘어가는 이유는
          53 ~62번째 줄에서 파일 있든 없든 페이지를 만들어서 웹브라우저로 돌려주게 되어있어서 그렇습니다.

          위의 코드들은 이런 문제에 대해서 아무런 예외처리가 없습니다.(설명을 간략하게 하기 위해서)

          위 코드를 이용해서 아주 간략하게 문제를 해결하고자 한다면
          53~54번 줄 사이에
          if (err != null) {
          response.writeHead(404);
          response.end("Not Found!");
          return;
          }

          를 삽입해서 err에 뭔가 값이 들어오면 Not Found! 페이지를 보여주는 방식이 있습니다만

          배운 범위 내에서 해결하기 위한 방식이기에 그리 올바른 해결책은 아닙니다.
          대화보기
          • labis98
            20210727 완료^^
          • Duke
            2021.07.18
          • 남이
            한글로 업데이트 및 생성 안되는 것에 대하여,

            response.writeHead(302, {Location: encodeURI(`/?id=${title}`)});

            location 에 대한 값을 encodeURI() 내부에 넣어 보시길 바랍니다.

            저는 이렇게 수정하고 정상 작동합니다.
          • 남이
            안녕하세요. 질문 남겨봅니다.
            Create, Update 할때 title 값에다가 '?'를 넣고 끝냈을때 (ex. 'what is this?') Create 혹은 update 할때 description 항목이 undefined 로 웹에 뜨지만, code editor 에서는 파일이 저장 및 수정되지 않습니다(VSCODE 사용중). PM2 logs 로 확인해봐도 에러가 잡히는 게 없어요.
            console.log 로 찍어보니 post 값까지는 제대로 넘어옵니다.

            ex)
            0|main | [Object: null prototype] {
            0|main | title: 'what is this?',
            0|main | description: 'what do you mean?'
            0|main | }

            반대로, 타이틀값은 '?' 문자를 제거하고 description 에는 마지막을 '?'로 끝냈을 때 정상적으로 파일이 저장 및 수정됩니다.
            ex)
            title: 'what',
            description: 'what is this??'

            이 문제에 대해 아시는 분 계시면 알려주시면 감사하겠습니다.
          • Jeong Il Haan
            20210421
          • byoonn
            완료
          • 21.02.27
          • chimhyangmoo
            21.02.24
          • jeisyoon
            2021.02.11 App - 글수정 - 수정된 내용 저장 완료
          • 마아앙
            2021.02.09
          • 뭄수
            완료
          • ohhigo
            21/1/24 ★★★★★
          • Noah
            2021.01.06 완료!
          • 손민철
            20/12/31 완료
          • 생활둘기
            2020 12 26
          • kkn1125
            20.12.22 완료~!
          • 옹옹
            재밌습니다!
          • 너구친구
            와우 감탄 나오네요 쉽고 효율적인 설명 감사합니다.
          • Juveloper
            갈수록 어려워지고 복잡해지네요..ㅜㅜ 여기까지 복습해봐야겠습니다!
          • 콜라
            20201015 완료
          • Yong Hyun Lee
            완료 201002
          • vampa
            2020.09.10
          • 김지민
            이미 알아차리셨을 수도 있지만 의견 공유해봅니다!
            writeFile 함수를 rename의 callback 함수로 썼느냐 안썼느냐의 차이인거 같습니다.
            만약 vocalyc님의 코드로 실행된다면 네트워크 상의 문제 등으로 rename 함수에서 error가 난다 해도 rename만 되지 않을 뿐 writeFile 함수는 실행될 수 있고 data 디렉토리 안에 wirteFile로 인한 파일이 따로 생겨날 수 있죠. 예제에서의 CSS 파일 update가 아니라 CSS3 파일이 create 될 것입니다
            하지만 영상처럼 callback 함수로 쓴다면 error가 아닐 경우 wirteFile 함수를 호출하는 것이기 때문에 error로 인해 rename이 되지 않으면 writeFile 즉, 갱신도 되지 않는거죠
            대화보기
            • Jenny Song
              30th.JULY.2020 완료

              write.File() 사용하면 그냥 그 rename한 제목과 동일한 제목을 가진상태로 decription만 내용으로 하는 파일이 새로 생기는것 아닌가?
              /create 에서 한것처럼, 파일명과 내용 입력하면 해당 파일이 새로 생성되는 것 처럼.
              어째서 새로 생성되지 않고 기존 파일에 덮어씌어지는 것일까??
            • 영호팍
              긋긋~~~ 출석 완료요~~