5 분 소요

오늘 한 일(회고)

  • 기존에 사용하던 AWS RDS를 localhost로 변경하기
  • AWS S3(Amazone Web Services) 클라우드 스토리지 사용해보기
  • 클라우드 스토리지 이용해서 이미지 저장/삭제 구현해보기

기존에 사용하던 AWS RDS를 localhost로 변경하기

네이버 클라우드로 사용하기전에 이전에 나는 AWS에서 제공하는 RDS를 사용해서 sequelize를 이용해 mysql를 사용하고 있었다. 그런데 다른 클라우드 스토리지를 사용하려면 계정을 새로 만들어야하는데 귀찮기도 해서 이미 가지고 있는 계정(AWS)을 쓰려고한다.그래서 DB를 로컬호스트로 변경하려고 한다.
변경하는 것은 무척 간단했다.

localhost로 변경하기
require('dotenv').config();
const env = process.env;


  // const development = {
  //   "username": env.DATABASE_USERNAME,
  //   "password": env.DATABASE_PASSWORD,
  //   "database": env.DATABASE_DATABASE,
  //   "host": env.DATABASE_HOST,
  //   "dialect": "mysql"
  // };

  const development = {
    "username": env.DATABASE_USERNAME,
    "password": env.DATABASE_PASSWORD,
    "database": env.DATABASE_DATABASE,
    "host": "localhost",
    "port": "3306",
    "dialect": "mysql",
    "define": {
      "charset": "utf8mb4",
      "collate": "utf8mb4_general_ci",
      "timestamps": true
    }
  };

  
  const test = {
    "username": "root",
    "password": null,
    "database": "database_test",
    "host": "127.0.0.1",
    "dialect": "mysql"
  };

  const production = {
    "username": "root",
    "password": null,
    "database": "database_production",
    "host": "127.0.0.1",
    "dialect": "mysql"
  };

  module.exports = {development, test, production };

기존에 쓰던 것은 주석처리 해주고 development를 새롭게 짜주었다.그리고 .env파일을 사용하고 있어서 이것만 변경해주면 끝…금방 연결이 되었다.

AWS S3(Amazone Web Services) 클라우드 스토리지 사용해보기

일단 기존에 내가 사용하던 RDS 콘솔에 접속해서 삭제먼저 해주었다..(혹시 과금이 될지도 모르니)

amz s3 설정 방법

AWS
일단 aws s3 접속하면 버킷 생성을 하자.
AWS
버킷 이름을 생성하자.
AWS
차단을 풀어주자.우리는 외부접속이 가능하게 해야한다.

나머진 나는 전부 비활성화를 했다. 잘 모르는 것도 있고, 나중에 수정가능한 것으로 알고 있어서 일단 전부 비활성화를 선택 후 버킷 만들기 클릭했다.

AWS
버킷을 클릭하자.
AWS
폴더 만들기 클릭후 적당한 이름 작성 후 폴더를 생성하자.
AWS
나는 test폴더를 생성했다. 한 번 업로드를 해보자.
AWS
우리의 bird를 한번 올려보았다.
AWS
성공적으로 올라갔고 상세정보도 확인가능하다.

이제 내부설정은 끝났으니 외부에서 업로드를 해보자~
먼저 인증자격을 증명 받아야 한다.

인증자격 증명받기

AWS

인증자격 증명 받기

  • 서비스 -> IAM으로 이동하기
  • 사용자 -> 사용자추가 -> 사용자 이름 -> 권한 설정(그룹에 대한 사용자 추가), 그룹생성하기


AWS
사용자 이름 적기

AWS
사용자 그룹 생성하기 이 때 권한 정책은 AmazonS3FullAccess 으로 설정하고 그룹 생성하기!!
AWS
사용자 생성하기
AWS
액세스 관리-> 사용자가서 보안자격증명에서 확인 및 생성할 수 있다. 잘 적어두자!!
AWS
밑에 쭉 내려가면 액세스 키도 생성할 수 있다.
AWS
일단 나는 이 비밀키들을 텍스트 파일로 잘 저장했다. 그리고 csv 파일도 다운 받아났다.

이제 준비는 다 끝났으니 본격적으로 연결해보자. 테스트용으로 내가 예전에 사용했던 프로젝트를 그대로 사용했다.

참고자료

aws공식문서

npm install aws-sdk 
npm install uuid 

Nodejs 프로젝트에 이 두 가지를 설치했다.


공식자료에 따르면


Linux, Unix 및 macOS의 공유 자격 증명 파일: ~/.aws/credentials
Windows의 공유 자격 증명 파일: C:\Users\USER_NAME.aws\credentials


이렇게 자격 증명 파일이 이 경로에 있다고 한다. 하지만 내 컴퓨터에는 안보여서 그냥 내가 직접 폴더를 만들었다. 그리고 credentials 파일도 직접 만들었다. 주의할 점은 확장자가 있으면 안된다.나도 처음에 credentials.text 로 만든 후

[default]
aws_access_key_id = <YOUR_ACCESS_KEY_ID>
aws_secret_access_key = <YOUR_SECRET_ACCESS_KEY>


위 내용을 그대로 넣어주었다. 그리고 아까 미리 복사한 액세스 키, 비밀 액세스 키를 넣어주면 된다.
그리고 공식문서에 sample 코드가 있다. 그것을 nodejs프로젝트에 임시 sample.js로 만든 후 실행시켜보자~

// Load the SDK and UUID
var AWS = require('aws-sdk');
var uuid = require('uuid');

// Create unique bucket name
var bucketName = 'node-sdk-sample-' + uuid.v4();
// Create name for uploaded object key
var keyName = 'hello_world.txt';

// Create a promise on S3 service object
var bucketPromise = new AWS.S3({apiVersion: '2006-03-01'}).createBucket({Bucket: bucketName}).promise();

// Handle promise fulfilled/rejected states
bucketPromise.then(
  function(data) {
    // Create params for putObject call
    var objectParams = {Bucket: bucketName, Key: keyName, Body: 'Hello World!'};
    // Create object upload promise
    var uploadPromise = new AWS.S3({apiVersion: '2006-03-01'}).putObject(objectParams).promise();
    uploadPromise.then(
      function(data) {
        console.log("Successfully uploaded data to " + bucketName + "/" + keyName);
      });
}).catch(
  function(err) {
    console.error(err, err.stack);
});

공식 문서에 나온 샘플 코드이다.


AWS
일단 터미널에서는 성공했다고 나왔다.
AWS
이렇게 아까 내가 만든 버킷 뿐만 아니라 새로운 샘플 버킷이 생성되었다.
해당 버킷을 들어가서 확인하면 hello_world.txt 파일이 업로드 된 것을 확인 할 수 있었다.

클라우드 스토리지 이용해서 이미지 저장/삭제 구현해보기

이제 본격적으로 코딩을 해보자…

const multer = require('multer');
const AWS = require('aws-sdk'); 
const fs = require('fs');

const s3 = new AWS.S3({
    accessKeyId: '액세스 키',
    secretAccessKey: '비밀 액세스 키'
});

const upload = multer({ dest: 'image/'})
// dest or storage 파일을 저장할 위치 , 업로드를 더 잘 제어하려면 storage 옵션 대신 사용하는게 좋다고한다. 

router.post('/', upload.single('image'), async (req, res) => {
    // const { currentUser } = res.locals;
    // const userId = currentUser.id; 
    const userId = 1; 
    try {
        const { title, content } = await    
        postCreateValidation.validateAsync(req.body);
        const fileContent = fs.readFileSync(req.file.path);
        const fileExtension = req.file.originalname.split('.').pop(); // 파일 확장자 추출
        const fileName = `${Date.now()}_${req.file.originalname}`; // 현재 시간과 원본 파일 이름 결합
        const params = {
            Bucket: '버킷이름', // 버킷 이름 
            Key: fileName,
            Body: fileContent,
            ContentType: req.file.mimetype, // 파일 MIME 타입 설정
            ACL: 'public-read-write' // 업로드된 파일에 대한 권한 설정
        };
        s3.upload(params, async function(err, data){
            if (err) {
                throw err; 
            }
            const image = data.Location;
            const post = await Post.create({
                title,
                content,
                image,
                userId
            });
            res.json(post);
            // 업로드된 파일 삭제 
            fs.unlinkSync(req.file.path);
        });
    } catch (err) {
        if (err.isJoi) {
            return res.status(422).json({ message: err.details[0].message });
        }
        res.status(500).json({ message: err.message });
    }
});

간단하게 코드 설명을 해보면 upload.sing(‘image’)는 ‘image’라는 이름으로 클라이언트가 업로드한 파일을 하나만 받도록 설정한 부분이다.
s3.upload 메소드를 호출하여 S3에 파일을 업로드하고, 업로드가 완료되면 data.Location으로 업로도된 파일의 url을 가져와서 image 변수에 저장한다.
마지막으로 Post.create()으로 게시글을 생성하면서 게시글 정보를 클라이언트에게 응답한다. 이후 ‘fs.unlinkSync’ 함수를 호출해서 업로드된 파일을 삭제한다.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>이미지 파일 업로드 테스트</title>
</head>
<body>
  <h1>File Upload Test</h1>
  <form action="/posts" method="POST" enctype="multipart/form-data">
    <input type="file" name="image" required>
    <br>
    <label>Title:</label>
    <input type="text" name="title" required>
    <br>
    <label>Content:</label>
    <textarea name="content" required></textarea>
    <br>
    <input type="submit" value="Upload">
  </form>
<script>
    $(document).ready(function() {
  $('form').submit(function(event) {
    event.preventDefault(); // 폼 기본 동작 취소
    var formData = new FormData($(this)[0]);
    $.ajax({
      url: '/posts',
      type: 'POST',
      data: formData,
      contentType: false,
      processData: false,
      success: function(data) {
        alert('File uploaded successfully!');
      },
      error: function(jqXHR, textStatus, errorThrown) {
        alert('File upload failed: ' + errorThrown);
      }
    });
  });
});
    </script>
</body>
</html>

테스트용 프론트는 이렇게 작성을 했다.

에러1

AccessControlListNotSupported: The bucket does not allow ACLs 

해당 에러는 ACL을 내가 S3 버킷 설정에서 ACL을 사용하지 않도록 설정해서 발생한 것 같다. 그래서 나는 버킷설정에서 변경하는 것보다는 코드에서 해당 부분을 삭제하기로 했다.
NodeJs-Homework
일단 성공적으로 업로드가 되었다.
NodeJs-Homework
로컬 db에서도 확인해보니 경로가 잘 들어간 것을 확인 할 수 있었다.
NodeJs-Homework
AWS S3에도 잘 적용된 것을 확인 할 수 있었다…

일단 성공적으로 이미지를 잘 업로드 한 것 같다.
그렇다면 이제 S3에서 이미지를 불러오도록 해보자.

 <img src="https://moduhospital.s3.amazonaws.com/1678419075689_bird.jpg" alt="bird Image">


html에 해당 이미지 링크를 넣어서 확인해보니 에러가 발생했다.

에러2

Failed to load resource: the server responded with a status of 403 (Forbidden) 

크롬 개발자 도구로 확인한 결과 이런 에러가 발생했는데 아무래도 인증되지 않은 사용자가 요청을 보낸 경우 이러한 결과가 나온다고 한다.
일단 문제점을 하나하나 살펴보자면 정상적으로 S3에 저장이 되었고 db에도 정상적으 링크가 넣어진 것을 확인했다. 그렇다면 버킷 정책 설정이 잘못된 경우인 것 같은데…
버킷 정책에 public-read 권한을 주기로 했다.
공식문서를 다시 살펴보니 aws공식문서

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "S3ConsoleAccess",
            "Effect": "Allow",
            "Action": [
                "s3:GetAccountPublicAccessBlock",
                "s3:GetBucketAcl",
                "s3:GetBucketLocation",
                "s3:GetBucketPolicyStatus",
                "s3:GetBucketPublicAccessBlock",
                "s3:ListAccessPoints",
                "s3:ListAllMyBuckets"
            ],
            "Resource": "*"
        },
        {
            "Sid": "ListObjectsInBucket",
            "Effect": "Allow",
            "Action": "s3:ListBucket",
            "Resource": ["arn:aws:s3:::moduhospital"]
        },
        {
            "Sid": "AllObjectActions",
            "Effect": "Allow",
            "Action": "s3:*Object",
            "Resource": ["arn:aws:s3:::moduhospital/*"]
        }
    ]
}


버킷 선택 -> 버킷 정책 편집 -> json 형식으로 이렇게 넣으라고 되어있다.
이것을 참고해서

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PublicReadGetObject",
            "Effect": "Allow",
            "Principal": "*",
            "Action": [
                "s3:GetObject"
            ],
            "Resource": [
                "arn:aws:s3:::버킷이름/*"
            ]
        }
    ]
}


이렇게 작성을 하였다.
에러가 성공적으로 해결 되었다. 이제 url을 통해서 이미지를 볼 수 있게 되었다.
이제 내가 현재 진행중인 프로젝트에 적용을 하러가야겠다.

내일 할일

  • 프로젝트 이어서 하기

참고자료

express-multer 공식문서