-
[Next.js] Route Handlers를 활용하여 storage에 이미지 업로드하기Next.js 2023. 7. 3. 23:59
Next.js의 Route Handlers를 통해 Supabase storage에 이미지를 업로드하는 기능을 구현했던 과정을 정리해보겠습니다.
먼저, 사용자가 Image를 입력하고 storage에 업로드 할 수 있는 컴포넌트를 생성합니다.
// ImageForm.tsx interface ImageState { imageFile: File | null; imageSrc: string; imageUrl: string; } function ImageForm() { const [imageState, setImageState] = useState<ImageState>({ imageFile: null, imageSrc: '', imageUrl: '', }); const setImagePreview = async (event: React.ChangeEvent<HTMLInputElement>) => { if (!event.target.files) return; const imageFile = event.target.files[0]; if (!imageFile.type.includes('image/')) return window.alert('이미지 파일만 업로드 가능합니다.'); if (imageFile.size > 512000) return window.alert('500kb 미만의 파일만 업로드 가능합니다.'); const imageSrc = URL.createObjectURL(imageFile); setImageState({ ...imageState, imageFile: imageFile, imageSrc: imageSrc }); }; const handleImageRegisterButtonClick = async () => { if (!imageState.imageFile) return window.alert('향수 이미지를 입력해주세요!'); const formData = new FormData(); formData.append('file', imageState.imageFile); formData.append('name', imageState.imageFile.name); const response = await fetch('/api/product/image', { method: 'POST', body: formData, }).then((res) => res.json()); setImageState({ ...imageState, imageUrl: response.imageUrl }); return window.alert('향수 이미지가 등록되었습니다.'); }; return ( <div className="w-full flex flex-col justify-start items-center text-stone-800"> <form className="w-full flex flex-col items-center gap-4"> <div className="w-48 h-48 flex justify-center items-center bg-stone-100 text-stone-600"> <label htmlFor="image"> {imageState.imageSrc ? ( <Image src={imageState.imageSrc} alt="productImage" width={176} height={176} /> ) : ( <BsPlus size={48} className="cursor-pointer" /> )} </label> <input className="hidden" onChange={setImagePreview} type="file" accept="image/*" id="image" name="imgae" /> </div> <Button text="향수 이미지 등록" onClick={handleImageRegisterButtonClick} type="button" /> </form> </div> ); } export default ImageForm;
위 코드에서 setImagePreview 함수는 사용자가 input 영역에 이미지 파일을 등록하고 등록된 이미지 파일을 화면에 띄우는 역할을 하는 함수입니다.
이 때 입력된 파일의 타입과 크기를 체크하여 입력된 파일이 조건에 맞는 경우에만 URL.createObjectURL() 메서드를 통해 생성된 URL을 생성하고 imageState를 업데이트 하도록 구현하였습니다.
handleImageRegisterButtonClick 함수는 FormData 객체를 생성하고, 사용자가 등록한 이미지 파일과 이미지 이름을 객체에 담아 HTTP 요청을 보내는 함수입니다.
다음으로, API 요청을 위한 handler를 생성합니다.
Handler를 구현하며 client에서 HTTP 요청의 body에 전달된 FormData를 처리하는 과정에서 많은 에러를 경험했는데, 그 과정에서 검색을 통해 Fetch API의 Request 인터페이스에 formData() 메서드가 존재한다는 것을 알게 되었습니다.
MDN 문서에서는 Request 인터페이스의 formData() 메서드를 아래와 같이 request의 body를 읽어 FormData 객체를 이행하는 promise를 반환한다고 설명하고 있습니다.
The formData() method of the Request interface reads the request body and returns it as a promise that resolves with a FormData object.
이러한 내용을 바탕으로 body에 전달된 FormData를 처리할 수 있었고, 아래 코드와 같이 handler 구현을 마무리 할 수 있게 되었습니다.
// app/api/product/image/route.ts import { supabase } from '@/utils/supabase/supabase'; import { NextResponse } from 'next/server'; export async function POST(request: Request) { const formData = await request.formData(); const file = formData.get('file') as File; const name = String(formData.get('name')); const { data, error } = await supabase.storage.from('product_image').upload(name, file); if (error) { console.error(error) throw new Error('향수 이미지 등록 실패!'); } return NextResponse.json({ status: 201, data: data.path }); }
참고 자료
- https://nextjs.org/docs/app/building-your-application/routing/router-handlers
- https://developer.mozilla.org/en-US/docs/Web/API/Request/formData
'Next.js' 카테고리의 다른 글
[Next.js] next/image src 외부 이미지 URL로 설정하기 (0) 2023.06.22 [Next.js] URL query string 관리하기 (0) 2023.04.10