Node.js

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

수업소개

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

 

 

 

강의

 

 

 

소스코드

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. 윈도우 파일 탐색기에서 파일의 이름을 바꿔보시면 아시겠지만
    ? 는 파일 이름에 쓸 수 없습니다.(\ / : * " < > | ? 을 사용할 수 없습니다. 아마 내부적으로 사용하는 키워드 같습니다.)

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

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

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

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

    배운 범위 내에서 해결하기 위한 방식이기에 그리 올바른 해결책은 아닙니다.
    대화보기
    • labis98
      20210727 완료^^
    • 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 ★★★★★
    • 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 에서 한것처럼, 파일명과 내용 입력하면 해당 파일이 새로 생성되는 것 처럼.
        어째서 새로 생성되지 않고 기존 파일에 덮어씌어지는 것일까??
      • 영호팍
        긋긋~~~ 출석 완료요~~
      • Amousk
        좋은 강의 감사합니다.
      • Katherine Roh
        rename이 동작은 하는데, 수정된 파일명으로 새로운 파일이 생기기만 하고,
        원래 파일은 삭제되지 않고 그대로 있습니다.
        test1 파일을 test2로 수정하면,
        test1과 test2 파일 두 개가 같이 있어요.
      • 김재익
        완료
      • 김보미
        완료
      • 바다의왕자
        완료
      • 소종원
        그 함수 두개는 그 상단의 함수 request.on의 아래 단에 위치하고 있습니다. 그리고 그 함수보다 위에 id와 title, description들을 정의했고요 이 정의한 값들을 a라고 임의의 명칭으로 지정했을 때 이렇게 했을때 rename함수 안에 write함수를 작성하지 않을 경우, 두 함수에서 쓰이는 id와 title, description은 값이 a가 됩니다. 반면 rename함수 안에 write을 작성했을 경우 만약 rename에서 id와 title, description을 다르게 정의했다면 이 값을 b라고 쳤을 때 rename함수 안에 있는 write함수는 이 b값을 가지고 사용하는 겁니다. 말이 좀 복잡하게 됐는데, 간단하게 말해서 rename에서 변경한 값들을 공유해서 write함수에서 사용하기 위해 rename함수 안에 작성하는 겁니다. 이걸 이해하기 위해서는 전역변수와 지역변수의 개념을 알아야 합니다.
        대화보기
        • vocalyc
          선생님이 만드신
          fs.rename(`data/${id}`, `data/${title}`, function(error){
          fs.writeFile(`data/${title}`, description, 'utf8', function(err){
          response.writeHead(302, {Location: `/?id=${title}`});
          response.end();
          })
          });
          이 코드는 파일 제목을 바꾸는 함수 안에 파일 내용을 바꾸는 함수가 들어가 있습니다.
          하지만 저는
          fs.rename(`data/${id}`, `data/${title}`, function(err){

          });
          fs.writeFile(`data/${title}`, description, 'utf8',
          function(err){
          response.writeHead(302, {Location:
          `/?id=${title}`});
          response.end();
          });
          이렇게 제목을 바꾸는 함수 바깥쪽에 내용을 바꾸는 함수를 해도 똑같이 작동을 하는데 혹시 제목을 바꾸는 함수 안에 내용을 바꾸는 함수가 꼭 들어가야 하는 이유가 있을까요?
          아니면 둘다 상관이 없는건가요?
        • 윤영훈
          감사합니다!
        • 다시듣기
        • rediretion 고민자
          파일명이 한글일 경우에 TypeError [ERR_INVALID_CHAR]: Invalid character in header content ["Location"]
          라고 뜨는데 해결하려면 어떻게 해야할까요? ?

          파일명이 영문, 숫자로 이뤄졌을때는 update_process 의 redirection이 잘 됩니다.
        • 준바이
          감사합니다.
        • 심여수
          감사합니다
        • 03.11 완료
        • eddylee123456
          복습
        • Dave Lee
          검색하세요...
        • ㄳㅎ
          제목을 한글로 지을시 왜 리다이렉션이 안되는걸까요???!!
        • ㄱㅎㅈ
          pm2 start main.js --watch --ignore-watch="data/*"
          명령프롬프트에 입력하면 잘 작동합니다.
        • 스티븐잡숴
          완료
        • 매리미
          pm2 delete main 은 뭐죠..? pm2가 main.js의 변경을 감시하는걸 중단시키는 명령인가요?
        • 임은정
          완료
        • codinginpain
          완료했읍니다다다닷!!
        • 강다리
          run
        • 쑤우
          수강완료. 감사합니다~
        • 굼벵이
          완료
        버전 관리
        egoing
        현재 버전
        선택 버전
        graphittie 자세히 보기