상세 컨텐츠

본문 제목

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

학부/캡스톤(a.k.a 졸작)

by ranlan 2021. 10. 10. 02:04

본문

728x90

 

전날에는 tensorflow.js를 한번 훑어보고 다음 날 노드 공부를 시작하였다.

이전에 노드 공부하며 들었던 인프런 강의 중 괜찮았던 강의가 몇 개 생각나 다시 들을 예정

나 이렇게나 인프런 잘 이용하는데 인프런에서 상이라도 줘야되는거 아닌가 ~..

그나저나 공부해야할게 참으로 많 군 요 허허

 


 

 [Inflearn]  테스트주도개발(TDD)로 만드는 NodeJS API 서버 - 김정환

 

테스트주도개발(TDD)로 만드는 NodeJS API 서버 - 인프런 | 강의

javascript 언어로 백엔드 개발을 할 수 있는 NodeJS 를 학습해 봅니다. 테스트 주도 개발 (TDD) 방법으로 NodeJS를 이용해 API 서버 개발을 배워보고 학습할 수 있는 강좌입니다. NodeJS의 대표적인 웹프레

www.inflearn.com

 

 

노드(NodeJS)

node.js는 확장성 있는 네트워크 애플리케이션(특히 서버 사이드) 개발에 사용되는 소프트웨어 플랫폼이다.

작성 언어로 자바스크립트를 활용하며 논블로킹(Non-blocking) I/O단일 스레드 이벤트 루프를 통한 높은 처리 성능을 가지고 있다.

내장 HTTP 서버 라이브러리를 포함하고 있어 웹 서버에서 아파치 등의 별도의 소프트웨어없이 동작이 가능하며 이를 통해 웹 서버의 동작에 더 많은 통제를 가능케 한다.

(출처: 위키백과)

 

노드에서 모듈(module)이란?

노드로 개발한 어플리케이션을 이루는 기본 조직

- 기본 모듈 : node.js에서 기본적으로 제공하는 모듈

- 써드파티(third-party) 모듈 : 노드 모듈 패키지 관리 툴(NPM)로 설치한 모듈, node_modules 폴더 아래 설치됨

- 사용자 정의 모듈

module.exports = {
    function1 : function1,
    function2 : function2,
    ....
};

 

노드의 특징

1. 브라우저 밖에서 자바스크립트 코드 실행 가능

2. 크롬 v8 엔진 사용

3. 이벤트 기반의 비동기 I/O 프레임워크

1) 싱글 스레드로 동작하는 이벤트 큐에 요청 저장

2) 가벼운 이벤트의 경우 이벤트 루프에서 처리

3-1) 무거운 이벤트(파일 읽고 쓰기, 네트워크 등 시간이 다소 걸리는 이벤트)의 경우 논블록킹 워커 스레드로 보내 처리

3-2) 처리 후 다시 이벤트 큐에 전달

 

4. CommonJS를 구현한 모듈 시스템

노드는 파일 형태로 모듈을 관리할 수 있는 CommonJS로 구현

const module = require('module');

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

 

노드 vs 자바스크립트

javascript는 브라우저를 통해서만 작동하며 주로 클라이언트 개발을 위해 사용되어왔음 (브라우저 콘솔창을 통해 실행됨)

이랬던 javascript를 서버사이드로 옮겨와 브라우저 밖에서 사용할 수 있도록 한 것이 바로 node

노드를 통해 자바스크립트로 서버 개발이 가능해짐!

 

 

http 모듈 이용하여 어플리케이션 만들기

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 등이 있음)

npm install express

 

어플리케이션(application)

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

 

미들웨어(middle ware)

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

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

- express 웹 어플리케이션 생성과 미들웨어 예제

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!');
})

 - 에러 미들웨어 예제

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 객체를 이용하여 라우팅도 가능)

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

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

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

 

 

테스트 주도 개발(Test Driven Development)

 모카(Mocha)  nodejs 테스트 러너를 지원하는 테스트 프레임워크

 Should  검증(assertion) 라이브러리로 assert 모듈을 불러오지 않고도 여러가지 검증 메서드 사용 가능, 가독성을 높혀줌

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

 

[mocha] https://mochajs.org/

 

Mocha - the fun, simple, flexible JavaScript test framework

 

mochajs.org

[should] https://github.com/tj/should.js/

 

GitHub - tj/should.js: BDD style assertions for node.js -- test framework agnostic

BDD style assertions for node.js -- test framework agnostic - GitHub - tj/should.js: BDD style assertions for node.js -- test framework agnostic

github.com

[supertest] https://github.com/visionmedia/supertest

 

GitHub - visionmedia/supertest: 🕷 Super-agent driven library for testing node.js HTTP servers using a fluent API.

🕷 Super-agent driven library for testing node.js HTTP servers using a fluent API. - GitHub - visionmedia/supertest: 🕷 Super-agent driven library for testing node.js HTTP servers using a fluent API.

github.com

 

npm 모듈 설치

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

package.json 의존성

"dependencies": {
    ...
  },
"devDependencies": {
  "mocha": "^9.1.2",
  "should": "^13.2.3",
  "supertest": "^6.1.6"
}

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

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

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

module.exports = {
    function1 : function1,
    function2 : function2,
    ...
};

테스트 파일 생성 -> ~.spec.js~.js에 대한 테스트 코드 파일

 

* 모카(mocha) 메서드

- describe() : 테스트의 범위 설정

- it() : 단위테스트 설정

- done() : 비동기 단위 테스트 완료를 알려줌

 

* 검증(assertion)

- assert 모듈 이용

/* 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 모듈 이용 (가독성↑)

const utils = require('./utils');
require('should') 

describe('utils.js모듈의 capitalize 함수는', () => {
    it('문자열의 첫번째 문자를 대문자로 반환한다', () => {
        const result = utils.capitialize('hello');
        result.should.be.equal('Hello')
    })
});

-> 검증 성공

 

-> 검증 실패

 

 

 

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

index.js

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

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가 없을 시 오류 발생

 

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

// index.js에 추가
req.query.limit = req.query.limit || 10;

 

검증 성공 시

 

 

 

++ )  morgan  모듈 이용하여 주석 남기기

개발 의존성 모듈로 설치

npm install morgan --save-dev

스크립트 추가

const morgan = require('morgan');

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

 

 


 

오랜만에 웹 플밍하니까 재밌는거 같기도하고..

얼른 익혀서 캡스톤 해야쥐

 

 

728x90

관련글 더보기

댓글 영역