'node'에 해당되는 글 17건

web/node.js

node.js에서 multer 사용하여 이미지 업로드 및 텍스트 파일 다루기

Multer는 파일 업로드를 위해서 사용되는 multipart/form-data를 다루기 위한 node.js 미들웨어이다. busyboy를 기반으로 하고 있다.
자세한 내용은 https://github.com/expressjs/multer/blob/master/doc/README-ko.md 이곳에서 참고하면 된다.

그럼 간단하게 multipart/form-data로 올린 이미지 파일과 텍스트파일을 request post로 받아서 처리하는 코드를 만들어보자.

우선 multer를 설치한다.
npm i multer

그리고 이미지 파일을 특정 경로에 저장해놓고 사용할 수 있지만 나는 메모리 스토리지를 사용해서 조작하는 방식으로 진행해보겠다.

multer 라이브러리를 선언하고 memoryStorage를 사용할 수 있도록 추가적으로 선언해준다.

1
2
3
const multer = require('multer');
const storage = multer.memoryStorage();
const upload = multer({ storage });
cs

그리고 router에 미들웨어로 upload.single('productImage')를 넣어서 productImage 필드로 넘어온 값을 버퍼로 가져오도록 한다. array, fields 등등 다른 메소드들도 있으나 나는 이미지가 하나라서 single을 사용했다.

1
2
3
4
5
6
7
8
9
router.post('/test', upload.single('productImg'), async (req, res, next) => {
    try {
      console.log(req.file);
      res.json(req.body);
    } catch (e) {
      next(e);
    }
  });
 
cs

그럼 postman을 통해서 데이터를 보내보자.


전송정보

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"method": "POST",
"url": "/info/test",
"headers": {
"authorization": "Bearer 8c6076013db8af716df89b1b48b90c9b2b0fad6c",
"content-type": "multipart/form-data; boundary=--------------------------820616490467091968093229",
"cache-control": "no-cache",
"postman-token": "217b466f-1f40-402e-b2cd-153426c61cc7",
"user-agent": "PostmanRuntime/7.6.0",
"accept": "*/*",
"host": "127.0.0.1:8081",
"accept-encoding": "gzip, deflate",
"content-length": "97764",
"connection": "keep-alive"
},
"body": {},
"query": {},
"level": "info",
"message": "Access"
}
 
cs

이렇게 전송하고 debug로 전송된 정보를 체크해보면 다음과 같이 출력된다.

이 정보를 이용해서 text 파일들은 파일대로 다루고 이미지 파일은 버퍼를 사용해서 s3에 저장을 하던지 여러가지 동작을 진행할 수 있다.

web/node.js

Nginx, Apache 그리고 node.js 성능 관련 잡다한 정리

Nginx과 Apache 비교


 

 Nginx

Apache 

특징 

- Nginx는 싱글 스레드 Event driven 방식

- 미리 설정된 worker 프로세스 안에서 요청이 들어올 때 마다 요청을 분배하여 worker에게 역할을 분배

- 기존에 정해놓은 리소스를 사용하기 때문에 CPU, Memory 등의 자원 사용률이 낮음

- 요청이 올 때마다 쓰레드를 생성하여 할당한다. 작업이 많아질 경우 많은 쓰레드할당이 필요하다. 그리고 쓰레드들이 작업을 진행 할 때마다  CPU를 사용하려 하기 때문에 문맥교환이 자주 발생된다. 

차이점 

 - Apache에 경우 Blocking 방식으로 Network, DB 등 별도의 동작이 진행 될 때 Block되지만 Nginx는 Non-blocking 방식을 지원함


Non-blocking, Event Loop 방식의 Node.js


- Event Loop 방식은 요청이 오면 무조건 이벤트 핸들러에게 요청을 넘김. 그 것이 처리되면 완료되었다고 연락이온다. 그래서 Single Thread Non-blocking 방식이 가능

- Single Thread이기 때문에 Multi thread의 자원에 대한 크리티컬 섹션에 대한 문제가 발생하지 않음.

- Node.js는 Single Thread를 사용하기 때문에 CPU에 문맥교환이 발생하지 않아서 Multi Thread를 지원하는 프레임워크보다 무조건 빠를다고 하지만 그렇지는 않다. Node.js에서 요청은 비동기를 지원하지만 내부적으로 Event를 처리할 때는 libio의 Thread Pool에 의해 동작하기 때문에 I/O 작업이 많을 때는 Multi Thread 방식이 유리하다.(Node.js는 메시지 처리와 같이 I/O가 적은 작업이 어울림.) 

- Single Thread이기 때문에 CPU를 많이 잡아먹는 연산일 경우 다른 작업도 모두 느려지기 때문에 연산이 오래걸리는 작업에는 어울리지 않음.

- Node.js에 특성상 오류가 발생하면 프로그램이 죽어버림

- Single Thread Async를 지원하고 있지만 네트워크, DB와 같은 I/O가 발생할 때는 libio의 Thread Pool에 작업이 전달되고 완료되면 EventQueue에 CallBack 함수에 전달된다.


=> 결론 

Single Thread를 사용. 비동기지만 I/O 발생 시 내부적으로 비동기처럼 동작하기 위해 쓰레드 풀을 사용하여 진행한다. 그래서 요청이 많을 경우 문맥교환이 일어나는 Multi Thread보다 오히려 성능이 나쁠 수 있다.


Nginx와 Node.js

Nginx와 Node.js를 사용할 때 성능적으로 도움이 되는 설정을 정리했다.


Port Range

- Nginx에서 Node.js에 요청을 보낼 때는 2개의 TCP 소켓이 필요하다.(2개의 포트가 필요) 그래서 가용 가능한 포트가 적을 경우에 요청을 받지 못하는 문제가 발생한다. 그래서 상황에 따라서 sysctl의 net.ipv4.ip_local_port_range를 사용하여 설정한다.


Time Wait

- Time wait는 커넥션이 종료 되었으나 할당된 연결이 아직 release가 되지 않은 자원이다. 그래서 요청이 와도 자원을 사용할 수 없어서 문제가 발생할 수 있어서 이를 사용할 수있게 해줘야 한다. sysctl의 net.ipv4.tcp_tw_reuse값을 1로 변경해줘야 한다.


Time Wait 시간

- Time Wait 자원을 사용할 수 있는 자원으로 변경되는 시간을 줄여서 빠르게 사용할 수 있게 해주는 것이 좋다. sysctl의 net.ipv4.tcp_fin.timeout값을 줄이면 된다.


Context Switch 

Node.js에서 사용중인 CPU 코어가 다른 프로세스에서 사용되지 않도록하면 성능 향상에 도움이 된다. 여러 프로세스에서 하나의 코어를 같이 사용할 수 있다. 각각의 프로세스는 돌아가면서 코어 스케줄대로 이용한다. 이 경우 문맥교환이 많이 발생하여 CPU 부하가 증가한다. 그렇기 때문에 node에서 사용하는 CPU 코어는 다른 업무를 못하게하여 서로 코어를 잡아먹으려 하는 문맥교환을 줄여야한다.

※ Context switch

- 문맥 교환(Context Switch)이란 하나의 프로세스가 CPU를 사용 중인 상태에서 다른 프로세스가 CPU를 사용하도록 하기 위해, 이전의 프로세스의 상태(문맥)를 보관하고 새로운 프로세스의 상태를 적재하는 작업을 말한다.


현재 Connection 상태를 확인하는 명령어

netstat -tan | awk '{print $6}' | sort | uniq -c


Nginx에서 Upstream이란?

Nginx 설정에서 Upstream이 있다. 여기서 Upstream은 순차적으로 서비스를 처리하기 위해 사용되는 서버를 의미한다. Nginx에서 내장된 Upstream 모듈은 설정된 서버들의 부하분산, 속도 개선을 담당한다. 일반적으로 설정된 서버의 Upstream은 라운드 로빈 방식으로 진행된다.


※ 라운드 로빈

라운드 로빈 스케줄링(Round Robin Scheduling, RR)은 시분할 시스템을 위해 설계된 선점형 스케줄링의 하나로서, 프로세스들 사이에 우선순위를 두지 않고, 순서대로 시간단위(Time Quantum)로 CPU를 할당하는 방식의 CPU 스케줄링 알고리즘이다.


web/node.js

sequelize에서 상황에 따라 조인 테이블을 변경하는 방법 - alias

sequelize를 사용하다 보면 하나의 필드를 어떤 상황에서는 User테이블과 조인하고 다른 상황에서는 Dept 테이블과 조인을 하고 싶을 때가 있다.

예를 들면 만약 로그를 기록 한다 하였을 때 모든 행위마다 로그를 기록할 수는 없기때문에 로그 테이블에 필드에 type을 나누고 발생한 object의 index만을 기록해서 진행하고 싶을 때 그럴 수있다.


그럴경우 targetIndex의 경우 동작 타입에 따라 User테이블과 조인할 수도 있고 Dept 테이블과 조인하는 등 유동적인 조치가 필요하다.


우선 Model을 define 하는 부분을 확인해보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const Pub = Sequelize.define('pub', {
  name: { type: Sequelize.STRING },
targetIdx: { type: Sequelize.INTEGER }.
  address: { type: Sequelize.STRING },
  latitude: {
    type: Sequelize.INTEGER,
    allowNull: true,
    defaultValue: null,
    validate: { min: -90, max: 90 }
  },
  longitude: {
    type: Sequelize.INTEGER,
    allowNull: true,
    defaultValue: null,
    validate: { min: -180, max: 180 }
  }
}

Pub.association = function(models) {
Pub.belongTo(models.User, {
target: 'targetIdx',
foreign: 'userId',
as: 'UserDefines'
});
Pub.belongTo(models.Dept, {
target: 'targetIdx',
foreign: 'deptId',
as: 'DeptDefines'
});
};
cs


모델을 define할때 조인이 필요한 테이블과의 관계를 미리 설정을 해둬야한다. alias를 미리 설정해줘야 필요에 따라 조인하고 싶은 연결 조건을 가져다 쓸 수 있다. 물론 별칭을 정의 하지 않으면 모델명 그 자체 이름으로 자동지정된다.


쿼리 사용 부분

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Dept 테이블과 조인할 때
await This.Pub.findOne({
  attributes: [['name', 'userName'], 'targetIdx'],
  include: [
    { 
        model: this.Dept, 
        required: false
        as: 'DeptDefines'
    }
  ]
});
 
// 사용자 테이블과 조인할 때
await This.Pub.findOne({
  attributes: [['name', 'userName'], 'targetIdx'],
  include: [
    { 
        model: this.User, 
        required: false
        as: 'UserDefines'
    }
  ]
});
cs


필요한 부분에 정확하게 as를 지엉해주고 model에 조인하고 싶은 테이블을 필요에 따라 지정해주면 된다. 이때 연관관계가 만들어지면 안되기 때문에 define시에 constraint옵션을 false로 지정하고 하면 더욱 좋다.


sequelize ORM이 점점 익숙해져간다.

web/node.js

sequelize에서 alias 사용하여 검색하는 방법

sequelize에서 검색을 할 때 alias를 줘서 검색하는 방법은 다음과 같다. 

생각보다 간단하다.


1
2
3
await This.User.findOne({
  attributes: ['id', ['name''userName']] //id, first AS firstName
});
cs


web/node.js

sequelize 사용시 테이블 이름 변동없이 고정 Alias 사용방법

sequelize는 마찬가지로 ORM을 사용하다보니 직접적으로 쿼리를 사용하는 것보다 정확하게 알지못하면 역시 개발속도도 늦어지고 문제가 많아지는 단점이 있다.


이번에는 sequelize를 사용하는데 조인할 때 테이블 이름이 갑자기 User에서 Users로 바뀌는 이슈가 발생했다.


이 이슈를 해결하기 위해서 sequelize Document를 검색했고 거기서 freeTableName 옵션을 발견했다.

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 Bar = sequelize.define('bar', { /* bla */ }, {
  // don't add the timestamp attributes (updatedAt, createdAt)
  timestamps: false,
  // don't delete database entries but set the newly added attribute deletedAt
  // to the current date (when deletion was done). paranoid will only work if
  // timestamps are enabled
  paranoid: true,
 
  // don't use camelcase for automatically added attributes but underscore style
  // so updatedAt will be updated_at
  underscored: true,
  // disable the modification of table names; By default, sequelize will automatically
  // transform all passed model names (first parameter of define) into plural.
  // if you don't want that, set the following
  freezeTableName: true,
 
  // define the table's name
  tableName: 'my_very_custom_table_name',
  // Enable optimistic locking.  When enabled, sequelize will add a version count attribute
  // to the model and throw an OptimisticLockingError error when stale instances are saved.
  // Set to true or a string with the attribute name you want to use to enable.
  version: true
})
cs

설명 그대로 이름이 plural로 바뀌는 것을 방지 하고 singular로 사용할 수 있게 해주는 옵션이다.



 [ 1 ]  [ 2 ]  [ 3 ]  [ 4 ] 

푸터바

알림

이 블로그는 구글에서 제공한 크롬에 최적화 되어있고, 네이버에서 제공한 나눔글꼴이 적용되어 있습니다.

카운터

  • Today : 13
  • Yesterday : 460
  • Total : 82,704