Nextjs 에서 MULTER로 이미지 업로드 - 2- (S3에 올리기)

column clash·2021년 10월 1일
1

지난 1편에서는 로컬에 이미지를 올리는 것을 기록해두었다.
2편에서는 S3 에 올려보자.

  1. S3 로 이동하자.
    https://s3.console.aws.amazon.com/s3/home?region=ap-northeast-2
    콘솔에 로그인 => s3

  2. 버킷 생성

  3. 이 버킷의 퍼블릭 액세스 차단 설정 => 해제

  4. 권한 => 정책

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AddPerm",
            "Effect": "Allow",
            "Principal": "*",
            "Action": [
                "s3:DeleteObject",
                "s3:GetObject",
                "s3:ListBucket",
                "s3:PutObject",
                "s3:PutObjectAcl"
            ],
            "Resource": [
                "arn:aws:s3:::버킷이름",
                "arn:aws:s3:::버킷이름/*"
            ]
        }
    ]
}

저장

  1. 아이디 ▼ 클릭

내 보안자격 증명 클릭
액세스키 => 새 액세스키 만들기

rootkey 다운로드가 되고, 그 액셀파일에 아이디 비번이 있음.

  1. yarn add multer-s3 aws-sdk

  2. .env 에
    S3_ACCESS_KEY_ID= rootkey 아이디
    S3_SECRET_ACCESS_KEY= rootkey 비밀번호
    (보통의 경우 ACCESS KEY 가 아닌 IAM 방식으로 많이함)

  3. 코드가 변경되는 부분은 크지 않다. /api/imgupload/index.js 살짝과
    imgup.tsx 의 경로 부분만 약간 달라진다.

(실제 s3 에 올라가기 전에, 한 파일당 1mb 이상이면, user 에게
한 파일당 1mb 이상 올라갈 수 없다고 하고 종료된다.

lamda 로 이미지 리사이징을 해도 되지만, 미리 user 가
적절한 크기에 맞게 올리는 것도 좋은 일이다.

단, 만약 sns 같은 서비스를 한다면, 핸드폰에 있는 사진을
찍어서 올릴 것이기때문에, 이런 경우, lamda 로 이미지
리사이징을 해주는 것이 좋을 것이다.

3편에서는 lamda 로 이미지 리사이징을 하고,
올리는 것 외에, 삭제하는 것까지를 하고,
이미지 관리 부분을 마무리 지으려 한다.)

/api/imgupload/index.js

import nextConnect from "next-connect";
import multer from "multer";
import path from "path";
import dayjs from "dayjs";
import multerS3 from "multer-s3";
import AWS from "aws-sdk";

const app = nextConnect({
  onError(error, req, res) {
    res
      .status(501)
      .json({ error: `Sorry something Happened! ${error.message}` });
  },
  onNoMatch(req, res) {
    res.status(405).json({ error: `Method '${req.method}' Not Allowed` });
  },
});

AWS.config.update({
  accessKeyId: process.env.S3_ACCESS_KEY_ID,
  secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,
  region: "ap-northeast-2",
});
var upload = multer({
  storage: multerS3({
    s3: new AWS.S3(),
    bucket: "cultureplace",
    key(req, file, cb) {
      const nowDate = dayjs(Date.now()).format("YYMMDDHHMM");
      cb(null, `original/${nowDate}_${file.originalname}`);
    },
  }),
  limits: { fileSize:  1024 * 1024 }, // 1mb
});

app.post(upload.array("file"), function (req, res) {
  return res.json(req.files.map((v) => v.location));  
});

export default app;

export const config = {
  api: {
    bodyParser: false, // Disallow body parsing, consume as stream
  },
};

/components/imgup.tsx

import React, { useCallback, useState } from "react";
import axios from "axios";
import { UiFileInputButton } from "./components/UiFileInputButton";

const IndexPage = () => {
  const [thumb, setThumb] = useState<string[]>([]);
  const [progress, setProgress] = useState<number>(0);
  const onChange = useCallback(
    async (formData: FormData) => {
      const config = {
        headers: { "content-type": "multipart/form-data" },
        onUploadProgress: (event: { loaded: number; total: number }) => {
          setProgress(Math.round((event.loaded * 100) / event.total));
        },
      };
      axios
        .post<any>("/api/imgupload", formData, config)
        .then((res) => {
          setThumb([...thumb, ...res.data]);
        })
        .catch(function (error) {
          if (
            error.response.data.error ===
            "Sorry something Happened! File too large"
          )
            return alert(
              "한 파일당 1MB 이상 올리 실 수 없습니다. 확인 후 다시 올려주세요"
            );
        });
    },
    [thumb]
  );

  return (
    <>
      <p>
        <span>이미지 업로드</span>
        <span>{progress}</span>
      </p>
      <UiFileInputButton
        label="Upload Single File"
        // acceptedFileTypes 로 파일 올릴 수 있는 것을 제한 가능
        acceptedFileTypes="image/*"
        allowMultipleFiles={true}
        uploadFileName="file"
        onChange={onChange}
      />
      <ul>
        {thumb &&
          thumb.map((item: string, i: number) => {
            console.log("item", item);
            return (
              <li key={i}>
                <img src={item} width="300" alt="업로드이미지" />
              </li>
            );
          })}
      </ul>
    </>
  );
};

export default IndexPage;

profile
풀스택 개발 중...

0개의 댓글