[Strapi] react-hook-form & Cloudinary로 이미지 업로드 하기

이춘구·2022년 5월 27일
1

플러그인 설치

@strapi/provider-upload-cloudinary

plugin 파일 생성

경로 : config/plugin.js

module.exports = ({ env }) => ({
  upload: {
    config: {
      provider: "cloudinary",
      providerOptions: {
        cloud_name: env("CLOUDINARY_NAME"),
        api_key: env("CLOUDINARY_KEY"),
        api_secret: env("CLOUDINARY_SECRET"),
      },
      actionOptions: {
        upload: {},
        uploadStream: {},
        delete: {},
      },
    },
  },
});

Cloudinary 가입 및 환경변수 설정

Cloudinary에 가입한 뒤 Dashboard 탭에 들어가면 나오는 Cloud Name, API Key, API Secret을 .env 파일에 넣어준다.

경로 : .env

CLOUDINARY_NAME=Cloud Name
CLOUDINARY_KEY=API Key
CLOUDINARY_SECRET=API Secret

Admin Panel의 Media Library에서 이미지 미리보기가 깨지는 현상 해결

Strapi의 Admin Panel에 이미지를 업로드 하면 Cloudinary에도 업로드 되는 걸 확인할 수 있다.

업로드는 정상적으로 되는 걸 확인했지만 Strapi의 Media Library에서 업로드 된 이미지의 미리보기가 깨지는 현상이 발생한다.

이는 Strapi의 middleware를 아래와 같이 수정해서 해결할 수 있다.

경로 : config/middlewares.js

module.exports = [
  "strapi::errors",
  {
    name: "strapi::security",
    config: {
      contentSecurityPolicy: {
        useDefaults: true,
        directives: {
          "connect-src": ["'self'", "https:"],
          "img-src": [
            "'self'",
            "data:",
            "blob:",
            "dl.airtable.com",
            "res.cloudinary.com",
          ],
          "media-src": [
            "'self'",
            "data:",
            "blob:",
            "dl.airtable.com",
            "res.cloudinary.com",
          ],
          upgradeInsecureRequests: null,
        },
      },
    },
  },
  "strapi::cors",
  "strapi::poweredBy",
  "strapi::logger",
  "strapi::query",
  "strapi::body",
  "strapi::session",
  "strapi::favicon",
  "strapi::public",
];

Media Library의 이미지가 깨지지 않고 잘 나온다.

이미지 업로드 하는 코드 작성

import { useState } from "react";
import { NextPage } from "next";
import axios from "axios";
import { useForm } from "react-hook-form";

interface AddProductValues {
  productName: string;
  price: number;
  discountRate: number;
  stock: number;
  images: FileList;
}

const AddProduct: NextPage = () => {
  const { register, handleSubmit } = useForm<AddProductValues>();

  // addProduct는 원래 다른 파일에 분리되어 있던 코드이다.
  const addProduct = async (values: AddProductValues) => {
    const formData = new FormData();
    const { images, ...rest } = values;
    formData.append("files.images", images[0]);
    formData.append("data", JSON.stringify(rest));
    const { data } = await axios.post(
      `${process.env.NEXT_PUBLIC_STRAPI_URL}/api/products`,
      formData,
      { headers: { "content-type": "multipart/form-data" } }
    );
    return data;
  };
  
  const onSubmit = async (values: AddProductValues) => {
    try {
      const data = await addProduct(values);
      if (data) alert("제품 등록 성공");
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        const errorMessage = (error.response.data as { error: Error }).error
          .message;
        alert(`제품 등록 실패 \n ${errorMessage}`);
      } else {
        alert("등록 도중에 오류 발생");
      }
    }
  };

  return (
    <>
      <h1>제품 등록</h1>
      <form onSubmit={handleSubmit(onSubmit)}>
        <div>
          <label htmlFor="productName">제품명</label>
          <input type="text" id="productName" {...register("productName")} />
        </div>
        <div>
          <label htmlFor="price">가격</label>
          <input type="text" id="price" {...register("price")} />
        </div>
        <div>
          <label htmlFor="discountRate">할인율</label>
          <input type="text" id="discountRate" {...register("discountRate")} />
        </div>
        <div>
          <label htmlFor="stock">재고</label>
          <input type="text" id="stock" {...register("stock")} />
        </div>
        <div>
          <label htmlFor="images">제품 이미지</label>
          <input type="file" id="images" {...register("images")} />
        </div>
        <button type="submit">제품 등록</button>
      </form>
    </>
  );
};

export default AddProduct;

References

@strapi/provider-upload-cloudinary
Upload file during entry creation

profile
프런트엔드 개발자

0개의 댓글