세리프 따라잡기

nodejs 객체와 모듈에 대해 본문

Node.js

nodejs 객체와 모듈에 대해

맑은 고딕 2021. 1. 10. 20:55

1. 자바 스크립트의 문법 중 하나인 객체(object)에 대해 알아보자.

  객체(Object) 배열(Array)
공통점 이 둘은 정보를 정리·정돈하는 수납 상자라고 생각하면 된다.
차이점 - 순서가 없는 정보를 저장하기에 최적화 됐다.
- 숫자로 식별자로 주는 것이 아닌, 이름으로 식별자를 줄 수 있다.
- 순서에 따라서 정리를 한다.
- 각각의 정보들은 고유한 식별자가 있고, 그 식별자는 숫자이다.
// 배열 예시
var members = ['malgun', 'gothic', 'tistory']; //배열 안에 정보가 담긴 것, 그냥 순서대로 데이터를 넣으면 된다.
console.log(members[1]); //gothic 출력

// 객체 예시
var roles = { //배열의 리터럴은 대괄호, 객체는 중괄호이다.
    'programmer': 'malgun',
    'designer': 'gothic',
    'manager': 'tistory'
}; //객체는 데이터를 그냥 넣는 것이 아닌, 각각의 데이터마다 고유한 이름을 준다.
console.log(roles.designer); //gothic 출력 + 객체는 부를 때 .을 찍어서 부른다.

2. 이번엔 객체에 담겨 있는 데이터를 하나씩 꺼내와서 반복문으로 처리하는 방법을 알아보자. (while, for문)

// 배열
var members = ['malgun', 'gothic', 'tistory'];
console.log(members[1]);
var i = 0;
while (i < members.length) {
    console.log('array loop:', members[i]); //'array loop:' 부분은 출력에서 구분하려고. 의미는 X.
    i += 1;
}

// 객체
var roles = {
    'programmer': 'malgun',
    'designer': 'gothic',
    'manager': 'tistory'
};
console.log(roles.designer); //객체에 대한 정보를 가져올때, .key값을 적어도 되지만, 배열처럼 roles['designer']라고 해도 된다.

for(var name in roles){ //객체는 어떻게 루프와 함께 사용하는지: for문 처음엔 변수(객체의 식별자=key 가 들어오도록 약속됨)를, in 이라는 약속된 키워드(value)를 꼭 써줘야 한다. 반복적으로 처리하려고 하는 객체를 in 부분에 넣어준다.
    console.log('object:', name, 'value:', roles[name]); //'object:'와 'value:'는 구분을 위함(의미X) + value를 얻기위한 코드 추가= roles[키]를 하면, 그 키에 해당되는 정보를 가지고 온다.
} // 정상 출력~

3. 객체 지향 프로그래밍(Object Oriented Programming)의 스타일, 기능들을 살펴보자.

  • 값으로서의 함수
var f=function () { //함수는 처리해야 할 일에 대한 정보를 담고 있는 일종의 구문이면서 동시에 값이다. 함수를 변수에 넣을 수 있다면 = 값이다.
    console.log(1 + 1);
    console.log(1 + 2);
}
console.log(f);
f(); //변수 f를 함수로 하고 실행시키면 된다. = 즉 값이다.

// var i= if(true){console.log(1)}; //js에선 조건문이라는 구문이 값이 아니라서 오류가 난다.
// var w = while(true){console.log(1)}; //while 구문 또한 값이 될 수 없어서 오류가 난다.
var f = function () {
    console.log(1 + 1);
    console.log(1 + 2);
} //함수는 서로 연관된 데이터를 그룹핑하는 객체
var a=[f]; //배열에 f라는 원소가 담겼는데, 이 f=함수이다.
a[0](); //이렇게 하면 f가 되고 f는 함수기 때문에 2, 3이 출력된다.
//즉 배열의 원소로서 함수가 존재할 수 있다.

var o = {
    func:f
} //객체의 원소(프로퍼티)로 f를 주고
o.func(); //이렇게 코드를 쓰면 func:f가 실행되고 f는 함수이기 때문에 2,3이 출력된다.
//즉 js에선 배열과 객체 모두 연관된 데이터를 담는 그릇인데, 처리방법을 그룹핑하는 함수조차도 데이터라 배열, 객체에 담을 수 있다.

보통 함수를 배열보다는 객체에 많이 담는다. 이름을 정해주기 때문에 꺼내기가 좋기 때문이다.

  • 데이터와 처리 방법을 담는 그릇으로서 객체
//1억줄이 넘는 코드를 프로젝트하고 있다고 생각해보자~

/*
var v1 = 'v1'; 라고 할때 다른 사람이
~100,000,000 code~
v1 = 'gothic'; 이다 라고 정의 해버리면 = v1이 gothic이라고 출력되는 상황이 벌어짐. 이걸 해결해주는게 객체.
var v2 = 'v2'; 
*/

var p = { //연관된 것들을 정리하기.
    v1: 'v1',
    v2: 'v2', //이렇게 v1, v2를 담아주면 문제없다!
    f1: function () { //아래의 f1, f2 함수를 이렇게 객체로 정리해준다.
        console.log(this.v1); //처음엔 변수 o 여서 o.v1이라고 호출했는데, 변수가 후에 바뀔 수가 있다. 이럴땐 아래 계속
    },
    f2: function () {
        console.log(this.v2); //함수와 같은 함수가 객체 안에서 사용될 때 참조할 수 있는 키워드(this)를 써준다.
    },
}

/*
function f1(){
    console.log(o.v1);
}
function f1(){
    ~code~
}
function f2(){
    console.log(o.v2);
} f1, f2라는 함수를 정의하고 쓰는데, 누군가 f1이라는 똑같은 함수를 사용했을 때! 이 또한 객체를 이용해 객체의 멤버로 해주면 된다.
*/

p.f1();
p.f2();

이렇듯 객체는 서로 연관된 것들을 정리해주고 그룹핑을 통해 코드의 복잡성을 획기적으로 낮추는 역할을 해준다!

 

4. template 기능 정리정돈하기.

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

var template = { //templateㅁㅁ이라고 적혀있던 함수들을 정리해준다.
  html: function (title, list, body, control) { //객체에 있는 값 하나하나를 property(프로퍼티)라고 한다.
    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>
          `;
  },
  list: function (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;
  }
} //정리를 해주었으니, 아래에서 templateㅁㅁ을 이용해 짠 코드들을 다시 수정해준다.

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 (err, filelist) {
        var title = 'Welcome';
        var description = 'Hello, Node.js';
        var list = template.list(filelist); //templatelist → template.list로 호출. 아래의 html도 수정.
        var html = template.html(title, list, `<h2>${title}</h2>${description}`, `<a href="/create">create</a>`); //template로 쓰던 변수 이름을 html로 바꿔주고
        response.writeHead(200);
        response.end(html); //template가 아닌 html로 수정.
      }) //홈페이지 부분 수정. 성공했다면 정상 출력이 된다. 그렇다면 이후 templateㅁㅁ들을 다 수정해주면 된다.
    } else {
      fs.readdir('./data', function (err, filelist) {
        fs.readFile(`data/${querydata.id}`, 'utf8', function (err, description) {
          var title = querydata.id;
          var list = template.list(filelist);
          var html = template.html(title, list, 
            `<h2>${title}</h2>${description}`, 
            `<a href="/create">create</a> <a href="/update?id=${title}">update</a> 
            <form action="delete_process" method="post">
            <input type="hidden" name="id" value="${title}">
            <input type="submit" value="delete">
            </form>`);
          response.writeHead(200);
          response.end(html);
        });
      });
    }
  } else if (pathname === '/create') {
    fs.readdir('./data', function (err, filelist) {
      var title = 'WEB - create';
      var list = template.list(filelist);
      var html = template.html(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(html);
    });
  } else if(pathname === '/create_process'){
    var body = '';
    request.on('data', function(data){
      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 (err, filelist) {
      fs.readFile(`data/${querydata.id}`, 'utf8', function (err, description) {
        var title = querydata.id;
        var list = template.list(filelist);
        var html = template.html(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(html);
      });
    }); 
  } else if(pathname === '/update_process'){
    var body = '';
    request.on('data', function(data){
      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(err){
        fs.writeFile(`data/${title}`, description, 'utf8', function(err){
          response.writeHead(302, {location: `/?id=${title}`});
          response.end();
        });
      })
    });
  } else if(pathname === '/delete_process'){
    var body = '';
    request.on('data', function(data){
      body += data;
    });
    request.on('end', function(){
      var post = qs.parse(body);
      var id = post.id;
      fs.unlink(`data/${id}`, function(err){
        response.writeHead(302, {location: `/`});
        response.end();
      })
    });
  } else {
      response.writeHead(404);
      response.end('not found');
    };
  });
app.listen(3000); //성공적으로 정리가 되었다면 이전과 같이 잘 동작하는 것을 볼 수 있다.

이 코드들이 이전과 비교해 동작 방법이 다르지 않다(똑같다). 동작 방법은 유지하면서 내부의 코드를 더 효율적으로 바꾸는 행위를 리펙토링(refactoring)이라고 한다. 처음부터 함수, 객체와 같은 코드를 짜는 것은 쉬운 일이 아니다. 우리가 알고 있는 최소한의 문법으로 투박하더라도 잘 동작이 된다면, 그 뒤에 반복되는 코드들을 다시 보며 자주 리펙토링을 하는 게 좋다. (처음부터 이상적인 코드를 짜려고 하면 한 줄도 안 짜질 수 있다.)

 

5. 모듈(Module)

정리정돈 하는 객체가 많아지면 이런 객체를 또 정리정돈할 수 있는 더 큰 틀의 도구가 모듈이다.

  • 형식
// file name: mpart일 때, 아래 코드는 mpart에 쓴 내용

var m = { //객체, 함수가 많다고 가정해보자.
    v:'v',
    f:function(){
        console.log(this.v);
    }
}

module.exports = m; //우리가 만들고 있는 모듈이 담겨있는 mpart.js에 여러 기능들 중 m이 가리키는 저 객체를 모듈 바깥에서 사용할 수 있도록 exports하겠다는 의미.
// file name: 2일 때, 아래는 2 file의 내용.
var part = require('./mpart.js') //가져올때 require를 쓴다.
// console.log(part); - 확인 작업. part라는 변수는 모듈을 로딩한 결과가 담겨있고, 이 안에는 mpart에서 모듈로 한 객체들이 담겨있다.
part.f(); //f에 담긴 것 꺼내기. v 출력.
  • 활용 (위의 4번에서 한 var template = 에 해당하는 부분을 새로운 파일 lib/template.js에 옮겨 모듈로 실행)
// lib/template.js에 속한 파일
module.exports = { //이렇게 써줘도 모듈을 외부로 보낼 수 있다.
    html: function (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>
            `; //title 부분의 web1 부분을 수정해 잘 동작되는지 체크 = 잘 된다!
    },
    list: function (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;
    }
  }

* lib = library의 줄임말 (폴더명)

// 이전에 길었던 부분을 lib/template.js에 잘라넣고, 이곳의 코드는 간편하게 바꿔준다.
var template = require('./lib/template.js'); //모듈 불러오기.

이렇게 해주고 잘 동작하는지 확인해주면, 메인 파일이 정상 실행이 되는 것을 볼 수 있다.

Comments