세리프 따라잡기

nodejs에서 동기와 비동기, callback, 패키지 매니저(pm2), html form, 글쓰기 ui 생성 본문

Node.js

nodejs에서 동기와 비동기, callback, 패키지 매니저(pm2), html form, 글쓰기 ui 생성

맑은 고딕 2021. 1. 6. 20:38

 

17. 동기적(synchronous)인 것과 비동기적(asynchronous)

  • 동기

일을 처리할 때, 단계적으로 처리하는 것을 말한다. 직렬적. 만약 1단계 일이 10시간이 걸린다고 하더라도 그 일을 기다렸다가 처리하는 상황을 말한다.

  • 비동기

일을 처리할 때, 동시에 여러가지 일을 처리하는 것을 말한다. 병렬적. 만약 1단계 일이 10시간이 소요된다면 그걸 10시간 동안 보고 있지 않고, 컴퓨터든 누군가에게 일을 맡겨두고 다음 처리를 시작하여 보다 효율적이게 일을 처리하는 것을 말한다. 그러나 동기적 상황에 비해 다소 복잡함을 갖고 있다는 단점이 있다. = nodejs에서는 비동기적인 상황에 좋은 기능들을 많이 가지고 있다.

  • 동기 vs 비동기 비교

nodejs.org/dist/latest-v14.x/docs/api/fs.html#fs_fs_readfilesync_path_options 의 readfile과 readfilesync 코드를 알아내 이용

  • readfilesync - 동기적 살펴보기
var fs = require('fs');

console.log('a'); // 이게 먼저 실행되면 a가 화면에 출력된다.
var result = fs.readFileSync('syntax/sample.txt', 'utf8'); //결과를 result라는 변수에 담음
console.log(result); // 이게 실행되면 B가 출력된다.
console.log('c'); // c 출력 → aBc가 차례로 출력.
  • readfile - 비동기적 살펴보기 → nodejs에서는 이걸 선호함.
var fs = require('fs');

console.log('a');
fs.readFile('syntax/sample.txt', 'utf8', function(err, result){ //문법의 callback 부분은 이렇게 function(함수)를 사용해 표시한다. 그리고 앞에 변수를 없애고, 함수를 3번째 인자로 줘야한다.
    console.log(result);
}); // 함수의 첫번째 인자에서는 err(error)가 있다면 err를 제공하고, 두번째 parameter엔 파일의 내용을 인자로써 공급해주도록 되어있다.
console.log('c'); // acB의 순서로 출력된다. 이는 a가 실행되고 다음 차례인 파일을 읽는데, 그와 동시에 c가 먼저 작업이 끝나 출력이 되고, 그것보다 후에 함수 호출 작업을 끝낸 B가 출력이 된 것이다. = 비동기적

 = 비동기적인 방식으로 작업을 하는 것을 추천. [물론 단순한 코드라면 동기적으로 해도 된다.]

 

18. callback 이란?

앞서 비동기 방식에서 서술(acB 출력 순서에 대해)했던 방식을 말한다. nodejs에게 syntax/sample.txt에 있는 파일을 읽어오게 하고, 그 작업이 완료가 된다면 3번째 인자인 함수를 실행하고 출력해달라고 한 것이다. = 말 뜻 그대로, 나중에 나한테 말해줘.

  • 함수 =  값
/*
function a(){
    console.log('a'); // a출력
}
function(){ //이 상태는 위의 코드와 함수의 기능, 내용은 똑같으나 이름이 없는 익명 함수 상태이다. 또한 이름이 없으면 호출을 할 수 없다.
    console.log('a');
}
*/
var a = function(){ //1번째 함수 코드와 동일하게 된다. 이 코드는 a 라는 변수를 통해 값으로써 함수를 정의하고 있음. = 자바 스크립트에서는 함수가 값이다. 라는 의미이다.
    console.log('a'); // a출력
}
a(); //a 호출 코드
  • callback 사용
var a = function(){
    console.log('a');
}
// 여기에 위치한 a(); 호출 코드를 지우고 아래로
function slowfunc(callback){ //오래 걸리는 함수가 있다고 생각해보자. 이때 이 함수의 일이 끝났으니까 다음 일을 하게끔 하고 싶다면 callback을 인자로 쓰고,
    callback(); //callback을 호출하면 된다.
}
slowfunc(a); //a 변수를 넣는다. 이러면: callback이라는 parameter는 a가 가리키는 함수값을 갖게 된다. = 결론적으로 callback을 호출하게 되면 console.log('a'); 부분이 출력된다.

19. 패키지 매니저 (Package Manager), PM2

- 패키지란 소프트웨어를 부르는 여러가지 표현 중 하나. 독립적으로 실행되는 프로그램도, 어떤 프로그램 안에서 부품으로 사용되는 작은 프로그램도 패키지라고 부를 수 있다.

- 패키지 매니저란 이런 소프트웨어들을 생성하고 설치하고 업데이트 및 제거를 통해 관리해준다. 각각의 언어와 운영 체제 별로 이는 중요한 역할을 수행한다. 살펴볼 패키지 매니저: nodejs에서 가장 광범위하게 사용되는 NPM (nodejs를 다운할 때 기본 설치된다.)

  • PM2

ADVANCED, PRODUCTION PROCESS MANAGER FOR NODE.JS(nodejs에서 실행중인 프로그램을 관리해준다) PM2는 nodejs가 어떤 일로 인해 중단되면 다시 실행시켜주기도, 파일이 수정되면 껏다 켜서, 우리가 수정할때마다 재실행 시키는 일들을 대신해주어 편리한 도구다.

pm2.keymetrics.io/ 에서 다운로드(콘솔창에 코드(npm install pm2 -g = -g는 독립적 소프트웨어니까 어디서든 사용할 수 있다, 라고 표현한 것.) 입력) 및 실행 방법 등이 나와있다. 아래는 보기 편하게 정리한 것!

pm2 start (파일 이름).js (파일 이름).js를 실행시킨다.
pm2 stop (파일 이름) (파일 이름)을 종료시킨다.
pm2 kill pm2를 통해 실행되고 있는 모든 걸 종료시킨다. (list에도 남지 않는다)
pm2 list 실행되고 있는 list를 보여준다.
pm2 monit 실행되고 있는 프로그램들을 모니터한다.
pm2 start (file name).js --watch (file name).js를 계속 감시·감독 해준다. 수정 사항을 바로 피드백하여 리로드.
pm2 start ().js --watch --no-daemon daemon이 아니다 = 즉, 백그라운드에서 실행되는 것이 아니다.
pm2를 실행함과 동시에 log까지 볼 수 있는 게 이 옵션이다.
[단, 이걸 이용할 때 꺼졌다가 다시 켜질 수가 있는데(메모리에 있던 데이터가 사라짐) 이 문제는 아래에 후술]
pm2 start ().js --watch --ignore-watch="(directory ex.data)/*" --no-daemon data 디렉토리에 있는 모든 파일에 대해 무시하겠다는 뜻. (이를 통해 중간에 꺼지는 것을 방지할 수 있다.) + 여러 개의 디렉토리를 관리해야 한다면, "()/* (sessions)/*" 이렇게 띄어쓰기를 통해 추가하면 된다.
pm2 log --watch를 사용할 때, 이전에 콘솔창이 에러 사항을 표시해주는 것이 안된다. 이때 log를 사용하면 에러사항 및 수정사항을 보여준다.

20. HTML form

사용자가 서버쪽으로 데이터를 전송하기 위한 방식을 HTML form이라 한다. 콘텐츠를 사용자가 web을 통해서 생성하고, 수정하고, 삭제하는 방법을 알아보자.

  • 사용자로부터 정보를 입력받는 방법
<form action="http://localhost:3000/create_process">
    <!-- 이런 정보들을 서버로 전송하기 위해 어디로 보낼 것인지에 대한 주소 = form action -->
    <p><input type="text" name="title"></p> <!-- 글씨를 입력할 수 있게 만든 것 + 입력값에 대한 이름 설정(컨트롤) + p태그로 감싸서 줄바꿈 -->
    <p>
        <textarea name="description"></textarea>
        <!--여러줄을 입력할 수 있는 텍스트란 생성-->
    </p>
    <p>
        <input type="submit"> <!-- 사용자가 입력에 대한 전송해줄 버튼 만들기 -->
    </p>
</form> <!-- 입력을 제출하면, 주소 뒤에 ?와 함께 쿼리 스트링이 만들어지는 것을 볼 수 있다. -->

form이라는 태그는 안에 있는 각각의 컨트롤 안에 사용자가 입력한 정보를 제출 버튼을 눌렀을 때 action 속성이 가리키는 서버로 쿼리스트링 형태로 데이터를 전송하는 html의 기능이다. 이때 1번째 줄 코드의 주소로 전송하면 http://localhost:3000/create_process?title=(입력 txt)&description=(입력 txt) 라는 주소로 전송되는데, 이는 좋은 주소가 아니다(이 주소를 통해 누군가 잘못 수정하면 대참사가 일어난다). 아래는 1번째 코드를 수정한 것

 

<form action="http://localhost:3000/create_process" method="POST"> <!--방법(method)을 추가, post를 적어주면 된다-->

이렇게 수정해주면 은밀하게 서버로 전송할 수 있다.

서버로부터 사용자가 데이터를 가져올 때는 method="get" 이거나 생략하면 된다. 하지만 서버의 데이터를 수정·삭제·생성할 때는 반드시 method를 post 방식으로 해야한다.

21. 글 생성 UI 만들기

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

function templateHTML(title, list, body) {
  return `
        <!doctype html>
    <html>
    <head>
      <title>WEB1 - ${title}</title>
      <meta charset="utf-8">
    </head>
    <body>
      <h1><a href="/">WEB</a></h1>
      ${list}
      <a href="/create">create</a>
      ${body}
    </body>
    </html>
        `; //글쓰기 화면 만들기(링크 만들기: create)
}

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; //우리가 필요한 게 /create 인지 확인하고 싶으면 console.log(pathname);을 이 코드 아래에 적어 확인해 볼 수 있다.
  if (pathname === '/') {
    if (querydata.id === undefined) {
      fs.readdir('./data', function (err, filelist) {
        var title = 'Welcome';
        var description = 'Hello, Node.js';
        var list = templateList(filelist);
        var template = templateHTML(title, list, `<h2>${title}</h2>${description}`);
        response.writeHead(200);
        response.end(template);
      })
    } else {
      fs.readdir('./data', function (err, 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}`);
          response.writeHead(200);
          response.end(template);
        });
      });
    }
  } else if (pathname === '/create') { //이 if 위에 해당이 안되고, else if()에도 해당이 안되면 아래 else가 실행된다.
    fs.readdir('./data', function (err, filelist) { //undefined 부분과 비슷하게 사용되니까 복붙해서 가져오고 수정함.
      var title = 'WEB - create'; // create에 맞는 이름으로 변경하고 description 부분은 필요가 없으니 삭제.
      var list = templateList(filelist);
      var template = templateHTML(title, list,
        `<form action="http://localhost:3000/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>`); //form.html에 적은 형식을 복붙. placeholder=""은 그 부분의 txt에 이런 것을 적으라고 명시해주는 것(가이드 라인). 
      response.writeHead(200);
      response.end(template);
    }); //이전 html에서 본 것과 같이 잘 실행. 하지만 404가 뜬다(아직 처리를 안 해서): 브라우저의 검사를 통해 웹 브라우저와 서버가 주고받는 데이터를 볼 수 있다. network란의 form data를 보면 우리가 전송한 것들이 host 방식으로 은밀하게 전송된 것을 볼 수 있다.
  } else {
      response.writeHead(404);
      response.end('not found');
    };
  });
app.listen(3000);
Comments