์ƒ์„ธ ์ปจํ…์ธ 

๋ณธ๋ฌธ ์ œ๋ชฉ

[Node.js] 16์žฅ ์„œ๋ฒ„๋ฆฌ์Šค ๋…ธ๋“œ ๊ฐœ๋ฐœ

22-23/22-23 Node.js

by dev otcroz 2023. 1. 12. 10:00

๋ณธ๋ฌธ

728x90

๐Ÿ” ์„œ๋ฒ„๋ฆฌ์Šค?

์„œ๋ฒ„๋ฆฌ์Šค, ์˜์–ด๋กœ๋Š” Server(์„œ๋ฒ„) + less(์—†๋Š”)์ด๋‹ค. ์„œ๋ฒ„๋ฅผ ํด๋ผ์šฐ๋“œ๊ฐ€ ๋Œ€์‹  ๊ด€๋ฆฌํ•ด ์ฃผ๋Š” ๊ฒƒ.

์„œ๋ฒ„๋ฅผ ํด๋ผ์šฐ๋“œ ์„œ๋น„์Šค๊ฐ€ ๋Œ€์‹  ๊ด€๋ฆฌํ•ด ์ฃผ๊ธฐ ๋•Œ๋ฌธ์—, ๊ฐœ๋ฐœ์ž ๋˜๋Š” ์šด์˜์ž๊ฐ€ ์„œ๋ฒ„๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐ์— ๋Œ€ํ•œ ๋ถ€๋‹ด์ด ์ค€๋‹ค.

 

๋Œ€ํ‘œ์ ์œผ๋กœ๋Š” AWS์™€ GCP์—์„œ ๋‹ค์–‘ํ•œ ์„œ๋ฒ„๋ฆฌ์Šค ์„œ๋น„์Šค๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

๐Ÿ“Œ AWS ์„œ๋น„์Šค ์•Œ์•„๋ณด๊ธฐ

AWS์˜ ๋žŒ๋‹ค(Lambda), API ๊ฒŒ์ดํŠธ์›จ์ด (API Gateway), S3์ด ์žˆ๋‹ค. 

๐Ÿ“Œ Google Cloud ์„œ๋น„์Šค ์•Œ์•„๋ณด๊ธฐ

Google Cloud์˜ ์•ฑ ์—”์ง„ (App Engine), ํŒŒ์ด์–ด๋ฒ ์ด์Šค (Firebase), ํด๋ผ์šฐ๋“œ ํŽ‘์…˜์Šค (Cloud Functions), ํด๋ผ์šฐ๋“œ ์Šคํ† ๋ฆฌ์ง€ (Cloud Storage) ๋“ฑ์ด ์žˆ๋‹ค.

 

๐Ÿ“Œ ๊ธฐ๋Šฅ์— ๋”ฐ๋ฅธ ๊ตฌ๋ถ„

โœ… FaaS

๋žŒ๋‹ค์™€ ํด๋ผ์šฐ๋“œ ํŽ‘์…˜์Šค๋Š” ํŠน์ •ํ•œ ๋™์ž‘์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๋กœ์ง์„ ์ €์žฅํ•˜๊ณ , ์š”์ฒญ์ด ๋“ค์–ด์˜ฌ ๋•Œ ๋กœ์ง์„ ์‹คํ–‰ํ•˜๋Š” ์„œ๋น„์Šค์ด๋‹ค. ํ•จ์ˆ˜์ฒ˜๋Ÿผ ํ˜ธ์ถœํ•  ๋•Œ ์‹คํ–‰๋˜๊ธฐ์— FaaS, Fuction as a Service๋กœ ๋ถˆ๋ฆฌ๊ธฐ๋„ ํ•œ๋‹ค.

์‚ฌ์šฉํ•œ ๋งŒํผ๋งŒ ์š”๊ธˆ์„ ์ง€๋ถˆํ•˜๋ฉด ๋œ๋‹ค.

 

โœ…ํด๋ผ์šฐ๋“œ ๋ฐ์ดํ„ฐ ์ €์žฅ์†Œ

S3์™€ ํด๋ผ์šฐ๋“œ ์Šคํ† ๋ฆฌ์ง€๋Š” ํด๋ผ์šฐ๋“œ ๋ฐ์ดํ„ฐ ์ €์žฅ์†Œ๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค. ์ •์  ํŒŒ์ผ์„ ์ œ๊ณตํ•˜๋„๋ก ๋…ธ๋“œ ์„œ๋ฒ„์—์„œ ์œ„์ž„๋ฐ›๊ธฐ๋„ ํ•œ๋‹ค.

 

๐Ÿ” AWS ์‚ฌ์šฉํ•˜๊ธฐ

  1. AWS ํ™ˆํŽ˜์ด์ง€ - ์ œํ’ˆ - ์Šคํ† ๋ฆฌ์ง€ - S3 ํด๋ฆญ
  2. ๋ฒ„ํ‚ท ๋งŒ๋“ค๊ธฐ ํ˜น์€ ์‹œ์ž‘ํ•˜๊ธฐ ๋ฒ„ํŠผ ํด๋ฆญ
  3. ๋ฒ„ํ‚ท์˜ ์ด๋ฆ„๊ณผ ๋ฆฌ์ „ ์„ค์ •
    1. ์ด๋•Œ ๋ฒ„ํ‚ท์˜ ์ด๋ฆ„์€ ๊ฐ์ž ๊ณ ์œ ํ•ด์•ผ ํ•จ
    2. ๋ฆฌ์ „์€ ๊ฐ€๊นŒ์šธ์ˆ˜๋ก ์ด๋ฏธ์ง€ ๋กœ๋”ฉ ์†๋„๊ฐ€ ๋น ๋ฆ„
  4. ๊ถŒํ•œ ์„ค์ • ๋‹จ๊ณ„
    1. ๋ชจ๋“  ํผ๋ธ”๋ฆญ ์•ก์„ธ์Šค ์ฐจ๋‹จ ์ฒดํฌ๋ฐ•์Šค ํ•ด์ œ
    2. ์‹ค๋ฌด์—์„œ๋Š” ํ•ด์ œํ•˜์ง€ ์•Š์Œ
  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 ํŒจํ‚ค์ง€์—์„œ ์‚ฌ์šฉ

9. .envํŒŒ์ผ์— ๋ฐœ๊ธ‰ ๋ฐ›์€ ํ‚ค ํŒŒ์ผ ์•ˆ์— ์ ํžŒ ์•ก์„ธ์Šค ํ‚ค ID์™€ ๋ณด์•ˆ ์•ก์„ธ์Šค ํ‚ค๋ฅผ ๋ณต์‚ฌ

๐Ÿ“Œ ์ด๋Ÿฐ์ €๋Ÿฐ ํŒ ๋ชจ์Œ

ํŒจํ‚ค์ง€ ์„ค์น˜๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ช…๋ น์–ด๋กœ ์ง„ํ–‰ํ•œ๋‹ค.

npm i multer-s3 aws-sdk

.env ํŒŒ์ผ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ตฌ์„ฑํ•œ๋‹ค.

...
S3_ACCESS_KEY_ID = ์•ก์„ธ์Šคํ‚คID
S3_SECRET_ACCESS_KEY = ์•ก์„ธ์Šคํ‚ค

๐Ÿ” AWS ๋žŒ๋‹ค ์‚ฌ์šฉํ•˜๊ธฐ

S3์— ์—…๋กœ๋“œํ•œ ์ด๋ฏธ์ง€๋ฅผ ๋ฆฌ์‚ฌ์ด์ง•ํ•œ ํ›„, ์ค„์–ด๋“  ์ด๋ฏธ์ง€๋ฅผ ๋‹ค์‹œ S3์— ์ €์žฅํ•˜๋Š” ์ž‘์—…์ด๋‹ค.

์ž‘์—…์˜ ์ง„ํ–‰ ๊ณผ์ •์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

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์—์„œ ๋ฐฐํฌ๋œ ํŒŒ์ผ์„ ๊ฐ€์ ธ์™€ ํ•จ์ˆ˜๋กœ ๋งŒ๋“ ๋‹ค.

๐Ÿ“Œ Lightsail์—์„œ ๋ฐฐํฌํ•˜๋Š” ์ด์œ 

sharp๊ฐ€ ์œˆ๋„์šฐ์šฉ, ๋งฅ์šฉ, ๋ฆฌ๋ˆ…์Šค์šฉ์œผ๋กœ ๊ตฌ๋ถ„๋œ๋‹ค. ํ˜„์žฌ๋Š” ์œˆ๋„์šฐ ํ™˜๊ฒฝ์—์„œ ์‹ค์Šต ์ค‘์ด๋ฏ€๋กœ ๋นŒ๋“œ ์‹œ sharp๊ฐ€ ์œˆ๋„์šฐ์šฉ์œผ๋กœ ์„ค์น˜๊ฐ€ ๋˜๋Š”๋ฐ, ๋žŒ๋‹ค๋Š” ๋ฆฌ๋ˆ…์Šค์ด๋ฏ€๋กœ ํ˜ธํ™˜์ด ๋˜์ง€ ์•Š๊ธฐ์— Lightsail์—์„œ ๋ฐฐํฌ๋ฅผ ์ง„ํ–‰ํ•œ๋‹ค.

 

  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. ๊ธฐ๋ณธ ์„ค์ • - ํŽธ์ง‘ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•œ๋‹ค.

  • ํ•ธ๋“ค๋Ÿฌ ์ด๋ฆ„ : 'ํŒŒ์ผ๋ช….ํ•จ์ˆ˜๋ช…' (์ฑ…์—์„œ๋Š” index.handler)
  • ๋ฉ”๋ชจ๋ฆฌ, ์ œํ•œ ์‹œ๊ฐ„์ด ๋ถ€์กฑํ•  ๊ฒฝ์šฐ ๋Š˜๋ ค์ค€๋‹ค.
  • ์‹คํ–‰ ์—ญํ•  : AWS ์ •์ฑ… ํ…œํ”Œ๋ฆฟ์—์„œ ์ƒˆ ์—ญํ•  ์ƒ์„ฑ์„ ์„ ํƒํ•œ๋‹ค.
  • ์—ญํ•  ์ด๋ฆ„์€ ์ž์œ , ์ •์ฑ… ํ…œํ”Œ๋ฆฟ์—์„œ Amazon S3 ๊ฐ์ฒด ์ฝ๊ธฐ ์ „์šฉ ๊ถŒํ•œ์„ ์ถ”๊ฐ€ํ•œ๋‹ค.

  5. ํŠธ๋ฆฌ๊ฑฐ ์ถ”๊ฐ€ : S3์— ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œ ํ• ๋–„๋งˆ๋‹ค ๋žŒ๋‹ค ํ•จ์ˆ˜๊ฐ€ ๋™์ž‘ํ•˜๋„๋ก ํ•œ๋‹ค.

  • ํŠธ๋ฆฌ๊ฑฐ ๊ตฌ์„ฑ : S3 ์„ ํƒ
  • ๋ฒ„ํ‚ท : ์ง์ ‘ ๋งŒ๋“ค์—ˆ๋˜ ๋ฒ„ํ‚ท ์„ ํƒ (์ฑ…์—์„œ๋Š” nodebird)
  • ์ด๋ฒคํŠธ ์œ ํ˜• : ๋ชจ๋“  ๊ฐ์ฒด ์ƒ์„ฑ ์ด๋ฒคํŠธ ๋กœ ์„ค์ •
  • ์ ‘๋‘์‚ฌ : 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) ์‹œ์—๋Š” ์›๋ณธ ์ด๋ฏธ์ง€๋ฅผ ๋ณด์—ฌ์ฃผ๊ณ , ์ด๋ฏธ์ง€๋ฅผ ์ €์žฅํ•œ ํ›„์—๋Š” ๋ฆฌ์‚ฌ์ด์ง•๋œ ์ด๋ฏธ์ง€๋ฅผ ๋ณด์—ฌ์ฃผ๋„๋ก ํ•œ๋‹ค.

์ด๋ฏธ์ง€ ์—…๋กœ๋“œ์™€ ๋ฆฌ์‚ฌ์ด์ง• ๊ฐ„์˜ ์‹œ๊ฐ„์ฐจ๋•Œ๋ฌธ์— ์ด๋ฏธ์ง€๊ฐ€ ๋ณด์ด์ง€ ์•Š๋Š” ํ˜„์ƒ์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•œ๋‹ค.

๐Ÿ” ๊ตฌ๊ธ€ ํด๋ผ์šฐ๋“œ ์Šคํ† ๋ฆฌ์ง€ ์‚ฌ์šฉํ•˜๊ธฐ

๊ตฌ๊ธ€ ํด๋ผ์šฐ๋“œ ์Šคํ† ๋ฆฌ์ง€์— ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.

1. ๊ตฌ๊ธ€ ํด๋ผ์šฐ๋“œ ํ”Œ๋žซํผ - Storage ์„ ํƒ - ๋ฒ„ํ‚ท ์ƒ์„ฑ ๋ฒ„ํŠผ

  • ๋ฒ„ํ‚ท ์ด๋ฆ„ ์ง€์ • : 0000
  • ๋ฐ์ดํ„ฐ ์ €์žฅ ์œ„์น˜ ์„ ํƒ : ์œ„์น˜ ์œ ํ˜• Region / ์œ„์น˜๋Š” Seoul
  • ๋ฒ„ํ‚ท ๊ถŒํ•œ ์ˆ˜์ •

2. ํด๋ผ์šฐ๋“œ ์Šคํ† ๋ฆฌ์ง€ ์ ‘๊ทผ ํ‚ค ๋ฐœ๊ธ‰๋ฐ›๊ธฐ

  • ์‚ฌ์šฉ์ž ์ธ์ฆ ์ •๋ณด ํ™”๋ฉด - ์‚ฌ์šฉ์ž ์ธ์ฆ ์ •๋ณด ๋งŒ๋“ค๊ธฐ ๋ฒ„ํŠผ ํด๋ฆญ
  • ์„œ๋น„์Šค ๊ณ„์ • ์ด๋ฆ„ : nodebird
  • ์„œ๋น„์Šค ๊ณ„์ • : nodebird
  • ์—ญํ•  : ์ €์žฅ์†Œ ๊ด€๋ฆฌ์ž
  • ํ‚ค ๋งŒ๋“ค๊ธฐ ๋ฒ„ํŠผ ํด๋ฆญ -> ๋‹ค์šด๋กœ๋“œ ๋ฐ›์€ JSON ํŒŒ์ผ์„ NodeBird ํ”„๋กœ์ ํŠธ ํด๋”๋กœ ๋ณต์‚ฌ

3. multer-google-storage ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•œ๋‹ค.

4. 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, ํ‚ค ํŒŒ์ผ๋ช…

๐Ÿ“Œ ํŒจํ‚ค์ง€ ์„ค์น˜ ๋ฐฉ๋ฒ•

npm i multer-google-storage

๐Ÿ” ๊ตฌ๊ธ€ ํด๋ผ์šฐ๋“œ ํŽ‘์…˜์Šค ์‚ฌ์šฉํ•˜๊ธฐ

๊ตฌ๊ธ€ ํด๋ผ์šฐ๋“œ ํŽ‘์…˜์Šค๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋กœ์ง์œผ๋กœ ๋™์ž‘ํ•œ๋‹ค. ์•ž์„œ ๋ณด์•˜๋˜ AWS ๋žŒ๋‹ค์™€ ์œ ์‚ฌํ•œ ๊ตฌ์กฐ๋ฅผ ๋„๊ณ  ์žˆ๋‹ค.

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

const storage = require('@google-cloud/storage')();
const sharp = require('sharp');

exports.resizeAndUpload = (data,context) => {
	const { bucket, name } = data;
    const ext = name.split('.')[Key.split('.').length -1];
    const requiredFormat = ext === 'jpg' ? 'jpeg' : ext;
    console.log('name', name, 'ext', ext);
    const file = storage.bucket(bucket).file(name);
    const readStream = file.createReadStream();
    
    const newFile = storage.bucket(bucket).file(`thumb/${name}`);
    const writeStream = newFile.createWriteStream();
    
    sharp(readStream)
    	.resize(200, 200, { fit : 'inside' })
        .toFormat(requiredFormat)
        .pipe(writeStream);
        
    return new Promise((resolve, reject) => {
    	writeStream.on('finish', () => {
        	resolve(`thumb/${name}`);
        });
        writeStream.on('error', reject);
    });
};
  1. git-upload ๊นƒ ๋ ˆํฌ์ง€ํ„ฐ๋ฆฌ ์ƒ์„ฑํ•˜๊ณ  ์ง€๊ธˆ๊นŒ์ง€ ์ž‘์„ฑํ•œ ์ฝ”๋“œ๋ฅผ push
  2. ์ปดํ“จํ„ฐ ์—”์ง„ ์ธ์Šคํ„ด์Šค SSH์— ์ ‘๊ทผํ•˜์—ฌ ๊นƒํ—ˆ๋ธŒ ๋ ˆํฌ์ง€ํ„ฐ๋ฆฌ๋ฅผ clone ๋ฐ›์Œ
  3. clone ๋ฐ›์€ ํ›„ npm i ๋ฅผ ํ•˜๊ณ  gcp-upload ํด๋” ์•„๋ž˜์˜ ๋ชจ๋“  ํŒŒ์ผ์„ ์••์ถ•ํ•˜์—ฌ gcp-upload.zip ํŒŒ์ผ ์ƒ์„ฑ
  4. ์ปดํ“จํŠธ ์—”์ง„์—์„œ ํด๋ผ์šฐ๋“œ ์Šคํ† ๋ฆฌ์ง€๋กœ ํŒŒ์ผ ์—…๋กœ๋“œ

 

1. GCP - Cloud Functions - ํ•จ์ˆ˜ ๋งŒ๋“ค๊ธฐ ๋ฒ„ํŠผ ํด๋ฆญ

- ํ•จ์ˆ˜ ์ด๋ฆ„ : gcp-upload

- ํŠธ๋ฆฌ๊ฑฐ : Cloud Storage

- ์ด๋ฒคํŠธ ์œ ํ˜• : ์™„๋ฃŒ/์ƒ์„ฑ

- ๋ฒ„ํ‚ท : ๋ณธ์ธ์ด ๋งŒ๋“  ๋ฒ„ํ‚ท ์„ ํƒ

- ์†Œ์Šค์ฝ”๋“œ : Cloud Storage์˜ ZIP์„ ํด๋ฆญํ•ด ๋ฐฉ๊ธˆ ์—…๋กœ๋“œํ•œ ํŒŒ์ผ ์—…๋กœ๋“œ

- ์‹คํ–‰ํ•  ํ•จ์ˆ˜ : resizeAndUpload (*index.js์˜ exports.resizeAndUpload ์ด ๋ถ€๋ถ„๊ณผ ๋™์ผํ•ด์•ผ ํ•จ)

 

2. 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;
...

 

 


Node.js

Editor : PeeP

728x90

๊ด€๋ จ๊ธ€ ๋”๋ณด๊ธฐ