오늘 할 부분은 S3에 이미지 업로드를 한 뒤 S3로 만들어진 주소를 DB에 저장해서 조회하기.
게시글을 작성할 때 사진 여러장을 업로드할 때를 대비하여 imgUrl을 배열형태로 보내줬어야했다.
(파일이름이 16진수로 보이는건 파일이름이 겹치지않도록 만들었기때문)
배열형태로 만드는 것까지 성공했으나 이제 저런 아래와 같은 오류가 떴다.
import express from "express";
import multer from "multer";
import { prisma } from "../utils/prisma/index.js";
import {
S3Client,
PutObjectCommand,
GetObjectAclCommand,
DeleteObjectCommand,
} from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import dotenv from "dotenv";
import crypto from "crypto";
const router = express.Router();
dotenv.config();
const bucketName = process.env.BUCKET_NAME;
const bucketRegion = process.env.BUCKET_REGION;
const accessKey = process.env.ACCESS_KEY;
const secretAccessKey = process.env.SECRET_ACCESS_KEY;
const s3 = new S3Client({
credentials: {
accessKeyId: accessKey,
secretAccessKey: secretAccessKey,
},
region: bucketRegion,
});
const storage = multer.memoryStorage();
const upload = multer({ storage: storage });
const randomImgName = (bytes = 32) => crypto.randomBytes(bytes).toString("hex");
/* 게시물 작성 */
router.post("/posts", upload.array("imgUrl", 5), async (req, res, next) => {
try {
const {
content,
likeCount,
categoryName,
storeName,
address,
latitude,
longitude,
} = req.body;
const nickname = "김아무개";
const user = await prisma.users.findFirst({
where: { nickname },
});
if (!user) {
return res.status(400).json({ message: "유저가 존재하지 않습니다." });
}
const category = await prisma.categories.findFirst({
where: { categoryName },
});
if (!category) {
return res.status(400).json({ message: "카테고리가 존재하지 않습니다." });
}
const districtName = address.split(" ")[1];
const district = await prisma.districts.findFirst({
where: { districtName }
});
if (!district) {
return res.status(400).json({ message: "지역이 존재하지 않습니다." });
}
console.log("req.body", req.body);
console.log("req.files", req.files);
const imgPromises = req.files.map(async (file) => {
const imgName = randomImgName();
const params = {
Bucket: bucketName,
Key: imgName,
Body: file.buffer,
ContentType: file.mimetype,
};
const command = new PutObjectCommand(params);
await s3.send(command);
return imgName;
});
const imgNames = await Promise.all(imgPromises);
// location 생성
const location = await prisma.locations.create({
data: {
storeName,
address,
latitude,
longitude,
starAvg: 1,
Category: { connect: { categoryId: +category.categoryId } },
User: { connect: { userId: +user.userId } },
District: { connect: { districtId: +district.districtId } },
},
});
// posts 생성
const post = await prisma.posts.create({
data: {
content,
likeCount: +likeCount,
User: { connect: { userId: +user.userId } },
Category: { connect: { categoryId: +category.categoryId } },
Location: { connect: { locationId: +location.locationId } },
imgUrl: imgNames,
},
});
return res.status(200).json([post]);
} catch (error) {
console.log("error", error);
}
});
Argument `imgUrl`: Invalid value provided. Expected String, provided (String, String, String, String, String).

그래서 join을 이용해서 아싸리 ,로 구분했다. join(",")

그랬더니 오류는 안나고 이런 결과가 반환되었다.
조회를 해보니

저 링크는 하나인데...넣어떤 이미지 여러개는 어디갔는가..
이렇게 하면 안되는가보다.
그렇다면 하나의 imgUrl마다 튜플을 생성하도록 했다.
router.post("/posts", upload.array("imgUrl"), async (req, res, next) => {
try {
const {
content,
likeCount,
categoryName,
storeName,
address,
latitude,
longitude,
} = req.body;
const nickname = "김아무개";
//user확인
const user = await prisma.users.findFirst({
where: { nickname },
});
if (!user) {
return res.status(400).json({ message: "유저가 존재하지 않습니다." });
}
//category 확인
const category = await prisma.categories.findFirst({
where: { categoryName },
});
if (!category) {
return res.status(400).json({ message: "카테고리가 존재하지 않습니다." });
}
console.log("req.body", req.body);
console.log("req.files", req.files);
const imgPromises = req.files.map(async (file) => {
const imgName = randomImgName();
const params = {
Bucket: bucketName,
Key: imgName,
Body: file.buffer,
ContentType: file.mimetype,
};
const command = new PutObjectCommand(params);
await s3.send(command);
return imgName;
});
const imgNames = await Promise.all(imgPromises);
const districtName = address.split(" ")[1];
const district = await prisma.districts.findFirst({
where: { districtName }
});
if (!district) {
// districtId를 찾지 못한 경우에 대한 처리
return res.status(400).json({ message: "지역이 존재하지 않습니다." });
}
//location 생성 : 저장 후 사용해야함.
const location = await prisma.locations.create({
data: {
storeName,
address,
latitude,
longitude,
starAvg: 1,
Category: { connect: { categoryId: +category.categoryId } },
User: { connect: { userId: +user.userId } },
District: { connect: { districtId: +district.districtId } },
},
});
const postsPromises = imgNames.map(async (imgName) => {
const post = await prisma.posts.create({
data: {
content,
imgUrl: imgName,
likeCount: +likeCount,
User: { connect: { userId: +user.userId } },
Category: { connect: { categoryId: +category.categoryId } },
Location: { connect: { locationId: +location.locationId } },
},
});
return post;
});
const posts = await Promise.all(postsPromises);
return res.status(200).json({ posts });
} catch (error) {
console.log("error", error);
}
});

같은 내용, 같은 UserId로 다른 imgUrl 다섯개가 들어간걸 볼 수 있다.
자 이제 이걸 클릭했을때 사진이 불러와지는지 확인할 차례.

역시 안되는군. 푸하하 재밌어. 권한이 문제인 듯하다.







/* 게시물 조회 */
router.get("/posts", async (req, res, next) => {
try {
const posts = await prisma.posts.findMany({
select: {
User: {
select: {
nickname: true,
imgUrl: true
}
},
Location: {
select: {
storeName: true,
address: true,
starAvg: true
}
},
imgUrl: true,
content: true,
likeCount: true
},
orderBy: { createdAt: 'desc' },
take: 3
});
for (const post of posts) {
console.log("포스트", post)
const getObjectParams = {
Bucket: bucketName,
Key: post.imgUrl,
};
//여기서 부터 문제
const command = new GetObjectAclCommand(getObjectParams);
const url = await getSignedUrl(s3, command, { expiresIn: 3600, ACL: 'public-read' });
post.imgUrl = url;
console.log("이미지유알엘", post.imgUrl)
}
return res.status(200).json(posts);
} catch (error) {
console.log("error", error);
}
});
posts를 반복문 돌리는 부분에서 생긴것 같아 콘솔로 찍어봤다.

하 어디가 문제인거지
get요청하는 라이브러리는 @aws-sdk/s3-request-presigner 을 사용한다. 그래서 혹시나 내가 문법을 잘 못 사용했던걸까 싶어 문서를 찾아봤다.
https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/Package/-aws-sdk-s3-request-presigner/

저 부분이 내가 작성한 부분과 상이했다. 고쳤더니 해결...
해결 후 코드
import express from "express";
import multer from "multer";
import { prisma } from "../utils/prisma/index.js";
import {
S3Client,
PutObjectCommand,
GetObjectCommand,
DeleteObjectCommand,
} from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import dotenv from "dotenv";
import crypto from "crypto";
const router = express.Router();
dotenv.config();
const bucketName = process.env.BUCKET_NAME;
const bucketRegion = process.env.BUCKET_REGION;
const accessKeyId = process.env.ACCESS_KEY;
const secretAccessKey = process.env.SECRET_ACCESS_KEY;
const s3 = new S3Client({
credentials: {
accessKeyId,
secretAccessKey,
},
region: bucketRegion,
});
const storage = multer.memoryStorage();
const upload = multer({ storage: storage });
const randomImgName = (bytes = 32) => crypto.randomBytes(bytes).toString("hex");
/* 게시물 조회 */
router.get("/posts", async (req, res, next) => {
try {
const posts = await prisma.posts.findMany({
select: {
User: {
select: {
nickname: true,
imgUrl: true
}
},
Location: {
select: {
storeName: true,
address: true,
starAvg: true
}
},
imgUrl: true,
content: true,
likeCount: true
},
orderBy: { createdAt: 'desc' },
take: 3
});
for (const post of posts) {
console.log("포스트", post)
const getObjectParams = {
Bucket: bucketName,
Key: post.imgUrl,
};
//여기서 부터 문제
const command = new GetObjectCommand(getObjectParams);
const url = await getSignedUrl(s3, command, { expiresIn: 3600 });
post.imgUrl = url;
console.log("이미지유알엘", post.imgUrl)
}
return res.status(200).json(posts);
} catch (error) {
console.log("error", error);
}
});