@strapi/provider-upload-cloudinary
경로 : 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에 가입한 뒤 Dashboard 탭에 들어가면 나오는 Cloud Name, API Key, API Secret을 .env
파일에 넣어준다.
경로 : .env
CLOUDINARY_NAME=Cloud Name
CLOUDINARY_KEY=API Key
CLOUDINARY_SECRET=API Secret
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