express 프로젝트에서 사진 업로드 기능을 담당하게 되어서 AWS s3와 multer 패키지를 통해서 기능을 구현해볼려고 한다.
AWS 버킷 생성 및 키 발급은 옆의 포스트에서 다루었다.
포스트에 이미지를 업로드해서 데이터베이스에 저장하는 방식은 두가지를 생각했다.
자세한 로직은 다음과 같다.
저장
1. 클라이언트에서 중간중간에 input 태그를 통해서 이미지 넣기
2. 폼 형태로 서버로 모든 데이터 저장 요청
ex ) thumbnail, title, content, images
3. images를 s3에 저장후 url로 변환
3. DB에 저장
불러오기
1. 서버로 해당 포스트 데이터 요청
2. 포스트 내용 중간 중간 위치에 맞게 image 배치
3. 포스트 내용 보여주기
자세한 로직은 다음과 같다.
저장
1. 포스트를 쓸때 이미지를 넣을때마다 s3에 저장하고 url로 변환 후 포스트 내용에 넣기
2. 폼 형태로 서버로 데이터 저장 요청
ex ) thumbnail, title, content
3. DB에 저장
불러오기
1. 서버로 해당 포스트 데이터 요청
2. 포스트 내용 보여주기
aws에서 s3에 접근이 허용된 key를 통해 연동한다.
const s3 = new aws.S3({
accessKeyId: process.env.AWS_S3_ACCESS_KEY_ID, // aws key
secretAccessKey: process.env.AWS_S3_SECRET_ACCESS_KEY, // aws secret key
region: process.env.AWS_S3_REGION // aws s3 region
});
썸네일 multer s3
const uploadThumbnailMulter = multer({
storage: multer_s3({
s3, // s3 넣기
bucket: AWS_S3_BUCKET_NAME, // s3 버킷 이름
acl: "public-read-write", // s3 acl 읽기 및 쓰기
key: (req, file, callback) => {
// 확장자 지원 여부 확인
const ext = path.extname(file.originalname);
if (!imageExtensions.includes(ext)) {
return callback(new Error("잘못된 파일 확장자입니다."));
}
// s3에 images 폴더에 파일 저장
callback(null, `images/${Date.now()}_${file.originalname}`);
}
})
}).single("thumbnail"); // 이미지 한장 key값은 "thumbnail"로 설정
이미지 multer s3
const uploadImagesMulter = multer({
storage: multer_s3({
s3,
bucket: AWS_S3_BUCKET_NAME,
acl: "public-read-write",
key: (req, file, callback) => {
const ext = path.extname(file.originalname);
if (!imageExtensions.includes(ext)) {
return callback(new Error("잘못된 파일 확장자입니다."));
}
callback(null, `images/${Date.now()}_${file.originalname}`);
}
})
}).single("image");
일반적으로는 위에서 정한 uploadThumbnailMulter와 uploadImagesMulter를 바로 미들웨어로 연결해도 되지만 예외 처리가 되지 않아 서버가 바로 종료될수 있다.
따라서 다음과 같이 미들웨어로 한번 더 포장해서 사용한다.
썸네일 미들웨어
export const uploadThumbnail = (req, res, next) => {
uploadThumbnailMulter(req, res, (err) => {
if (err) {
return res.status(500).json({
ok: false,
message: "썸네일 업로드 실패하였습니다."
});
}
next();
});
};
이미지 미들웨어
export const uploadImages = (req, res, next) => {
uploadImagesMulter(req, res, (err) => {
if (err) {
return res.status(500).json({
ok: false,
message: "이미지 업로드 실패하였습니다."
});
}
next();
});
};
라우터에 이미지 미들웨어 연결하기
postRouter.post("/posts/image", uploadImages, async (req, res) => {
if (!req.file || !req.file.location) {
return res.status(400).json({
ok: false,
message: "사진 파일이 잘못되었습니다."
});
}
return res.status(201).json({
ok: true,
message: "사진이 성공적으로 업로드 되엇습니다.",
data: {
image: req.file.location
}
});
});