상세 컨텐츠

본문 제목

[Node.js]16장 서버리스 노드 개발

21-22/21-22 Node.js

by Kimpeep 2022. 1. 31. 13:00

본문

728x90

서버리스(severless) : 서버를 클라우드 서비스가 대신 관리해주는 것

 

대표적인 서비스

AWS의 람다(Lambda) / API 게이트웨이(API Gateway) / S3

GCP의 앱 엔진(App Engine) / 파이어베이스(Firebase) / 클라우드 펑션스(Cloud Functions) / 클라우드 스토리지(Cloud Storage)

 

FaaS(Function as a Service) : 람다와 클라우드 펑션스는 특정한 동작을 수행하는 로직을 저장하고 요청이 들어올 때 로직을 실행함

 

16.2 AWS S3 사용하기

  1. AWS 홈페이지 - 제품 - 스토리지 - S3 클릭
  2. 버킷 만들기 혹은 시작하기 버튼 클릭
  3. 버킷의 이름과 리전 설정
    1. 이때 버킷의 이름은 각자 고유해야 함
    2. 리전은 가까울수록 이미지 로딩 속도가 빠름
  4. 권한 설정 단계
    1. 모든 퍼블릭 액세스 차단 체크박스 해제
    2. 실무에서는 해제하지 않음

Node.js 교과서 개정2판(조현영 저) 619페이지

5. 버킷 만들기 버튼을 눌러 버킷 생성

6. 웹사이트에서 버킷의 이미지를 불러올 수 있도록 권한을 부여

- 버킷을 클릭하여 권한 > 버킷 정책 메뉴 선택하고 버킷 정책 편집기에 아래의 코드 생성 후 저장

{
	"Version": "2022-01-24",
    "Statement" : [
    {
    	"Sid" : "AddPerm",
        "Effect" : "Allow",
        "Principal" : "*",
        "Action" : [
        	"s3:GetObject",
            "s3:PutObject"
        ],
        "Resource": "arn:aws:s3:::당신의버킷명/*"
    }
  	]
}

7. AWS 액세스 키 발급

  1. 계정 - 내 보안 자격 증명
  2. 새 액세스 키 만들기 버튼 클릭하여 키 생성
  3. 키 파일 다운로드 버튼을 클릭하여 추후에 필요할 때 볼 수 있도록 함

8. 프로젝트에 multer-s3 패키지, aws-sdk 패키지 설치

multer-s3 : multer에서 S3로 업로드할 수 있게 해주는 패키지

aws-sdk : AWS의 기능을 노드에서 사용할 수 있게 해주는 패키지, multer-s3 패키지에서 사용

 

npm i multer-s3 aws-sdk

 

.env파일에 발급 받은 키 파일 안에 적힌 액세스 키 ID보안 액세스 키를 복사

...
S3_ACCESS_KEY_ID = 액세스키ID
S3_SECRET_ACCESS_KEY = 액세스키

 

nodebird/routes/post.js

const express = require('express');
const multer = require('multer');
const path = require('path');
const fs = require('fs');
const AWS = require('aws-sdk'); //추가
const multerS3 = require('multer-s3'); //추가

const { Post, Hashtag } = require('../models');
const { isLoggedIn } = require('./middlewares');

const router = express.Router();

try {
  fs.readdirSync('uploads');
} catch (error) {
  console.error('uploads 폴더가 없어 uploads 폴더를 생성합니다.');
  fs.mkdirSync('uploads');
}

// AWS/config.update 메서드로 AWS에 관한 설정을 함
// accessKeyID, secretAccessKey, region 입력
AWS.config.update({
  accessKeyId: process.env.S3_ACCESS_KEY_ID,
  secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,
  region: 'ap-northeast-2',
});

//multer의 storage 옵션을 multerS3로 교체
// multerS3 -> s3 객체, 버킷명, 파일명(key) 입력
// key 옵션으로 저장할 파일명을 설정하고 버킷 내부에서 original 폴더 아래에 생성
const upload = multer({
  storage: multerS3({
    s3: new AWS.S3(),
    bucket: 'nodebird',
    key(req, file, cb) {
      cb(null, `original/${Date.now()}${path.basename(file.originalname)}`);
    },
  }),
  limits: { fileSize: 5 * 1024 * 1024 },
});

// req.file.location에 S3 버킷 이미지 주소가 담겨 있음
// 해당 주소를 클라이언트로 보냄
router.post('/img', isLoggedIn, upload.single('img'), (req, res) => {
  console.log(req.file);
  res.json({ url: req.file.location });
});
...

 

 16.3 AWS 람다 사용하기

S3에 올린 이미지를 리사이징한 후 줄어든 이미지를 다시 S3에 저장하는 작업을 수행할 것입니다.

Node.js 교과서 개정 2판(조현영 저) 627페이지

1. nodebird 폴더 외부에 aws-upload 폴더를 만들고 package.json을 작성

{
  "name": "aws-upload",
  "version": "1.0.0",
  "description": "Lambda 이미지 리사이징",
  "main": "index.js",
  "author": "ZeroCho",
  "license": "ISC",
  "dependencies": {
    "aws-sdk": "^2.634.0",
    "sharp": "^0.25.1"
  }
}

2. aws-upload/.gitignore

node_modules

3. aws-upload/index.js

const AWS = require('aws-sdk');
const sharp = require('sharp');

const s3 = new AWS.S3();

//handler 함수가 람다 호출 시 실행되는 함수
// event : 호출 상황에 대한 정보가 담겨 있음
// context : 실행되는 함수 환경에 대한 정보가 담겨 있음
// callback : 함수가 완료되었는지 람다에게 알림
// callback의 첫번째 인수 error 여부 / 두번째 인수 반환값
exports.handler = async (event, context, callback) => {
	// event 객체로부터 버킷 이름(Bucket)과 파일 경로(Key)를 받아옴
    // 이를 통해 파일명과 확장자도 얻음
  const Bucket = event.Records[0].s3.bucket.name;
  const Key = event.Records[0].s3.object.key;
  const filename = Key.split('/')[Key.split('/').length - 1];
  const ext = Key.split('.')[Key.split('.').length - 1];
  const requiredFormat = ext === 'jpg' ? 'jpeg' : ext; // sharp에서는 jpg 대신 jpeg 사용합니다.
  console.log('name', filename, 'ext', ext);

	// s3.getObject 메서드로 버킷으로부터 파일을 불러옴
    // s3Object.Body에 파일 버퍼가 담겨 있음
  try {
    const s3Object = await s3.getObject({ Bucket, Key }).promise(); // 버퍼로 가져오기
    console.log('original', s3Object.Body.length);
    
    // sharp 함수를 파일 버퍼에 넣고 resize 메서드로 크기 지정
    // resize(가로, 세로, 조정 기준)
    // fit:inside -> 가로 세로 사이즈 안에 딱 맞게 이미지 조정
    // toBuffer 메서드는 리사이징된 이미지 결과를 버퍼로 출력
    const resizedImage = await sharp(s3Object.Body) // 리사이징
      .resize(200, 200, { fit: 'inside' })
      .toFormat(requiredFormat)
      .toBuffer();
    
    // s3.putObject 메서드로 리사이징된 이미지를 thumb 폴더에 저장
    await s3.putObject({ 
      Bucket,
      Key: `thumb/${filename}`,
      Body: resizedImage,
    }).promise();
    console.log('put', resizedImage.length);
    return callback(null, `thumb/${filename}`);
  } catch (error) {
    console.error(error);
    return callback(error);
  }
};

4. 람다에 배포

Lightsail에서 빌드한 후 S3로 배포하고, 람다는 S3에서 배포된 파일을 가져와 함수로 만듦

 

  1. 깃허브에 aws-upload 레포지터리 생성 후 push

  2. Lightsail 인스턴스 SSH에 접속하여 깃허브 레포지터리 clone 받음

  3. npm i 후 aws-upload 폴더 아래의 모든 파일을 압축하여 aws-upload.zip 파일 만듦

  4. Lightsail에서 S3로 파일 업로드 (aws-cli 설치하고 aws configure 명령을 입력 후 발급받았던 키 파일 정보 입력)

  5. aws-cli를 사용해 aws-upload.zip 업로드

 

5. 람다 서비스 설정

  1. AWS 홈페이지 - 제품 - 주요 서비스 - AWS Lambda 클릭

  2. 함수 생성 버튼 클릭

    1) 함수 이름 : node-deploy

    2) Runtime : Node.js 12.x

  3. Amazon S3에서 파일 업로드

    1) Amazon S3 링크 URL : https://버킷명.s3.지역명.amazonaws.com/파일명 

  4. 기본 설정 - 편집 버튼 클릭

    1) 핸들러 이름 : '파일명.함수명' (책에서는 index.handler)

    2) 메모리, 제한 시간이 부족할 경우 늘림

    3) 실행 역할 : AWS 정책 템플릿에서 새 역할 생성을 선택

    4) 역할 이름은 자유, 정책 템플릿에서 Amazon S3 객체 읽기 전용 권한 추가

  5. 트리거 추가 : S3에 이미지를 업로드 할떄마다 람다 함수가 동작하도록 함

    1) 트리거 구성 : S3 선택

    2) 버킷 : 직접 만들었던 버킷 선택 (책에서는 nodebird)

    3) 이벤트 유형 : 모든 객체 생성 이벤트 로 설정

    4) 접두사 : original 폴더 안에 파일이 업로드되었을 때 함수를 트리거할 것이므로 original/ 로 설정

 

  6. nodebird/routes/post.js

    기존 주소에서 original 폴더를 thumb 폴더로 교체

...
router.post('/img', isLoggedIn, upload.single('img'), (req, res) => {
  console.log(req.file);
  const originalUrl = req.file.location;
  const url = originalUrl.replace(/\/original\//, '/thumb/');
  res.json({ url, originalUrl });
});
...

  7. nodebird/views/main.html

    img 태그에 onerror 속성을 붙여 리사이징된 이미지를 로딩하는 데 실패하면 원본 이미지를 사용하도록 함

...
{% if twit.img %}
		// 아래 div 태그 추가
              <div class="twit-img">
                <img
                  src="{{twit.img}}"
                  onerror="this.src = this.src.replace(/\/thumb\//, '/original/');"
                  alt="섬네일"
                />
              </div>
              
...
{% block script %}
  <script>
    if (document.getElementById('img')) {
      document.getElementById('img').addEventListener('change', function(e) {
        const formData = new FormData();
        formData.append('img', this.files[0]);
        axios.post('/post/img', formData)
          .then((res) => {
            document.getElementById('img-url').value = res.data.url;
            document.getElementById('img-preview').src = res.data.originalUrl; // 추가
            document.getElementById('img-preview').style.display = 'inline';
          })

 

이미지 미리보기(img-preview) 시에는 원본 이미지를 보여주고, 이미지를 저장한 후에는 리사이징된 이미지를 보여주도록 함 -> 이미지 업로드와 리사이징 간의 시간차 때문에 이미지가 보이지 않는 현상을 해결하기 위함

 

16.4 구글 클라우드 스토리지 사용하기

- 구글 클라우드 스토리지에 이미지를 업로드하기

 

1. 구글 클라우드 플랫폼 - Storage 선택 - 버킷 생성 버튼

Node.js 교과서 개정2판(조현영 저) 640페이지

- 버킷 이름 지정 : nodebird

- 데이터 저장 위치 선택 : 위치 유형 Region / 위치는 Seoul

- 버킷 권한 수정

 

2. 클라우드 스토리지 접근 키 발급받기

- 사용자 인증 정보 화면 - 사용자 인증 정보 만들기 버튼 클릭

- 서비스 계정 이름 : nodebird

- 서비스 계정 : nodebird

- 역할 : 저장소 관리자

- 키 만들기 버튼 클릭 -> 다운로드 받은 JSON 파일을 NodeBird 프로젝트 폴더로 복사

 

3. multer-google-storage 패키지 설치

npm i multer-google-storage

nodebird/routes/post.js

const express = require('express');
const multer = require('multer');
const fs = require('fs');
const multerGoogleStorage = require('multer-google-storage');

const upload = multer({
  storage: multerGoogleStorage.storageEngine({
    bucket: 'nodebird',
    projectId: 'node-deploy-270114',
    keyFilename: 'node-deploy-270114-b024dbed754a.json',
  }),
  limits: { fileSize: 5 * 1024 * 1024 },
});

storage 속성 안에 multerGoogleStorage를 사용하여 multer-google-storage 사용

bucket, projectId, keyFilename : 버킷명, 프로젝트 ID, 키 파일명

 

16.5 구글 클라우드 펑션스 사용하기

Node.js 교과서 개정2판(조현영 저)

gcp-upload/package.json

nodebird 폴더 외부에 gcp-upload 폴더 생성 후 package.json 파일 작성

{
  "name": "gcp-upload",
  "version": "1.0.0",
  "description": "Cloud Functions 이미지 리사이징",
  "main": "index.js",
  "author": "ZeroCho",
  "license": "ISC",
  "dependencies": {
    "@google-cloud/storage": "^1.6.0",
    "gm": "^1.23.1",
    "sharp": "^0.25.1"
  }
}

gcp-upload/.gitignore

node_modules

gcp-upload/index.js

 

1. git-upload 깃 레포지터리 생성하고 지금까지 작성한 코드를 push

2. 컴퓨터 엔진 인스턴스 SSH에 접근하여 깃허브 레포지터리를 clone 받음

3. clone 받은 후 npm i 를 하고 gcp-upload 폴더 아래의 모든 파일을 압축하여 gcp-upload.zip 파일 생성

4. 컴퓨트 엔진에서 클라우드 스토리지로 파일 업로드

 

 

GCP - Cloud Functions - 함수 만들기 버튼 클릭

- 함수 이름 : gcp-upload

- 트리거 : Cloud Storage

- 이벤트 유형 : 완료/생성

- 버킷 : 본인이 만든 버킷 선택

- 소스코드 : Cloud Storage의 ZIP을 클릭해 방금 업로드한 파일 업로드

- 실행할 함수 : resizeAndUpload (*index.js의 exports.resizeAndUpload 이 부분과 동일해야 함)

 

NodeBird 코드 수정하기(16.3과 동일)

nodebird/routes/post.js

...
router.post('/img', isLoggedIn, upload.single('img'), (req, res) => {
  console.log(req.file);
  const filePath = req.file.path.split('/').splice(0, 3).join('/');
  const originalUrl = `${filePath}/${req.file.filename}`;
  const url = originalUrl.replace(/\/original\//, '/thumb/');
  res.json({ url, originalUrl });
});

...

nodebird/views/main.html

...
            {% if twit.img %}
              <div class="twit-img">
                <img
                  src="{{twit.img}}"
                  onerror="this.src = this.src.replace(/\/thumb\//, '/original/');"
                  alt="섬네일"
                />
              </div>
            {% endif %}
...

...
			document.getElementById('img-preview').src = res.data.originalUrl;
...

 

728x90

관련글 더보기