NODEJS] AWS의 S3에 파일 업로드 다운로드 구현

AWS EC2 서버에서 공지사항에 들어가는 첨부 파일을 업로드 하고 다운로드 하는 기능이 필요 했다.

다음의 순서로 처리하면 쉽게 된다.

1. 사용자 생성 하기

1. AWS IAM에서 사용자를 생성 하고, 애세스 유형을 프로그램 방식으로 선택

사용자 추가
{: .image-left}

이후 결과로 Access key ID, Secret access key를 알고 있어야 한다.

2. 권한으로 AmazonS3FullAccess를 할당

사용자 권한
{: .image-left}

2. nodejs에서 aws-sdk 사용하기

# 프로젝트 초기화
$> npm init

# aws-sdk 모듈 설치
$> npm install aws-sdk --save

3. bucket 생성 하기

createBucket.js

const AWS = require('aws-sdk');
const ID = 'Access key Id';
const SECRET = '';
const BUCKET_NAME = '';
const s3 = new AWS.S3({
  accessKeyId: ID,
  secretAccessKey: SECRET
});
const params = {
  Bucket: BUCKET_NAME,
  CreateBucketConfiguration: {
      // Set your region here
      LocationConstraint: "ap-northeast-2"
  }
};

s3.createBucket(params, function(err, data) {
  if (err) console.log(err, err.stack);
  else console.log('Bucket Created Successfully', data.Location);
});

4. 파일 업로드

uploadFile.js

const fs = require('fs');
const AWS = require('aws-sdk');
const BUCKET_NAME = '';
const s3 = new AWS.S3({
  accessKeyId: '',
  secretAccessKey: ''
});
const uploadFile = (fileName) => {
  const fileContent = fs.readFileSync(fileName);
  const params = {
      Bucket: BUCKET_NAME,
      Key: 'test.txt', // File name you want to save as in S3
      Body: fileContent
  };
  s3.upload(params, function(err, data) {
      if (err) { throw err; }
      console.log(`File uploaded successfully. ${data.Location}`);
  });
};
uploadFile('./uploads/test.txt');

5. 파일 다운로드

download.js

const fs = require('fs');
const AWS = require('aws-sdk');
const BUCKET_NAME = '';
const s3 = new AWS.S3({
  accessKeyId: '',
  secretAccessKey: ''
});
const downloadFile = (fileName) => {
  const params = {
      Bucket: BUCKET_NAME,
      Key: 'test.txt', // File name you want to save as in S3
  };
  s3.getObject(params, function(err, data) {
      if (err) {
          throw err;
      }
      fs.writeFileSync(fileName, data.Body.toString());
  });
};
downloadFile('../../uploads/test-download.txt');

참고자료

Posted by lahuman

댓글을 달아 주세요

javascript에서 제공되는 console methods

오늘 javascript 강좌를 보다가 다음과 같은 코드를 보았다.

console.time('Label');
... 성능측정(시간 측정)을 하려는 코드
console.timeEnd('Label');

... 결과
Label: 13998.528076171875ms

time이라는 method는 인자로 받은 문자열을 기준으로 실행 시간의 시작을 의미하고, timerEnd method는 인자로 받은 문자열을 기준으로 실행 시간을 화면에 표출하고 종료 한다.
이외에 timeLog도 제공하며 시작이후 지금가지 시간을 표기한다.

console.time('Label');
... 성능측정(시간 측정)을 하려는 코드

console.timeLog('Label');
... 출력
Label: 7327.245849609375ms

console.timeEnd('Label');

... 결과
Label: 13998.528076171875ms

기본적으로 console의 info, debug, trace, log, warn 과 같이 로그 레벨을 기준으로 사용하거나, 특별하게 dir(object 형식으로 출력) method 정도 사용해보았다.

금일 알게된건 console에 더 많은 옵션들이 존재 한다는 것이다.

console의 옵션 정리

  • Console.assert()
    • 첫 번째 매개변수가 false인 경우 메시지와 스택 추적을 출력합니다.
  • Console.clear()
    • 콘솔의 내용을 지웁니다.
  • Console.count()
    • 주어진 라벨로 메서드를 호출한 횟수를 출력합니다.
  • Console.countReset()
    • 주어진 라벨의 횟수를 초기화합니다.
  • Console.debug()
    • "debug" 중요도로 메시지를 출력합니다.

참고: Chromium 브라우저는 58 버전 이후 브라우저 콘솔의 "Verbose" 레벨을 선택하지 않으면 이 메서드의 출력 결과가 보이지 않습니다.

  • Console.dir()

    • 주어진 JavaScript 객체의 속성 목록을 상호작용 가능한 형태로 표시합니다. 속성 값이 다른 객체라면 펼쳐서 살펴볼 수 있습니다.
  • Console.dirxml()

    • 객체를 XML/HTML 요소 형태로 나타낼 수 있으면 그렇게 표시하고, 아닐 경우 JavaScript 객체 형태로 표시합니다.
  • Console.error()

    • 오류 메시지를 출력합니다. 추가 매개변수와 함께 문자열 치환을 사용할 수 있습니다.
  • Console.exception() 표준 아님, 사용하지 않기를 권고

    • error()의 별칭입니다.
  • Console.group()

    • 새로운 인라인 그룹을 생성해, 이후 모든 출력을 한 단계 들여씁니다. 그룹을 나오려면 groupEnd()를 호출하세요.
  • Console.groupCollapsed()

    • 새로운 인라인 그룹을 생성해, 이후 모든 출력을 한 단계 들여씁니다. 그러나 group()과 달리, groupCollapsed()로 생성한 그룹은 처음에 접혀 있습니다. 그룹을 나오려면 groupEnd()를 호출하세요.
  • Console.groupEnd()

    • 현재 인라인 그룹을 나옵니다.
  • Console.info()

    • 정보 메시지를 출력합니다. 추가 매개변수와 함께 문자열 치환을 사용할 수 있습니다.
  • Console.log()

    • 일반 메시지를 출력합니다. 추가 매개변수와 함께 문자열 치환을 사용할 수 있습니다.
  • Console.profile()

    • 브라우저의 내장 프로파일러(Firefox 성능 측정 도구 등)를 실행합니다. 선택 사항으로 프로파일에 이름을 붙일 수 있습니다.
  • Console.profileEnd()

    • 프로파일러를 멈춥니다. 프로파일 결과는 브라우저의 성능 측정 도구(Firefox 성능 측정 도구 등)에서 확인할 수 있습니다.
  • Console.table()

    • 표 형태의 데이터를 표에 그립니다.
  • Console.time()

    • 주어진 이름의 타이머를 실행합니다. 하나의 페이지에서는 최대 10,000개의 타이머를 동시에 실행할 수 있습니다.
  • Console.timeEnd()

    • 지정한 타이머를 멈추고, 소요시간을 출력합니다.
  • Console.timeStamp() 표준 아님

    • 브라우저의 타임라인이나 워터폴에 마커를 추가합니다.
  • Console.trace()

    • 스택 추적을 출력합니다.
  • Console.warn()

    • 경고 메시지를 출력합니다. 추가 매개변수와 함께 문자열 치환을 사용할 수 있습니다.

몰라서 쓰지 못했던 옵션이 6개도 넘는다. 앞으로 프로젝트에서 많이 사용해보자!

출처

Posted by lahuman

댓글을 달아 주세요

pm2 로그를 관리하는 모듈을 이용하자

pm2는 Nodejs에서 프로세스를 관리 하는 모듈이다.

아주 많이 사용되고 있으며 주요 기능은 프로세스 관리 및 모니터링을 제공한다. 또한 사용법이 간단하다.

# 설치 
$> npm install pm2 -g 
# 사용
$> pm2 start app.js

6개월 가량 운영하던 서비스에서 디스크 FULL 메시지를 받고 가장 많이 사용중인 파일을 확인하였는데, pm2 로그 파일이었다.

기본 설정을 사용시, 로그 파일은 한개만 생성되고 계속 사이즈가 증량되는 방식으로 제공한다.

pm2 로그를 관리 하기 위하여 검색 결과 pm2-logrotate를 찾았다.
pm2 모듈로, 설치는 다음과 같이 하면 된다.

#설치
$> pm2 install pm2-logrotate

[PM2][Module] Module downloaded
[PM2][WARN] Applications pm2-logrotate not running, starting...
[PM2] App [pm2-logrotate] launched (1 instances)
== pm2-logrotate ==
┌────────────────┬─────────────────────┐
│ key            │ value               │
├────────────────┼─────────────────────┤
│ max_size       │ 10M                 │
│ retain         │ 30                  │
│ compress       │ false               │
│ dateFormat     │ YYYY-MM-DD_HH-mm-ss │
│ workerInterval │ 30                  │
│ rotateInterval │ 0 0 * * *           │
│ rotateModule   │ true                │
└────────────────┴─────────────────────┘

설치를 하면 다음과 같은 기본 설정이 반영 된다.

파일 한개의 최대 크기는 10M 이고 파일은 30개만 남는다. 파일명은 dateFormat을 기반으로 표시 되고 매일 1개의 파일이 생성된다.

설정을 변경하려면 다음과 같은 명령어를 이용하면 된다.

# (1KB)
$> pm2 set pm2-logrotate:max_size 1K 
#  (compress logs when rotated)
$> pm2 set pm2-logrotate:compress true
#  (force rotate every minute)
$> pm2 set pm2-logrotate:rotateInterval '*/1 * * * *'

설치하고 사용이 참 쉬웠다.

참고 자료

Posted by lahuman

댓글을 달아 주세요

mongodb expire data

로그인 처리를 위해서 특정 시간동안 데이터를 저장할 필요가 생겼다.

기존에 사용경험이 있었던 Redis를 이용할지를 고민하던 중에, 혹시 요즘 많이 쓰고 있는 Mongodb에도 혹시 특정 시간동안만 저장하는 기능이 있을까 해서 찾아보았다.

검색 키워드로는 "mongodb expire data"를 이용했고 바로 첫번째로 검색 되었다.

사용방법은 인덱스를 생성할때 날짜 필드에 expireAfterSeconds 를 추가하면된다.

# 1시간뒤에 삭제되는 데이터 구조 생성
db.log_events.createIndex( { "createdAt": 1 }, { expireAfterSeconds: 3600 } );

# 사용법 
db.log_events.insert( {
   "createdAt": new Date(),
   "logEvent": 2,
   "logMessage": "Success!"
} );

createAt 이라는 필드 명에는 날짜 형식이 들어가고 해당 날짜를 기준으로 expireAfterSeconds 의 초 만큼 지나가면 데이터가 삭제 된다.

nodejs에서 mongodb를 사용할때는 다음과 같이 스키마 구조를 생성하면 된다.

new Schema({ createdAt: { type: Date, expires: 3600 }});

참고 자료

Posted by lahuman

댓글을 달아 주세요

AWS EC2 서버 DISK 크기 늘리기

EC2의 디스크는 탄력적 볼륨이라고 하여 DISK의 크기를 가변적으로 조절 할수 있다.

이번에 EC2 인스턴스의 DISK 용량을 확장하였는데, WEB GUI 기반으로 볼륨 수정 요청을 하고 하루가 지나도 용량의 변경이 없었다.

구글에 검색을 해보니, "파일 시스템을 확장 해야 디스크 용량이 확장되다"고 한다.

먼저 lsbkl 명령어를 이용해서 인스턴스의 볼륨을 확인한다.

[ec2-user ~]$ lsblk
NAME          MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
nvme1n1       259:0    0  30G  0 disk /data
nvme0n1       259:1    0  16G  0 disk
└─nvme0n1p1   259:2    0   8G  0 part /
└─nvme0n1p128 259:3    0   1M  0 part
  • 루트 볼륨 /dev/nvme0n1에는 /dev/nvme0n1p1라는 파티션이 있다. 루트 볼륨에 새 크기인 16GB가 반영되는 동안 파티션의 크기에 원래 크기인 8GB가 반영되기 때문에 파일 시스템을 확장하려면 먼저 파티션 크기를 늘려야 한다.
  • 볼륨 /dev/nvme1n1에는 파티션이 없습니다. 볼륨 크기에 새 크기 30GB가 반영됩니다.

루트 볼륨에서 파티션을 확장하려면 다음 growpart 명령을 사용한다.

[ec2-user ~]$ sudo growpart /dev/nvme0n1 1

lsbkl 명령어를 이용해서 인스턴스의 볼륨의 크기가 반영 되었는지 확인한다.

[ec2-user ~]$ lsblk
NAME          MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
nvme1n1       259:0    0  30G  0 disk /data
nvme0n1       259:1    0  16G  0 disk
└─nvme0n1p1   259:2    0  16G  0 part /
└─nvme0n1p128 259:3    0   1M  0 part

예제: ext2, ext3 또는 ext4 파일 시스템 확장

df -h 명령어로 볼륨에 대한 파일 시스템 크기를 확인한다.

[ec2-user ~]$ df -h
Filesystem       Size  Used Avail Use% Mounted on
/dev/xvda1       8.0G  1.9G  6.2G  24% /
/dev/xvdf1       8.0G   45M  8.0G   1% /data

resize2fs 명령어를 이용해서 각 볼륨에서 파일 시스템을 확장한다.

[ec2-user ~]$ sudo resize2fs /dev/xvda1
[ec2-user ~]$ sudo resize2fs /dev/xvdf1

마지막으로 볼륨에 대한 파일 시스템의 크기를 확인하면 된다.

[ec2-user ~]$ df -h
Filesystem       Size  Used Avail Use% Mounted on
/dev/xvda1        16G  1.9G  6.2G  12% /
/dev/xvdf1        30G   45M  8.0G   1% /data

참고

Posted by lahuman

댓글을 달아 주세요

느낌표 2개의 의미

const x = '';
!!x; //false

!!를 사용하면 빈값 등(i.e., except for false, 0, "", null, undefined, and NaN)은 false를 그 외엔 true를 리턴한다.

참고

Posted by lahuman

댓글을 달아 주세요

package-lock.json은 왜 필요 한가?

npm install을 이용해서 package를 인스톨하면 자동으로 package-lock.json이라는 파일이 생성된다.

용도를 몰라서 그냥 두고 있었는데, 가끔씩 이 파일이 형상관리 과정에서 충돌이 일어나 .gitignore에 추가해버렸었다.

함께 일하시는 분이 package-lock.json은 꼭 필요하다는 이야기를 해주셔서 검색을 해보니 다음과 같은 경우에 사용된다.

  1. npm의 버전의 다른 경우
  2. 의존성을 가진 패키지의 버전이 업데이터 되는 경우
  3. 의존성을 가진 패키지가 의존하는 패키지의 버전이 업데이트되는 경우

node_modules의 폴더의 스냅샷을 저장하여, 다른 곳에서 npm install 명령어를 실행시 package-lock.json에 명시된 의존 패키지들을 통해 node_modules를 만들어 낸다. 다만, package.json의 변경은 package-lock.json 보다 우선 된다.

주요 사항은 package-lock.json은 꼭 형상 관리에 포함 시켜야 한다.

그러하다

참고자료

Posted by lahuman

댓글을 달아 주세요

Broswer의 Cookie 와 localstorage 의 차이점 정리

쿠키는 오랫동안 사용된 클라이언트에 저장하는 정보이다.

로컬 스토리지는 웹 스토리지의 하나로 기본적으로 key와 value 형태이다.

웹 스토리지세션 스토리지로컬 스토리지가 존재한다.
세션 스토리지와 로컬 스토리지의 차이는 세션이 종료되는 경우 스토리지 내용의 삭제 유무이다.

쿠키와 로컬스토리지와의 가장 큰 차이점

  1. 쿠키는 서버에서 읽을수 있음
  2. 로컬 스토리지는 클라이언트에서만 읽고 쓸수 있음
  3. 쿠키는 4Kb 까지만 지원
  4. 로컬 스토리지는 5Mb 까지 지원

참고자료

Posted by lahuman

댓글을 달아 주세요

Loop와 함께 async, await 사용하기

함께 일하는 분이 while 과 함께 async를 사용하면 동작 하지 않는다고 하셔서 확인을 해보았다.

결론은 동작은 잘된다.

다만, array를 이용해서 forEach, map 등을 할경우는 동작 하지 않는다.

그래서 array는 for 문을 이용해서 직렬로 처리 하거나, map으로 모든 오브젝트를 promise로 변환하여 병렬로 처리 하여야 한다.

const sleep = (ms) => {
  return new Promise(resolve => setTimeout(resolve, ms));
}
const wrapSlept = async () => { await sleep(2000) };

function delay(item) { return new Promise(resolve => setTimeout(() => { console.log(item); resolve(); }, 500)); }

const testAwait = async () => {
  //동작
  let i = 0;
  while (i < 5) {
    console.log('i: ', i);
    i++;
    await wrapSlept(3000);
  }
  console.log("Done!");

  //동작
  for (var f = 0; f < 5; f++) {
    console.log('f: ', f);
    await wrapSlept(3000);
  }
  console.log("Done!");

  //동작 안함
  [1, 2, 3].forEach(async (n) => {
    console.log('n: ', n);
    await wrapSlept(3000);
  });
  console.log("Done!");

  //직렬 처리
  for (const item of [1,2,3]) {
    await delay(item);
  }
  console.log("Done!");

  //병렬
  const promises = [1, 2, 3].map(item => delay(item));
  await Promise.all(promises);
  console.log("Done!");

}

testAwait();

기억해두자

참고자료

Posted by lahuman

댓글을 달아 주세요

Async Await을 이용하여 처리시 catch를 처리하지 않아 response 무한 대기 현상

개인적으로 Promise, callback 보다는 async await를 많이 이용한다.

const getSomething = asycn (req, res, next) => {

    const something = await callPromiseObject();

    res.json(something);
}

이와 같이 처리시 기본적으로는 큰 문제가 없지만, callPromiseObject() 를 실행하다 오류가 발생하면 response를 전송하지 못하여 무한 대기 하는 오류가 발생한다.

해결하는 방법은 몇가지가 있는데, 그중 하나는

const getSomething = asycn (req, res, next) => {
    try{
        const something = await callPromiseObject();

        res.json(something);
    }catch(e){
        next(e);
    }
}

이와 같이 try, catch를 이용하는 것이다. 개인적으로 동일한 코드가 계속 반복되어 좋아하는 스타일은 아니다.

또 다른 방법은 Promise를 이용하거나 callback을 이용하는 방식이다.

const asyncHandler = require('express-async-handler')

express.get('/', asyncHandler(async (req, res, next) => {
    const bar = await foo.findAll();
    res.send(bar)
}))

이런 방식으로 처리 하는 것인데, 미들웨어 코드를 보면 간단하다.

const asyncUtil = fn =>
function asyncUtilWrap(...args) {
  const fnReturn = fn(...args)
  const next = args[args.length-1]
  return Promise.resolve(fnReturn).catch(next)
}

module.exports = asyncUtil

개인적으로는 미들웨어를 사용하는 방식으로 좀더 쉬운듯 하여 찾아보니 다음과 같은 모듈도 있었다.

var express = require('express')
var wrap = require('async-middleware').wrap

var app = express()

app.use(wrap(function (req, res) {
  return Promise.reject(new Error('boom!'))
}))

참고자료

Posted by lahuman

댓글을 달아 주세요