Home 캡스톤일지 | ~ 10.05 NodeJS TDD 웹개발 훑어보기(1)
Post
Cancel

캡스톤일지 | ~ 10.05 NodeJS TDD 웹개발 훑어보기(1)

전날에는 tensorflow.js를 한번 훑어보고 다음 날 노드 공부를 시작하였다. 이전에 노드 공부하며 들었던 인프런 강의 중 괜찮았던 강의가 몇 개 생각나서 다시 들어볼 예정. 나 이렇게나 인프런 잘 이용하는데 인프런에서 상이라도 줘야되는거 아닌가 ~..
그나저나 공부해야할게 참으로 많 군 요 허허



📹 [Inflearn] 테스트주도개발(TDD)로 만드는 NodeJS API 서버 - 김정환 https://www.inflearn.com/course/%ED%85%8C%EC%8A%A4%ED%8A%B8%EC%A3%BC%EB%8F%84%EA%B0%9C%EB%B0%9C-tdd-nodejs-api/dashboard

노드(NodeJS)

node.js는 확장성 있는 네트워크 애플리케이션(특히 서버 사이드) 개발에 사용되는 소프트웨어 플랫폼이다.
작성 언어로 자바스크립트를 활용하며 논블로킹(Non-blocking) I/O와 단일 스레드 이벤트 루프를 통한 높은 처리 성능을 가지고 있다.
내장 HTTP 서버 라이브러리를 포함하고 있어 웹 서버에서 아파치 등의 별도의 소프트웨어없이 동작이 가능하며 이를 통해 웹 서버의 동작에 더 많은 통제를 가능케 한다. (출처: 위키백과)

노드에서 모듈(module)이란? 노드로 개발한 어플리케이션을 이루는 기본 조직

  • 기본 모듈 node.js에서 기본적으로 제공하는 모듈
  • 써드파티(third-party) 모듈 노드 모듈 패키지 관리 툴(NPM)로 설치한 모듈, node_modules 폴더 아래 설치됨
  • 사용자 정의 모듈
    1
    2
    3
    4
    5
    
    module.exports = {
      function1 : function1,
      function2 : function2,
      ....
    };
    

노드 특징

  • 브라우저 밖에서 자바스크립트 코드 실행 가능
  • 크롬 v8 엔진 사용
  • 이벤트 기반의 비동기 I/O 프레임워크
    image
    1. 싱글 스레드로 동작하는 이벤트 큐에 요청 저장
    2. 가벼운 이벤트의 경우 이벤트 루프에서 처리
    3. 무거운 이벤트(파일 읽고 쓰기, 네트워크 등 시간이 다소 걸리는 이벤트)의 경우 논블록킹 워커 스레드로 보내 처리
    4. 처리 후 다시 이벤트 큐에 전달
  • CommonJS를 구현한 모듈 시스템 노드는 파일 형태로 모듈을 관리할 수 있는 CommonJS로 구현
    1
    
      const module = require('module');
    

    ES6에서는 자바스크립트 자체 ES6 Module이라는 이름으로 모듈화를 지원하기 시작 > import

노드 vs 자바스크립트

javascript는 브라우저를 통해서만 작동하며 주로 클라이언트 개발을 위해 사용되어왔음 (브라우저 콘솔창을 통해 실행됨)
이랬던 javascript를 서버사이드로 옮겨와 브라우저 밖에서 사용할 수 있도록 한 것이 바로 node
노드를 통해 자바스크립트로 서버 개발이 가능해짐!

http 모듈 이용한 어플리케이션

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const http = require('http');

const hostname = '127.0.0.1'; // host
const port = 3000; // port

const server = http.createServer((req, res) => {

  // routing
  if(req.url == '/') {
    res.statusCode = 200;
    res.setHeader('Content-Type', 'text/plain');
    res.end('Hello World!\n');
  } else if (req.url == '/users') {
    res.statusCode = 200;
    res.setHeader('Content-Type', 'text/plain');
    res.end('users\n');
  }else {
    res.statusCode = 404;
    res.end('Not Found\n')
  }
  
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

Express JS

node.js로 REST 서버를 편리하게 구현할 수 있도록 해주는 웹 프레임워크 중 하나 (그 외 Koa, Hapi 등이 있음)

1
npm install express

어플리케이션(application)
익스프레스 인스턴스로 서버에 필요한 기능을 추가하고 라우팅 설정, 서버를 요청 대기 상태로 만들 수 있다.

미들웨어(middle ware)
요청(req)과 응답(res) 객체, 요청-응답 중 다음 미들웨어 함수에 대한 액세스 권한을 갖는 함수로 구조 내에서 중간 처리를 위한 함수

next()로 다음 함수를 호출하지 않을 경우 에러 발생

  • express 웹 어플리케이션 생성과 미들웨어 예제
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
      const express = require('express');
      const app = express();
    
      function logger(req, res, next) {
          console.log('i am logger');
          next(); // 다음 실행
      }
    
      function logger2(req, res, next) {
          console.log('i am logger2');
          next();
      }
    
      app.use(logger2);
      app.use(logger);
      // i am logger2 -> i am logger
    
      app.listen(3000, function() {
          console.log('Server is Running!');
      })
    
  • 에러 미들웨어 예제
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    
      const express = require('express');
      const app = express();
    
      // 일반 미들웨어
      function commonmw(req, res, next) {
          console.log('commonmw');
          next(new Error('error occured'));
      }
    
      // 에러 미들웨어
      function errormw(err, req, res, next) {
          console.log(err.message);
          next();
      }
    
      app.use(commonmw);
      app.use(errormw); // error occured (commonmw error message)
    
      app.listen(3000, function() {
          console.log('Server is Running!');
      })
    

    에러가 발생할 때 앞에서 발생한 에러 객체를 인자로 받아서 사용

라우팅(routing)
요청 url에 대해 적절한 핸들러 함수로 연결해주는 기능 (Router 객체를 이용하여 라우팅도 가능)

1
2
3
4
5
6
7
8
9
10
11
app.use('/', function(req, res) {
  // '/' 주소로 들어오는 모든 HTTP 메소드 요청 처리
});

app.get('/', function(req, res) {
  // '/' 주소로 들어오는 GET 메소드 요청 처리
});

app.post('/data', function(req, res) {
  // '/data' 주소로 들어오는 POST 메소드 요청 처리
});

테스트 주도 개발(Test Driven Dev.)

  • 모카(Mocha) 
    nodejs 테스트 러너를 지원하는 테스트 프레임워크
    📄 https://mochajs.org/

  • Should
    검증(assertion) 라이브러리로 assert 모듈을 불러오지 않고도 여러가지 검증 메서드 사용 가능, 가독성을 높혀줌
    📄 https://github.com/tj/should.js/

  • SuperTest 
    익스프레스 통합 테스트용 라이브러리, 내부적으로 익스프레스 서버를 가동시켜 실제로 요청을 보낸 뒤 결과 검증
    📄 https://github.com/visionmedia/supertest

npm 모듈 설치

1
2
3
4
# 개발 의존성 모듈로 설치
npm install mocha --save-dev
npm install should --save-dev
npm install supertest --save-dev

package.json 의존성

1
2
3
4
5
6
7
8
"dependencies": {
    ...
  },
"devDependencies": {
  "mocha": "^9.1.2",
  "should": "^13.2.3",
  "supertest": "^6.1.6"
}

package.json 테스트 스크립트 추가 (mocha 테스트코드파일.js)

1
2
3
4
"scripts": {
  "test": "mocha index.spec.js", // npm test
  "start": "node index.js" // npm start
},

테스트할 js파일 마지막에 exports 추가

1
2
3
4
5
module.exports = {
    function1 : function1,
    function2 : function2,
    ...
};

테스트 파일 생성

~.spec.js~.js 에 대한 테스트 코드 파일

  • 모카(mocha) 메서드
    • describe() : 테스트의 범위 설정
    • it() : 단위테스트 설정
    • done() : 비동기 단위 테스트 완료를 알려줌
  • 검증(assertion)
    • assert 모듈 이용
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      
        /* utils.js
        function capitialize(str) {
            return str.charAt(0).toUpperCase() + str.slice(1);
        }
        */
      
        const utils = require('./utils');
        const assert = require('assert')
      
        describe('utils.js모듈의 capitalize 함수는', () => {
            it('문자열의 첫번째 문자를 대문자로 반환한다', () => {
                const result = utils.capitialize('hello');
                assert.equal(result, 'Hello');
            })
        });
      
    • should 모듈 이용 (가독성↑)
      1
      2
      3
      4
      5
      6
      7
      8
      9
      
        const utils = require('./utils');
        require('should') 
      
        describe('utils.js모듈의 capitalize 함수는', () => {
            it('문자열의 첫번째 문자를 대문자로 반환한다', () => {
                const result = utils.capitialize('hello');
                result.should.be.equal('Hello')
            })
        });
      
  • 검증 성공 image
  • 검증 실패 image


예제) /users?limit=limit 만큼 사용자 목록 조회
index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
const express = require('express')
const app = express()
const morgan = require('morgan');
const port = 3000;

app.use(morgan('dev'));

// user list
var users = [
    {id: 1, name: 'alice'},
    {id: 2, name: 'bek'},
    {id: 3, name: 'chris'},
]

app.get('/users', (req, res) => {
    const limit = parseInt(req.query.limit, 10); // 정수로 타입 변경(10진수)

    if (Number.isNaN(limit)) {
        // limit이 정수가 아닐 경우 parseInt() 결과 NaN 반환
        return res.status(400).end();
    }
    
    res.json(users.slice(0, limit)); //res.body
});

app.listen(port, () => {
  console.log(`Example app listening at http://localhost:${port}`)
});

// test
module.exports = app;

index.spec.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
describe('GET /users는', () => {
    describe('성공 시', () => {
        it('1-1) 유저 객체를 담은 배열을 응답한다.', (done) => {
            request(app)
                .get('/users')
                .end((err, res) => {
                    console.log(res.body);
                    res.body.should.be.instanceOf(Array);
                    done();
                });
        });

        it('1-2) 최대 limit 갯수만큼 응답한다', (done) => {
            request(app)
                .get('/users?limit=2')
                .end((err, res) => {
                    console.log(res.body);
                    res.body.should.have.lengthOf(2)
                    done();
                });
        });
    });
    // -- 성공시

    describe('2) 실패 시', () => {
        it('limit이 숫자형이 아니면 400을 응답한다.', (done) => {
            request(app)
                .get('/users?limit=two')
                .expect(400)
                .end(done);
        })
    }) 
    
})
  • limit가 없을 시 오류 발생
    image

  • 해결
    limit가 없을 때 기본값 10으로 설정

    1
    2
    
      // index.js에 추가
      req.query.limit = req.query.limit || 10;
    
  • 검증 성공 시
    image

morgan  모듈 이용하여 주석 남기기
개발 의존성 모듈로 설치

1
npm install morgan --save-dev

스크립트 추가

1
2
3
const morgan = require('morgan');

app.use(morgan('dev'));

image



오랜만에 웹 플밍하니까 재밌는거 같기도하고.. 얼른 익혀서 캡스톤 해야쥐

This post is licensed under CC BY 4.0 by the author.

캡스톤일지 | ~ 10.04 생활코딩으로 간단히 살펴본 TensorflowJS

캡스톤일지 | ~ 10.13 미디어파이프 이용한 정적제스처 인식과 문제 발생