[23.12.05] TIL

yy·2023년 12월 5일

개발일지

목록 보기
53/122

오늘 할 부분은 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 다섯개가 들어간걸 볼 수 있다.
자 이제 이걸 클릭했을때 사진이 불러와지는지 확인할 차례.

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

시도 1. 버킷 권한 설정 => 안된다.


시도 2. 퍼블릭 액세스 차단 편집 ( 버킷 설정 ) 다 해제 => 안된다.

시도 3. CORS 설정 변경 => 안된다.

시도 4. 정책을 버킷 정책에 다 집어넣기 => 안된다.

시도 5. 코드 수정

/* 게시물 조회 */
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를 반복문 돌리는 부분에서 생긴것 같아 콘솔로 찍어봤다.
![](https://velog.velcdn.com/images/dbsdud143/post/e45da737-5fc7-4a94-a0f7-b6bca117f34f/image.png

s3에 올라온 이름과 동일하다
DB에 저장하는데에는 이상이 없어보인다. 이제 가져오는 도중 문제가 생긴 것처럼 보인다.

curl -v 명령어를 통해 아래과 같이 확인했다. 403 에러가 뜨는걸 확인할 수 있다.

하 어디가 문제인거지

시도 6. 라이브러리 확인 => 해결

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);
  }
});

https://juhi.tistory.com/10

profile
시간이 걸릴 뿐 내가 못할 건 없다.

0개의 댓글