Promise에서 Unhandled Rejection 설명

web/node.js|2018. 11. 9. 23:16

Rejection 프로미스에서 발생하는 에러를 칭한다. Es6에서는 비동기 동작의 상태 표현으로 "pending", "fulfilled", "rejected" 세가지가 정의 되었다.


pending 비동기 작업중인 것을 나타내고 fulfilled 비동기 동작이 완료된것을 표현한다. 그리고 rejected 비동기가 실패한 것을 표현해준다. 

promise에서 작업이 성공할 경우에는 resolve 실행하고 실패할 경우에는 reject 수행한다.


그럼 Promise에서 에러가 발생했을 때는 어떻게 처리하는게 좋을 ?

대부분은 에러가 발생했을 때는 promise chaining 사용해서 .catch(err => { } ); 잡을 있다.


그럼 이런 경우에도 정상적으로 잡힐까?


1
2
new Promise((_, reject) => reject(new Error('woops'))).
  catch(error => { console.log('caught', err.message); });
cs


잡히지 않고 결국 Unhandled 에러가 발생한다. 그럴까? 대부분은 error 잡힐꺼 같은데 안잡히는 이유가 몰까? 이유는 우리가 잡으려고 했던 error 위에서 발생하는 것이기 때문에 reject catch 설정을 해줘야 문제없이 처리가 가능하다.


1
2
3
4
5
6
7
8
9
new Promise((_, reject) => reject(new Error('woops'))).
  catch(error => new Promise((resolve, reject) => {
    sentry.captureMessage(error.message, function(error) {
      if (error) {
        return reject(error);
      }
      resolve();
    });
  }));
cs


비동기 상태에서 에러처리는 참 쉽지 않다.


댓글()

node js에서 stream pipe 사용시 에러 처리 방법

web/node.js|2018. 11. 9. 22:28

node js에서 데이터를 stream을 사용하여 처리하고 pipe를 사용해서 계속해서 stream을 가지고 작업을 이어나갈 수 있다. 그런데 pipe를 통해서 작업을 진행하다 보니까 중간에 오류가 발생했을 때 try / catch 로는 정상적으로 처리하지 못하는 경우가 발생했다. 

나에 경우에는 에러가 발생했을 때 try / catch에서 잡히지 않아서 프로그램이 Unhandled Promise Rejections를 출력 하며 죽어버렸다.

그 예는 다음과 같이 request를 통해서 받은 이미지를 sharp 라이브러리를 통해서 이미지 크기를 변경하려고 할 때 발생했다.

1
try {
await request('https://image.toast.com/aaaaab/ticketlink/TKL_3/ion_main08061242.jpg').pipe(transformer).pipe(writeStream);
} catch (e) {
console.error(e);
}
cs


그래서 이를 처리하기 위해서 알아봤는데 각 파이프라인에서 발생하는 에러를 처리하기 위해서는 try/catch로만 잡을 수가 없다. 그래서 이를 해결하기 위해서 각 파이프 앞단에서 error 이벤트를 잡는 설정을 해줘야한다.


1
2
3
4
5
6
7
8
// 파일로 쓰기
await request('https://image.toast.com/aaaaab/ticketlink/TKL_3/ion_main08061242.jpg').on('error', function (e) {
  console.error(e);
}).pipe(transformer).on('error', function (e) {
  console.error(e);
}).pipe(writeStream).on('error', function (e) {
  console.error(e);
});
cs


하지만 이렇게만 하면 에러는 잡을 수 있어도 pipe에서 행이 걸리는 경우가 발생된다. 그래서 에러가 발생했을 때 행 걸리지 않고 다음 로직으로 정상적으로 처리되도록 하기 위해서는 this.emit('end')를 넣어줘야 한다.


1
2
3
4
5
6
7
8
9
10
11
// 파일로 쓰기
await request('https://image.toast.com/aaaaab/ticketlink/TKL_3/ion_main08061242.jpg').on('error', function (e) {
  console.error(e);
  this.emit('end');
}).pipe(transformer).on('error', function (e) {
  console.error(e);
  this.emit('end');
}).pipe(writeStream).on('error', function (e) {
  console.error(e);
  this.emit('end');
});
cs


참고

https://stackoverflow.com/questions/21771220/error-handling-with-node-js-streams


댓글()

node.js에서 sharp를 사용해서 이미지 크기 변경하기

web/node.js|2018. 10. 22. 09:27

node.js에서 이미지 크기를 변경하기 위해서 임시 파일에 데이터를 다운로드 받고 파일 크기를 변경하는 작업을 보통 진행한다.


하지만 이번에는 html stream으로 임시로 내려받은 버퍼를 사용해서 사이즈를 변경하고 바로 s3에 전송하거나 파일로 내보내는 작업을 진행해보려한다.


이미지의 사이즈를 변경하기 위해서 필요한 라이브러리는 sharp이다. 

1
npm install sharp
cs

https://www.npmjs.com/package/sharp


먼저 request 요청으로 내려받은 image데이터를 sharp로 이미지를 변경하고 파일로 내보내보자.

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
'use strict';
 
const request = require('request-promise');
const sharp = require('sharp');
const fs = require('fs');
const Stream = require('stream').Transform;
 
class ImageResize {
 
  async imageResize() {
 
    const transformer = sharp().resize(250211);
    const writeStream = fs.createWriteStream('test.jpg');
 
    // fs.writeaStream으로 파일로 쓰기
    await request('https://image.toast.com/aaaaab/ticketlink/TKL_3/ion_main08061242.jpg').pipe(transformer).pipe(writeStream);
 
    // stream 처리를 통해 파일로 직접 쓰기
    this.streamToFile(await request('https://image.toast.com/aaaaab/ticketlink/TKL_3/ion_main08061242.jpg').pipe(transformer));
  }
 
  streamToFile (stream) {
    const chunks = new Stream();
    return new Promise((resolve, reject) => {
      stream.on('data', chunk => chunks.push(chunk));
      stream.on('error', reject);
      stream.on('end', () => fs.writeFileSync('tt.jpg', chunks.read()));
    });
  }
};
 
module.exports = new ImageResize();
cs

코드를 보면 알겠지만 생각보다 간단하다. request 요청으로 받은 html stream을 sharp로 넘겨서 처리하고 그리고 나온 스트림을 writeStream으로 넘겨서 처리하거나 별도의 스트림 처리를 설정해서 파일로 저장할 수 있다.


추가적으로 파일로 내보내는 작업이 아닌 s3로 이미지 파일을 내보내기 위해 이미지 크기가 변경된 stream을 buffer로 바꾸는 작업을 해보자. 

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
'use strict';
 
const request = require('request-promise');
const sharp = require('sharp');
const fs = require('fs');
const Stream = require('stream').Transform;
 
class ImageResize {
 
  async imageResize() {
 
    const transformer = sharp().resize(250211);
    const writeStream = fs.createWriteStream('test.jpg');
 
    // s3로 전송 (String)
    let dd1 = await this.streamToString(await request('https://image.toast.com/aaaaab/ticketlink/TKL_3/ion_main08061242.jpg').pipe(transformer));
 
    s3.upload({
        Body : ddl 
    });
  }
 
  streamToString (stream) {
    const chunks = [];
    return new Promise((resolve, reject) => {
      stream.on('data', chunk => chunks.push(chunk));
      stream.on('error', reject);
      stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')))
    });
  }
 
};
 
module.exports = new ImageResize();
cs


node.js stream을 이해하면 생각보다 어렵지 않은 작업으로 이미지를 임시로 저장하지 않고 바로 크기를 변경할 수 있다.


소스 git repository

https://github.com/weduls/nodejs_study


참고

https://stackoverflow.com/questions/12740659/downloading-images-with-node-js

https://stackoverflow.com/questions/10623798/writing-node-js-stream-into-a-string-variable

https://stackoverflow.com/questions/13979558/saving-an-image-stored-on-s3-using-node-js


댓글()

node.js에서 aws s3 스토리지에 이미지 저장하기

web/node.js|2018. 10. 6. 01:55

AWS에서 제공하는 S3 스토리지는 다양한 파일을 bucket에 보관할 수 있다. 자세한 내용은 생활코딩 강의 참고하면 좋다. https://opentutorials.org/course/608/3006


그럼 우선 aws sdk를 설치하여야 한다.

1
npm i aws-sdk --save
cs


그리고 AWS IAM에서 생성한 사용자가 있어야 한다. 사용자는 S3에 대한 권한을 가지고 있어야 한다.


사용자를 생성하고 권한을 부여하는 기능은 어렵지 않기때문에 검색해서 적용하면 된다.

그리고 해당 사용자를 생성하면 csv 파일로 secretkey를 받을 수 있다. 이를 AWS S3에 접근하여 사용하기 위해서 사용된다.




기본 정보가 담긴 config.json 생성

우선 부여 받은 사용자 accessKeyId와 secretAccessKey 그리고 지역정보가 담긴 config.json 파일을 만든다.

1
2
3
4
5
{
  "accessKeyId""<accessKeyId>",
  "secretAccessKey""<secretAccessKey>",
  "region""ap-northeast-2"
}
cs


그리고 aws-sdk 라이브러리를 로드하면서 위에 생성한 config.json 파일을 추가해준다. 그리고 S3 객체의 경우 로드한 aws-sdk에서 new와 함께 s3() 메소드를 실행하여 만들 수 있다.

1
2
3
4
5
6
'use strict';
 
const AWS = require('aws-sdk');
const path = require('path');
AWS.config.loadFromPath(path.join(__dirname, 'config.json'));
const S3 = new AWS.S3();
cs


그리고 이미지 파일을 업로드 하기위해서 제공하는 메소드인 upload에는 bucket 이름, 파일의 고유 키, 파일이 담긴 Body, Body의 contentType 정보가 필요하다. 나는 이미지를 올리려고 하기 때문에 contentType은 image/jpg로 하였다.

1
2
3
4
5
   const params = {};
    params.Key = `image/${md5(url)}.jpg`;
    params.Bucket = bucket;
    params.Body = await request(url);
    params.contentType = 'image/jpg';
cs

생성된 최종 코드는 다음과 같다.

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
'use strict';
 
const AWS = require('aws-sdk');
const path = require('path');
AWS.config.loadFromPath(path.join(__dirname, 'config.json'));
const S3 = new AWS.S3();
const md5 = require('md5');
const request = require('request-promise');
const bucket = 's3-buckets';
 
class AwsS3 {
 
  async upload(url)  {
    const params = {};
    params.Key = `image/${md5(url)}.jpg`;
    params.Bucket = bucket;
    params.Body = await request(url);
    params.contentType = 'image/jpg';
 
    S3.upload(params, function (err, data) {
      console.log(err);
      console.log(data);
    });
  }
 
}
 
module.exports = new AwsS3();
 
cs


그리고 테스트 코드를 작성하여 동작 시켜보면 정상적으로 파일이 올라간것을 확인 할 수 있다.

1
2
3
4
5
6
7
8
9
'use strict';
 
const s3 = require('../../lib/s3');
 
describe('s3 test', () => {
  it('upload', async () => {
    await s3.upload('http://ticketimage.interpark.com/Play/image/large/18/18009670_p.gif');
  });
});
cs


 이런 문제가 발생하면 config.json에 기재한 키 정보를 다시한번 확인해봐야한다.
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
35
{ InvalidAccessKeyId: The AWS Access Key Id you provided does not exist in our records.
    at Request.extractError (/Users/wedul/Documents/repository/nodejs/node_modules/aws-sdk/lib/services/s3.js:580:35)
    at Request.callListeners (/Users/wedul/Documents/repository/nodejs/node_modules/aws-sdk/lib/sequential_executor.js:109:20)
    at Request.emit (/Users/wedul/Documents/repository/nodejs/node_modules/aws-sdk/lib/sequential_executor.js:81:10)
    at Request.emit (/Users/wedul/Documents/repository/nodejs/node_modules/aws-sdk/lib/request.js:683:14)
    at Request.transition (/Users/wedul/Documents/repository/nodejs/node_modules/aws-sdk/lib/request.js:22:10)
    at AcceptorStateMachine.runTo (/Users/wedul/Documents/repository/nodejs/node_modules/aws-sdk/lib/state_machine.js:14:12)
    at /Users/wedul/Documents/repository/nodejs/node_modules/aws-sdk/lib/state_machine.js:26:10
    at Request.<anonymous> (/Users/wedul/Documents/repository/nodejs/node_modules/aws-sdk/lib/request.js:38:9)
    at Request.<anonymous> (/Users/wedul/Documents/repository/nodejs/node_modules/aws-sdk/lib/request.js:685:12)
    at Request.callListeners (/Users/wedul/Documents/repository/nodejs/node_modules/aws-sdk/lib/sequential_executor.js:119:18)
    at Request.emit (/Users/wedul/Documents/repository/nodejs/node_modules/aws-sdk/lib/sequential_executor.js:81:10)
    at Request.emit (/Users/wedul/Documents/repository/nodejs/node_modules/aws-sdk/lib/request.js:683:14)
    at Request.transition (/Users/wedul/Documents/repository/nodejs/node_modules/aws-sdk/lib/request.js:22:10)
    at AcceptorStateMachine.runTo (/Users/wedul/Documents/repository/nodejs/node_modules/aws-sdk/lib/state_machine.js:14:12)
    at /Users/wedul/Documents/repository/nodejs/node_modules/aws-sdk/lib/state_machine.js:26:10
    at Request.<anonymous> (/Users/wedul/Documents/repository/nodejs/node_modules/aws-sdk/lib/request.js:38:9)
    at Request.<anonymous> (/Users/wedul/Documents/repository/nodejs/node_modules/aws-sdk/lib/request.js:685:12)
    at Request.callListeners (/Users/wedul/Documents/repository/nodejs/node_modules/aws-sdk/lib/sequential_executor.js:119:18)
    at callNextListener (/Users/wedul/Documents/repository/nodejs/node_modules/aws-sdk/lib/sequential_executor.js:99:12)
    at IncomingMessage.onEnd (/Users/wedul/Documents/repository/nodejs/node_modules/aws-sdk/lib/event_listeners.js:294:13)
    at emitNone (events.js:111:20)
    at IncomingMessage.emit (events.js:208:7)
    at endReadableNT (_stream_readable.js:1064:12)
    at _combinedTickCallback (internal/process/next_tick.js:139:11)
    at process._tickDomainCallback (internal/process/next_tick.js:219:9)
  message: 'The AWS Access Key Id you provided does not exist in our records.',
  code: 'InvalidAccessKeyId',
  region: null,
  time: 2018-10-01T15:53:23.570Z,
  cfId: undefined,
  statusCode: 403,
  retryable: false,
  retryDelay: 32.0943451721504 }
 
cs


참고 URL
https://www.npmjs.com/package/aws-sdk
https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/s3-example-creating-buckets.html

https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/loading-node-credentials-json-file.html

댓글()

node.js 애플리케이션 프로세스 관리 도구 매니저

web/node.js|2018. 10. 6. 01:38

PM2 built-in 되어있는 Load Balancer 함께 node.js 애플리케이션의 runtime and process 관리 기능을  제공해 주는 라이브러리이다. PM2를 이용하여 application 죽지 않고 돌아갈 수있게 watch 기능을 제공해주는 기능도 제공한다.

그리고 error.log, info.log, 자원 점유 등등 여러 기능을 제공해준다.

간단하게 설치부터 사용법에 대해 정리해보자.


설치
$ npm install pm2 -g


실행방법
$ pm2 start app.js


사용법은 아주 간단하다. 위와 같이 command로 프로그램을 실행할 때 여러 옵션도 줄 수 있다.

1
2
// wedul_app.js 를 max_space_size를 500으로 지정하고 실행하라.
pm2 start wedul_app.js —node_args=“—max_old_space_size=500”
cs


두 개 이상의 옵션도 부여할 수 있다.

1
2
// max_space_size와 harmony(es 2015 옵션)를 둘다 사용.
pm2 start wedul_app.js --node-args="--max_old_space_size=500 harmony"  
cs


하지만 실제 운영 에서는 옵션이 한두가지가 아니기 때문에 계속 command로 설정을 지정 하기에는 부담이 된다. 

그래서 별도의 json 파일에 옵션값들을 정의할 수 있다.

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
{
  "apps": [
    {
      "name": "wedul_batch",
      "script": "./batch/index.js",
      "watch": true,
      "node_args":"--harmony",
      "env": {
        "NODE_ENV": "development"
      },
      "env_production": {
        "NODE_ENV": "production"
      }
    },
    {
      "name": "wedul",
      "script": "./bin/www",
      "watch": true,
      "node_args":"--harmony",
      "env": {
        "NODE_ENV": "development"
      },
      "env_production": {
        "NODE_ENV": "production"
      }
    }
  ]
}
cs


위의 옵션은 각 실행시키고자 하는 애플리케이션의 이름과 위치 그리고 옵션등을 지정할 수 있다. 

위와 같이 설정하고 아래 명령어로 실행시킨다.

 pm2 start pm2test.json --name wedul_batch --env development

이렇게 실행하면 pm2test.json 옵션에 맞게 실행이 된다.( --env 옵션을 사용하면 이전에 공부했었던 process.env.NODE_EVN 전역변수값을 사용할 수 있다.)

이렇게 모든 설정을 하고 실행하면 다음과 같은 화면으로 실행된다.

그리고 더 여러 기능들을 제공하는데 그 중 몇가지만 살펴보자.


# detail (특정 애플리케이션 현재 상태 확인)

1
pm2 show [appname]
cs



# 실시간 로그 및 자원 현황 모니터링

1
pm2 monit
cs


#로그 확인

1
pm2 log [appname]
cs


댓글()
  1. BlogIcon node 2018.11.02 19:06 댓글주소  수정/삭제  댓글쓰기

    watch로 소스코드 변경 후 재시작 기능도 확인할 수 있어요.
    http://pm2.keymetrics.io/docs/usage/watch-and-restart/

  2. node 2018.12.26 14:40 댓글주소  수정/삭제  댓글쓰기

    cluster기능을 통해 싱글 코어에서 이벤트 루프를 받던 방식을 멀티 코어로 바꿀수도 있어요

node.js로 구글 스프레드시트에 접속하여 데이터 가져오기

web/node.js|2018. 10. 5. 00:24

자신의 구글 드라이브에 있는 엑셀 (스프레드시트)에 접속하여 데이터를 가져오는 방법을 알아보자.

우선 google-spreadsheet에 접근할 수 있는 Js를 다운로드 받자.

1
npm i google-spreadsheet --save
cs

https://www.npmjs.com/package/google-spreadsheet


구글 스프레드 시트 만들기
구글 드라이브에 스프레드 시트를 만들자. 저 주소 URL에 블록처리되어있는 부분이 바로 키로 사용된다


구글 스프레드 시트 접속권한 사용자 생성

그리고 이 스프레드 시트에 접속할 수 있는 계정을 만들어야한다. https://console.developers.google.com 이곳에 접속하여 사용자를 만들고 키를 생성한다. (단순하게 들어가서 확인해보면 무리없이 만들 수 있다.)


키 만들기 버튼을 이용해서 키를 생성하면 해당 키드는 json 파일로 떨어지는데 이곳에 private_pw와 ID등이 들어있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
// 가짜 데이터
{
  "type""service_account",
  "project_id""ringed-dispatch-157300",
  "private_key_id""0467e31241242428c12412421412449234960ae3c749e1609c5e3ca53b41",
  "private_key""-----BEGIN PRIVATE KEY-----\8iBnuPwiI\n5/ZUYY3yv2n5+UAs+dLVAYo=\n-----END PRIVATE KEY-----\n",
  "client_email""820847001811221412412214124236-compute@developer.gserviceaccount.com",
  "client_id""1060476412312214212323ㄴㅇㅁㄹㄴㅇ5255744659718",
  "auth_uri""https://accounts.google.com/o/oauth2/auth",
  "token_uri""https://oauth2.googleapis.com/token",
  "auth_provider_x509_cert_url""https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url""https://www.googleapis.com/robot/v1/metadata/x509/820847001836-compute%40developer.gserviceaccount.com"
}
cs

그리고 이 사용자에게 해당 문서의 권한을 공유 해준다.


Node.js로 접근하기

google-spreadsheet 라이브러리를 가져와서 사용자 정보를 입력하고 URL KEY를 이용해서 접속하면 해당 스프레드 시트에 접속할 수 있고 조작 및 정보를 가지고 올 수 있다.

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
/**
 * Created by we on 2018. 9. 5.
 */
'use strict';
 
const GoogleSpreadsheet = require('google-spreadsheet');
const defer = require('../defer');
 
// spreadsheet key is the long id in the sheets URL
const config = require('../../config/config');
 
class WedulData {
 
  constructor() {
    this.doc = new GoogleSpreadsheet(config.google.key);
  }
 
  async _initDoc() {
    await defer(this.doc, this.doc.useServiceAccountAuth)(config.google.credit);
    return await defer(this.doc, this.doc.getInfo)();
  }
 
  async readSheetData() {
    const sheet = (await this._initDoc()).worksheets[0];
    const rows = await defer(sheet, sheet.getRows)({offset: 1, limit: 100});
 
    return rows;
  }
 
}
module.exports = new WedulData();
cs


결과


댓글()

router 경로 enumset 처럼 받는 방법과 테스트 사이트

web/node.js|2018. 10. 5. 00:20

router.get('/wedul/:name(cjung|gglee), () => {
});

이런식으로 기재되어 있는 router 주소 매핑을 책에서 봤다. 자세한 설명이 없어서 알아보던 중 /wedul/ 주소 다음에 cjung 또는 gglee가 올 수 있는 형태라고 한다.

이걸 찾아보면서 router 경로를 만들고 테스트 까지 해볼 수 있는 사이트를 찾았다.


http://forbeslindesay.github.io/express-route-tester/


Route에 규칙을 추가하고 path에 테스트할 규칙을 입력하면 key와 RegExp 그리고 결과까지 확인 할 수 있도록 제공해준다.

좋다.



댓글()

node.js에 swagger 적용

web/node.js|2018. 10. 5. 00:13

Spring Boot에 적용했었던 swagger를 node.js에도 적용해보자.

spring boot에서는 자동으로 만들어졌으나, node.js에서는 Definition을 적용해줘야해서 귀찮다.
설정 방법을 알아보자.

설치 패키지

1
2
"swagger-jsdoc": "^3.0.2",
"swagger-ui-express": "^4.0.0"
cs



Definition정의

swagger에 대해 적용할 프로그램에 대한 정보와 path, api들 위치등에 대해 정의한 definition을 정의한다.
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
/**
 * Created by wedul on 2018. 8. 30.
 */
'use strict';
 
module.exports = {
  swaggerDefinition: {
    // 정보
    info: {
      title: 'node js test app',
      version: '1.0.0',
      description: 'Make For node js test.'
    },
    // 주소
    host: "localhost:3000",
    // 기본 root path
    basePath: "/",
    contact: {
      email: "rokking1@naver.com"
    },
    // 각 api에서 설명을 기록할 때 사용할 constant들을 미리 등록해놓는것
    components: {
      res: {
        BadRequest: {
          description: '잘못된 요청.',
          schema: {
            $ref: '#/components/errorResult/Error'
          }
        },
        Forbidden: {
          description: '권한이 없슴.',
          schema: {
            $ref: '#/components/errorResult/Error'
          }
        },
        NotFound: {
          description: '없는 리소스 요청.',
          schema: {
            $ref: '#/components/errorResult/Error'
          }
        }
      },
      errorResult: {
        Error: {
          type: 'object',
          properties: {
            errMsg: {
              type: 'string',
              description: '에러 메시지 전달.'
            }
          }
        }
      }
    },
    schemes: ["http""https"], // 가능한 통신 방식
    definitions:  // 모델 정의 (User 모델에서 사용되는 속성 정의)
      {
        'User': {
          type: 'object',
          properties: {
            id: {
              type: 'string'
            },
            age: {
              type: 'integer'
            },
            addr: {
              type: 'string'
            }
          }
        }
      }
  },
  apis: ['./routes/**/*.js'// api 파일 위치들 
};
cs


api 주소
각 라우터에 대한 정보를 적어주어야 swagger-ui에서 정의된대로 나온다.

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
35
36
37
38
39
40
41
/**
 * @swagger
 * tags:
 *   name: User
 *   description: 사용자 정보 가져오기
 */
module.exports = router;
 
/**
 * @swagger
 * /user/:
 *   get:
 *     summary: 사용자 정보 가져오기
 *     tags: [User]
 *     parameters:
 *       - in: query
 *         name: id
 *         type: string
 *         enum: [cjung, gglee, etc..]
 *         description: |
 *          사용자 아이디 전달
 *     responses:
 *       200:
 *         description: 성공
 *       403:
 *         $ref: '#/components/res/Forbidden'
 *       404:
 *         $ref: '#/components/res/NotFound'
 *       500:
 *         $ref: '#/components/res/BadRequest'
 */
router.get('/', async (req, res, next=> {
  const {id} = req.query;
  let data = await userService.findUser(new UserDto.ParamBuilder(id).build());
 
  if (data) {
    res.json(data);
  } else {
    return next(new NotFoundError('잘못된 요청입니다.'));
  }
});
cs


app.js
app.js에 Definition을 정의하고 swaggerSpec이랑 swaggerUI관련 설정을 해주면된다.

1
2
3
4
5
6
const swaggerJSDoc = require('swagger-jsdoc');
const swaggerOption = require('./routes/swagger');
const swaggerSpec = swaggerJSDoc(swaggerOption);
const swaggerUi = require('swagger-ui-express');
 
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec));
cs


접속
http://localhost:3000/api-docs/ 에 접속하면 접속한 화면이 나온다.


#
설정한 router 정보

각 router에서 정의한 api 주소 정보가 확인된다.


또한 테스트도 진행할 수 있다. 파라미터 값을 선택하고 execute 버튼을 누르면 실제 테스트를 진행하고결과까지 보여 준다.


그리고 함께 정의하였던 response 정보도 확인할 수 있다.


마지막으로 Definion 문서 마지막에 정의하였던 각 Model의 Definition도 확인할 수 있다. 


자세한 문서는 여기 참고
https://frontalnh.github.io/2017/11/22/nodejs-swagger-api-doc-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0/

https://github.com/swagger-api/swagger-editor


위의 예제 코드는 github 참고
https://github.com/weduls/nodejs_test


## 참고로 swagger를 쓰지 않고 테스트에서 super-test 라이브러리를 사용해서 테스트할수도 있다. 하지만 ui가 없고 front나 다른 개발자한테 api 정보를 넘겨주고 테스트까지 가능하게 해주는 swagger가 좋은거 같다. (super-test는 하단의 링크 참고)


댓글()
  1. ㅇㅇ 2019.01.16 16:02 댓글주소  수정/삭제  댓글쓰기

    위의 예제코드 깃헙 링크가 404 not found 뜹니다..

  2. lll 2019.10.09 15:10 댓글주소  수정/삭제  댓글쓰기

    에구. ㅠㅠ 혹시 다시 예제를 올려주실 생각 없나요? 설명 잘 적으셨지만, 그래도 소스로 참고하고 싶네요