Lsiron

HELLO FOLIO(express, mongoose)- 9(AWS S3, multer를 이용한 이미지 업로드 및 삭제 기능 구현) 본문

개발일지/HELLO FOLIO

HELLO FOLIO(express, mongoose)- 9(AWS S3, multer를 이용한 이미지 업로드 및 삭제 기능 구현)

Lsiron 2024. 8. 14. 02:59

현재 폴더 구조

web_project/
├── db/
│   ├── model             # 각종 모델 폴더
│   ├── schemas           # 각종 스키마 폴더
│   └── index.js          # db 연결파일
├── middleware/           # 각종 middleware 폴더
├── passport/	          # 로그인 관련 passport 폴더
│   ├── strategies        # 전략 구성
│   └── index.js          # 전략 exports 파일
├── routes/               # 라우트 관련 폴더
│   └── index.js     	  # 라우트 exports 파일
├── services/             # 유저 관련 service 폴더
├── views/                # EJS 템플릿 폴더
├── public/               # static 파일 폴더
│   ├── css/              # CSS 파일들
│   ├── js/               # JS 파일들
│   └── images/           # 이미지 파일들
├── .env                  # 환경 변수 파일
├── .gitignore            # Git 무시 파일
├── package.json          # 프로젝트 메타데이터 및 종속성 목록
├── app.js                # 애플리케이션 진입점
├── index.js              # 서버 실행 파일
└── README.md             # 프로젝트 설명 파일

현재 설치한 모듈

  "dependencies": {
    "bcrypt": "^5.1.1",
    "cookie-parser": "^1.4.6",
    "dotenv": "^16.4.5",
    "ejs": "^3.1.10",
    "express": "^4.19.2",
    "jsonwebtoken": "^9.0.2",
    "mongodb": "^6.7.0",
    "mongoose": "^8.4.3",
    "nodemailer": "^6.9.13",
    "passport": "^0.7.0",
    "passport-jwt": "^4.0.1",
    "passport-local": "^1.0.0"
  },

app.js

const express = require('express');                     
const app = express();
const bcrypt = require('bcrypt');
const passport = require('passport');				

const { userRouter, myPageRouter } = require("./routes");		               

require('./passport')();                                           
const authMiddleware = require('./middleware/authMiddleware');       
app.use(express.json());							
app.use(express.urlencoded({ extended: false }));	
app.use(cookieParser());                               

app.use(passport.initialize());					

app.use('/', userRouter);
app.use('/', resetPwRouter);				
app.use('/my-page', authMiddleware ,myPageRouter); 

module.exports = { app };

.env

# DB 접속 URL
DB_URL="디비주소"
# DB 프로젝트 이름
DB_NAME="디비이름"
# 서버 포트 번호
PORT=3000
# JWT토큰 발급 키
SECRET="secret"
# REFRESH 토큰 발급 키
REFRESH_SECRET="toosecret"
# 비밀번호 찾기 이메일 발송메일
EMAIL_USER=내이메일
# 비밀번호 찾기 이메일 비밀번호
EMAIL_PASS=내비번

 

추가 기능으로, 마이페이지에서 유저의 프로필 사진을 업데이트 하는 기능을 구현 하기 위해 aws s3와 multer를 이용해 보자.

 

보통 유저가 이미지를 서버로 전송하면 서버는 하드에 이미지를 저장하고 해당 이미지를 다른 유저도 볼 수 있도록 할 것이다. 허나, 유저가 많아질 경우엔, 하드가 견디기 힘들 수 있다.

 

때문에 파일 저장용 클라우드로 aws를 사용해보자.

 

먼저 aws로 들어가서 회원가입 후, 카드를 등록하고 IAM을 들어가서 엑세스 키를 발급받아야 한다.

 

IAM 으로 들어간 뒤, 왼쪽 사이드바의 사용자를 들어간다.

 

나는 미리 사용자를 만들어 두었기 때문에 목록이 뜬다. 이 IAM에서 엑세스 키를 발급받는 과정은 다른 유저가 나의 AWS 계정을 사용할 수 있도록 하위계정을 만드는 과정이다. 

 

사용자 생성 버튼을 눌러보자.

 

사용자 이름을 임의로 하나 적어주고 다음 버튼을 눌러보자.

 

권한 설정 탭이 뜨는데, 처음엔 그룹에 사용자 추가로 초기 설정이 돼 있을 것이다.

 

이를 직접 정책 연결로 클릭 해 준 후, S3를 검색하여 AmazonS3FullAccess 에 체크를 해 준 뒤, 다음 버튼을 눌러주자.

 

이는 다른 유저가 내  AWS의 하위계정에서 어떤 서비스를 이용할 수 있는지 권한을 설정 해 주는 것 이다. 즉, 다른 유저에게 S3를 이용할 수 있는 권한을 준 것이다.

 

이제 사용자 생성 버튼을 눌러주자.

 

이제 PROJECT 라는 하위계정이 만들어 진 것을 확인 할 수 있다. 이제 PROJECT 하위계정을 들어가 보자.

 

여기에서 엑세스 키를 만들어줘야 한다. 엑세스 키는 이 하위계정의 ID와 PW라고 생각하면 된다.

 

나는 로컬코드에서 AWS 계정을 엑세스 시켜 줄 것이기 때문에 로컬 코드로 선택 한뒤, 하단의 확인 버튼을 누르고 다음 버튼을 눌러보자.

 

태그 입력창은 무시해도 된다. 엑세스 키 만들기 버튼을 눌러보자.

 

엑세스 키와 비밀 엑세스 키가 생성이 되었다. 이는 후에 사용해야 하기 때문에 둘 다 복사를 해두고 잘 저장해두자.

 

이제 완료 버튼을 눌러주자. 반드시 저장 해 놓아야 한다.

 

여기서 ARN도 잘 복사를 해 두자. 이제 본격적으로 S3를 셋팅 해 보겠다.

( 편의상 기존에 만들어두었던 하위계정으로 진행 할 것이다. )

 

검색창에 S3를 검색하여 들어가보자.

 

현재 미리 만들어 두었기 때문에 버킷이 뜨지만, 아무 버킷도 없을 때는 버킷 만들기 버튼만 덩그러니 있다.

 

한번 버킷 만들기 버튼을 클릭해보자.

임의로 이름을 설정하고, ACL 비활성화됨을 클릭 해 주자. 

 

 

아마 처음에 퍼블릭 엑세스 차단이 설정되어 있을텐데 일단 아래와 같이 풀어주자.

 

테스트가 모두 끝나면 "새 ACL을 통해 부여된 버킷 및 객체에 대한 퍼블릭 엑세스 차단" 및 "임의의 ACL을 통해 부여된 버킷 및 객체에 대한 퍼블릭 엑세스 차단"은 활성화 시켜주자.

( 외부에서 S3 버킷이나 객체에 무단으로 접근하는 것을 방지하기 위함. )

 

이 외에는 기본 설정으로 두고 버킷 만들기를 눌러주자.

 

버킷이 생성 되었음을 확인 할 수 있다. 허나 여기서 끝이 아니다. 권한을 설정 해 주어야 한다.

 

즉, 어떤 사람들이 이 버킷에 이미지를 업로드 할 수 있는지 지정을 해 주어야 한다는 뜻.

 

권한은 버킷 정책을 설정하거나 ACL 설정을 통해 설정 할 수 있다.

 

ACL설정은 비교적 예전에 사용하는 방법이기 때문에 현재는 버킷 정책을 통해 권한을 설정 해 주는 방법이 더 권장된다.

 

때문에 버킷정책의 편집 버튼을 눌러주자. 누가 버킷을 읽기, 수정, 삭제 할 수 있는지 정의하는 부분이다.

 

아래와 같이 입력 해 주자.

 

설명은 아래를 참고하면 되겠다. 버킷 정책 예시는 https://docs.aws.amazon.com/ko_kr/AmazonS3/latest/userguide/example-bucket-policies.html

 

Amazon S3 버킷 정책 예시 - Amazon Simple Storage Service

특정 IP 주소에 대한 액세스를 제한할 때는 S3 버킷에 액세스할 수 있는 VPC 엔드포인트, VPC 소스 IP 주소 또는 외부 IP 주소도 지정해야 합니다. 그렇지 않으면 적절한 권한이 이미 갖춰지지 않은

docs.aws.amazon.com

위 링크에서 참조할 수 있다.

{
    "Version": "2012-10-17", //Bucket Policy 문법이 확정된 날짜
    "Statement": [
        {
            "Sid": "IpAllow", // 각 Statement의 고유 아이디. 보통 무슨 역할을 하는지 써준다.
            "Effect": "Allow", // 특정 사용자에 대해 명령을 제한(Deny)하거나, 허용(Allow)하는 식으로 사용한다.
            "Principal": "*", // Bucket Policy의 적용대상, 대상을 전체로 설정
            "Action": "s3:GetObject", // Bucket Policy에서 허용한 Action, s3:GetObject는 자료를 읽는 행동
            "Resource": "arn:aws:s3:::lsiron/*" // 대상이 대는 Bucket에 대한 명세, 우리 버킷의 이름을 써 주자.
        },
        {
            "Sid": "AdminAllow", // 각 Statement의 고유 아이디. 보통 무슨 역할을 하는지 써준다.
            "Effect": "Allow", // 특정 사용자에 대해 명령을 제한(Deny)하거나, 허용(Allow)하는 식으로 사용한다.
            "Principal": {
                "AWS": "사용자 계정의 ARN" // Bucket Policy의 적용대상, 대상을 관리자로 설정, 액세스 키 발급받았던 그 사용자 계정의 ARN을 넣어주자. 
            },
            "Action": [
                "s3:PutObject",  // Bucket Policy에서 허용한 Action, s3:PUTObject는 자료를 쓰는 행동
                "s3:DeleteObject"  // Bucket Policy에서 허용한 Action, s3:DeleteObject는 자료를 삭제하는 행동
            ],
            "Resource": "arn:aws:s3:::lsiron/*" // 대상이 대는 Bucket에 대한 명세, 우리 버킷의 이름을 써 주자.
        }
    ]
}

 

이제 변경사항 저장을 눌러주자.

 

스크롤을 내리면 CORS를 설정 할 수 있다.

 

어떤 도메인에서 이 버킷에 있는 이미지를 사용할 수 있는지 설정하는 부분이다.

 

설명은 아래를 참고하면 되겠다. 마찬가지로 CORS 정책 예시는 
https://repost.aws/ko/knowledge-center/s3-configure-cors

 

Amazon S3에서 CORS 구성 및 확인

Amazon Simple Storage Service(S3) 버킷의 리소스에 대한 교차 오리진 액세스를 허용하고 싶은데 오류가 발생합니다.

repost.aws

위 링크에서 참조할 수 있다.

[
    {
        "AllowedHeaders": [
            "*"  // 클라이언트가 요청에 포함할 수 있는 모든 헤더를 허용
        ],
        "AllowedMethods": [
            "PUT",  // 클라이언트가 PUT 메서드를 사용하여 S3 버킷에 요청을 할 수 있도록 허용
            "POST"  // 클라이언트가 POST 메서드를 사용하여 S3 버킷에 요청을 할 수 있도록 허용
        ],
        "AllowedOrigins": [
            "배포 시, 도메인 주소 입력"  // 클라이언트 요청을 허용할 도메인(출처, Origin)을 지정. 실제 배포 시, 해당 사이트의 도메인 주소를 입력해야 함
        ],
        "ExposeHeaders": [
            "ETag"  // 클라이언트가 응답 헤더 중 ETag 헤더에 접근할 수 있도록 허용
        ]
    }
]

 

이제 변경사항 저장 버튼을 눌러주면 AWS S3 설정이 끝났다. 이제 서버에서 이미지를 업로드 할 수 있는 기능을 만들어 보자.