# AWS-S3를 이용해 이미지 업데이트하기
< 문제 상황 >
이미지관련 처리를 하는 작업을 2명이 각자의 브랜치에서 작업하다보니, 통일성이 필요 했다.
나는 모듈과 서비스로직을 만들어서 활용했고, 다른 한분은 미들웨어로 처리를 하였다. 통일성을 위해 결국 미들웨어로 처리하기로 했다.
1. 모듈을 만들어 사용하기
프로필쪽 기능을 맡게 됐는데, 이미지를 불러와야하는데 처음이라 그런지 어떻게 해야할지 몰랐다. 그러던 와중 S3를 이용해 사진을 업로드 하신분들도 있어서 물어보면서 구글링을 하며 완성을 했다. 처음하다 보니 거의 하루를 투자해서 완성했던것 같다..
우선, AWS-S3 를 통해 이미지를 업로드하려면 준비해야 할게 있다. 그것은 바로 버킷을 생성하고 버킷이름, 리전, 액세스키, 시크릿키를 알아야한다. 그러고나서 나같은 경우는 따로 upload 폴더를 만들어서 app.module에 추가를 했다.
upload Service쪽을 보면, 아래와 같이 import를 해줘야 한다.
import * as AWS from 'aws-sdk';
import * as process from 'process';
export class UploadService {
private readonly s3 = new AWS.S3({
accessKeyId: process.env.S3_ACCESS_KEY,
secretAccessKey: process.env.S3_ACCESS_KEY_SECRET,
region: process.env.AWS_REGION,
});
그리고 액세스키, 시크릿키 ,리전 이름을 따로 env 파일에 추가를 해서 저렇게 불러오면 된다.
다음으론, 변수를 만들고 인자값을 준다. 그리고 그 안에 버켓이름을 실제 버켓이름으로 선언을 해줘야한다.
Bucket: AWS_S3_BUCKET,
Key: String(file.originalname),
Body: file.buffer,
변수하나를 만들고 위 처럼 설 버켓,키,바디에 들어갈 것들을 설정을 해주면 되는데, 위에 있는 file은 내가 인자(파라미터)로 준 값이기에 file로 나와있다. 그리고 body에서는 buffer 를 사용했다. 그 이후에는 try-catch문으로 성공적으로 저장할 수 있게 코드를 작성하면된다. (upload method 사용)
이렇게 서버로직을 다 구현하고 나서 프론트쪽에서도 추가를 해줘야 하는데, 아래와 같이
let formData = new FormData();
formData.append('name', name.value);
formData.append('email', email.value);
formData.append('nickname', nickname.value);
formData.append('address', address.value);
formData.append('subAddress', subAddress.value);
formData.append('tel', tel.value);
formData.append('file', profileImage.files[0]);
formData를 선언하고 append 해서 각자 필요한 값들을 넣어주면된다.
<< 위에는 내가 작업할때 프로필을 담당하다 보니 name,email 등이 들어간거임 >>
2. 미들웨어로 만들어 사용하기
use(req:Request, res:Response, next: NextFunction) {
const s3 = new AWS.S3({
accessKeyId:환경변수에서 설정한 액세스 키,
secretAccessKey:환경변수에서 설정한 시크릿 키,
region:환경변수에서 설정한 지역설정
})
}
우선 use 함수를 만들어서 파라미터에 req(요청),res(응답),next(미들웨어로 넘어감)을 만든다. 그리고 AWS S3 인스턴스를 만들어서
S3에 접근하기 위한 액세스키, 시크릿키, 리전(지역) 설정을 환경변수(.env) 파일에서 설정을 하고 가져온다.
const upload = multer({
storage: multerS3({
s3,
bucket: process.env.BUCKET_NAME,
contentType: multerS3.AUTO_CONTENT_TYPE,
shouldTransform: true,
key: function (_, file: Express.Multer.File, callback) {
const fileId = uuid();
const type = file.mimetype.split('/')[1];
if (!allowedExtensions.includes(path.extname(file.originalname.toLowerCase())) || !file.mimetype.startsWith('image/')) {
const errorMessage = '이미지 파일만 업로드가 가능합니다.';
const errorResponse = { errorMessage };
return res.status(HttpStatus.BAD_REQUEST).json({ errorResponse });
}
const fileName = `${fileId}.${type}`;
callback(null, fileName);
},
acl: 'public-read-write',
limit: { fileSize: 5 * 1024 * 1024 },
}),
});
upload.array('images', 5)(req, res, next);
그 다음, multer를 사용해 파일 업로드를 설정을 한다. multer를 설정하는 부분 안에서는 multer-s3를 사용해서 aws-s3에 저장하는 설정을 하면된다. 위의 방법은 파일이름은 uuid로 했고, 타입은 MIME 타입으로 하였다. 그리고 다른 확장자로 업로드가 되려고 하면 에러를 반환시켜는 조건을 추가하면 된다. 그리고 나는 이미지를 최대 5개까지만 제한두고 싶어서 array 메서드를 통해 5개까지 업로드하도록 하였다. 마지막으로 제일 좋은 방법은 try-catch문을 이용하여 에러를 잡으면서 메시지를 보며 접근하는것이 제일 좋을 것 같다.
# 기술 면접 공부
- CORS(Cross Origin Resource Sharing)
HTTP 헤더를 사용하여, 한 출처에서 실행 중인 웹애플리케이션이 다른 프로토콜,도메인,포트 등의 리소스에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 원리이다. 그래서 브라우저에서 Cross-origin(교차 출처) 요청을 안전하게 할 수 있다.
그럼 CORS(Cross Origin Resource Sharing) 가 왜 필요할까 ?
CORS가 없이 모든 곳에서 데이터를 요청할 수 있게 되면, 다른 사이트에서 원래 사이트를 흉내낼 수 있게 된다. 예를 들어서 기존 사이트와 완전히 동일하게 동작하도록 하여 사용자가 로그인을 하도록 만들고, 로그인했던 세션을 탈취하여 악의적으로 정보를 추출하거나 다른 사람의 정보를 입력하는 등 공격을 할 수 있는데, 이렇다 할 공격을 할 수 없도록 브라우저에서 보호하고, 필요한 경우 에만 서버와 협의하여 요청할 수 있도록 하기 위해서 필요하다.
CORS(Cross Origin Resource Sharing) 는 어떻게 동작할까 ?
1. 서버로 요청을 보낸다.
2. 서버의 응답이 왔을 때 브라우저가 요청한 Origin 과 응답한 헤더 Access-Controller-Request-Headers 의 값을
비교하여 유효한 요청이라면 리소스(resourse) 를 응답한다.
3. 유효하지 않은 요청이라면 브라우저에서 이를 막고 에러를 발생시킨다.
요청 헤더 목록
- Access-Controller-Request-Method : preflight 요청을 할때 실제 요청에서 어떤 메서드를 사용할 것인지, 서버에 알리기 위해 사용함
- Access-Controller-Request-Headers : preflight 요청을 할 때 실제 요청에서 어떤 header를 사용할 것인지, 서버에 알리기 위해 사용함
위에서 말한 preflight 요청이란 ?
간단한 요청이 아닌 cross-origin 요청, 즉 교차 출처는 모두 preflight 요청을 하게 되는데, 실제 요청을 보내는 것이 안전한지 확인하기 위해 먼저 OPTIONS method 를 사용하여 cross-origin(교차출처) HTTP 요청을 보낸다.
이렇게 하는 이유는 사용자 데이터에 영향을 미칠 수 있기때문에 그 전에 확인해보고 요청을 보내는 것이다.