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. Juveloper
    갈수록 어려워지고 복잡해지네요..ㅜㅜ 여기까지 복습해봐야겠습니다!
  2. 콜라
    20201015 완료
  3. Yong Hyun Lee
    완료 201002
  4. 2020.09.10
  5. 김지민
    이미 알아차리셨을 수도 있지만 의견 공유해봅니다!
    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
      • 쑤우
        수강완료. 감사합니다~
      • 굼벵이
        완료
      • morning
        pm2 start main.js --watch --ignore-watch="data/*" 이게 꼭 필요하군요.. nginx 에서 upstream 이용해서 포트를 바꿔서 node js에 넘겨주는데 계속 502 에러가 나서 한참 구글링 헤매다가 정작 "수업에서 다루지 못한 이야기"는 보지 못했네요.

        /var/log/nginx/error_log
        *76 recv() failed (104: Connection reset by peer) while reading response header from upstream, client: 192.168.0.4, server: _, request: "GET /?id=new HTTP/1.1", upstream: "http://192.168.0.10:3000/?id=new", host: "192.168.0.10", referrer: "http://192.168.0.10/update?id=new"
      • 박호용
        윈도우 10에서 VScode로 테스트 중입니다.
        rename 하는 과정에서 오류가 발생합니다.
        errno 는 -4058 인데 왜 그럴까요
      • CronEB
        완료
      • YesterdayKite
        완료. 감사합니다!
      • Stephen Lee
        이상한 점을 발견했는데요 리스트중 아무거나 클릭한 후(예를 들어 html) update 를 누르고 element 탭에 들어가면 input type 이 hidden 인 element 의 value 가 html 인 것을 확인하실 수 있습니다. 이를 리스트에 없는 이름으로 바꾼후(예를 들어 html2) 그다음 description 도 아무렇게나 바꿔준후, submit 을 누를시 새로운 파일이 생성됩니다. 마치 create_process 영역의 로직처럼 실행됩니다. 제 생각에는 fs.rename 로직을 만났을때 old path 에 들어갈 값이 없으므로 에러가 발생해 실행이 멈춰야 된다 생각하지만 callback 로직이 실행돼 fs.writeFile 로 새로운 파일을 만들고 있습니다.

        err 에 대한 처리가 없으니 err 가 발생해도 callback 이 그대로 실행되고 writeFile 이 실행돼 새로운 파일을 만든다는 것을 알게 됐네요(err 가 있을시 console 로 에러를 찍으라 했더니 log 가 찍혔습니다.) 그리고 마찬가지로 리스트에 이미 존재하는 이름으로 hidden input 에 값을 주었을때 해당 파일이 바뀌니 fs.writeFile 은 경로가 있으면 파일을 업데이트하고 없으면 새로생성하는 것이 맞는건가요?
      • Stephen Lee
        writeFile() 은 첫번째 변수에 경로를 넣어주는데 해당 경로가 없으면 새로 file을 만드는 건가요? 만약 해당 경로가 있다면 그 파일을 수정하는 것인지 궁금합니다.
        단지 writeFile() 이 새로운 파일을 생성하는것이라면 rename 한 css 는 css3 가 되고 css3 라는 이름을 가진 새로운 다른 파일이 생성되는게 아닌가 하는 의문이 듭니다.
      • youngjin.lee
        completed
      • 허공
        190510 감사합니다.
      • 위준우
        완료
      • 자유로움
        완료
      • title이 공백문자를 포함하고있는 경우 수정 페이지에 들어갔을시에 첫번째 공백문자 뒤로는 문자가 나오지 않는데 이 문제는 어떻게 해결해야하나요?
      • supernet
        감사합니다.
      • 호두
        고맙습니다.
      • 권문수
        완료!! 감사합니다^^
      • jo_onc
        "서식을 준 형태로 글을 저장" 질문이 이해가 잘 가지 않는데,
        만약 서식이,
        1.디자인(사이즈, 컬러 등)을 말씀하시는 거라면 'CSS'를 알아보시고,
        2.값을 말씀하시는 거라면 파일의 데이터 값을 알아보시면 됩니다!
        대화보기
        • JongHun Cha
          pm2 start main.js --watch --ignore-watch="data/*"

          상기 방법이 잘 적용이 되지 않습니다.
          명령어를 입력하여도 main.js나 data 디렉토리 안의 폴더를 변경하거나 앱상에서 생성 및 변경을 할 때 모두
          앱을 리로드 합니다.. --watch 명령어가 --ignore를 오버라이드 하지 않나 생각이듭니다.

          만약 아래와 같이 좀더 구체적으로 watch의 경로를 지정해주면 명령어가 적용됩니다.
          pm2 start main.js --watch="main.js" --ignore-watch="data/*"

          하지만 이렇게 구체적인 경로를 지정하기 어려우므로,
          pm2 ecosystem 으로 ecosystem.config.js 파일 생성후 아래와 같은 형식으로 내용 변경 후
          pm2 start ecosystem.config.js 를 실행하면 잘 됩니다.
          하지만... 혹시 그냥 저렇게 단순한 명령어로 잘 실행이 되도록 다른 방법이 있다면 답변 부탁드려용~ ㅠ

          module.exports = {
          apps :
          {
          name : 'main',
          script : 'main.js',
          watch : true,
          ignore_watch : ["data/*"],
          exec_mode : "cluster",
          instances : 1,
          merge_logs : true,
          log_date_format : "YY-MM-DD HH:mm:ss",
          error_file : "./logs/err.log",
          out_file : "./logs/out.log"
          }
          };
        • Kyoungil Lee
          이번 예제에서 헤깔릴수 있는 부분이

          fs.rename(`old_path`,`new_path`,function(error){
          fs.writeFile(`new_path`,description,'utf8',function(error){
          response.writeHead(302,{location: `/?id=${title}`});
          response.end('update success!');
          });
          });

          여기서 fs.rename 메소드가 이 fs.writeFile 메소드 보다 먼저 실행됩니다.
          때문에 fs.writeFile에서 new_path를 삽입해 주어야합니다.

          fs.writeFile 윗줄에다

          fs.readdir(`./data`,function(err,filelist){ console.log(filelist); } );

          이줄을 넣어서 실행시켜보시면 확실하게 알 수 있습니다.
        • 삼고잉
          첫번째 훑기
        • Gimme_Gsuit
          rename 함수???를 사용하면서 업데이트시 바뀐 title, description 값으로 수정하게 됩니다.(미사용시 기존에 있던 값들을 그대로 두고 바뀐 값들을 추가 하게 되는 경우도 확인 되었습니다.) 감사합니다.
        버전 관리
        egoing
        현재 버전
        선택 버전
        graphittie 자세히 보기