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. 영호팍
    긋긋~~~ 출석 완료요~~
  2. Amousk
    좋은 강의 감사합니다.
  3. Katherine Roh
    rename이 동작은 하는데, 수정된 파일명으로 새로운 파일이 생기기만 하고,
    원래 파일은 삭제되지 않고 그대로 있습니다.
    test1 파일을 test2로 수정하면,
    test1과 test2 파일 두 개가 같이 있어요.
  4. 김재익
    완료
  5. 김보미
    완료
  6. 바다의왕자
    완료
  7. 소종원
    그 함수 두개는 그 상단의 함수 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 값으로 수정하게 됩니다.(미사용시 기존에 있던 값들을 그대로 두고 바뀐 값들을 추가 하게 되는 경우도 확인 되었습니다.) 감사합니다.
      • 저는 update_process 에서 변수로 선언한 body가
        위에 create_process내 에서 선언한 body랑 충돌이나는데
        왜 그러는 걸까요....ㅠㅠ
      • moon
        감사합니다.
      • Seo Yun Seok Tudoistube
        TextArea 안에 서식을 준 형태로 글을 저장하려면 뭘 알아야 할지 궁금합니다.
        감사합니다!
      버전 관리
      egoing
      현재 버전
      선택 버전
      graphittie 자세히 보기