[Next.js, Supabase] 파일 업로드 기능 오류

hyejinJo·2025년 8월 12일
0

React

목록 보기
19/19
post-thumbnail

Next.js 환경에서 Supabase 의 스토리지를 연결하여 파일업로드를 하는 작업을 진행하고 있었다. 하지만 계속 400 BadRequest 가 떠서 Supabase 공식문서 및 여러 곳에서 검색을 해보았지만 명확한 원인을 찾지 못했다…🙁

파일을 선택한 후 “파일업로드” 버튼을 누르면 Supabase 의 스토리지에 저장되도록 로직을 짜놓은 상태

storageActions.ts

'use server';

import { createServerSupabaseClient } from 'utils/supabase/server';

function handleError(error: Error) {
  if (error) {
    console.error(error);
    throw error;
  }
}

export async function uploadFile(formData: FormData) {
  const supabase = await createServerSupabaseClient();
  const file = formData.get('file') as File;

  const { data, error } = await supabase.storage
    .from(process.env.NEXT_PUBLIC_STORAGE_BUCKET)
    .upload(file.name, file, { upsert: true });

  handleError(error);
  return data;
}

AddFileZone.tsx

'use client';

import React, { useRef } from 'react';
import * as S from './styled';
import Button from '@/app/_modules/common/components/button/button/Button';
import { uploadFile } from 'actions/storageActions';

const AddFileZone = () => {
  const fileRef = useRef<HTMLInputElement>(null);

  return (
    <S.AddFileZone
      onSubmit={async (e) => {
        e.preventDefault();
        const file = fileRef.current?.files?.[0];
        if (file) {
          const formData = new FormData();
          formData.append('file', file);
					await uploadFile(formData);
        }
      }}
    >
      <input type='file' ref={fileRef} />
      <Button
        type='submit'
        text='파일 업로드'
        iconName='plus'
        filled
      />
    </S.AddFileZone>
  );
};

export default AddFileZone;

서버액션 파일에서 uploadFile 함수를 가져와 선택된 file 을 인수로 넣어 작동하게끔 해놓았다.

하지만 계속 에러가 뜨는 상황이었다.

수정

결국 AI 를 통해그 원인을 찾아보니, Next.js 에서는 서버액션을 할 때 FormData 형식의 데이터를 받는데 제한이 있다고 한다. 그래서 아래와 같이 수정을 했다.

AddFileZone.tsx

'use client';

import React, { useRef } from 'react';
import * as S from './styled';
import Button from '@/app/_modules/common/components/button/button/Button';

const AddFileZone = () => {
  const fileRef = useRef<HTMLInputElement>(null);

  // API Route로 파일 업로드
  const handleUpload = async (formData: FormData) => {
    const res = await fetch('/api/upload', {
      method: 'POST',
      body: formData,
    });
    const result = await res.json();
    if (!res.ok) throw new Error(result.error || 'Upload failed');
    return result.data;
  };

  return (
    <S.AddFileZone
      onSubmit={async (e) => {
        e.preventDefault();
        const file = fileRef.current?.files?.[0];
        if (file) {
          const formData = new FormData();
          formData.append('file', file);
          try {
            const result = await handleUpload(formData);
            console.log(result);
          } catch (err) {
            alert((err as Error).message);
          }
        }
      }}
    >
      <input type='file' ref={fileRef} />
      <Button type='submit' text='파일 업로드' iconName='plus' filled />
    </S.AddFileZone>
  );
};

export default AddFileZone;

서버액션인 uploadFile 을 무시하고, 따로 API Router 를 통해 클라이언트 요청을 진행했다.

route.ts

import { NextRequest, NextResponse } from 'next/server';
import { createServerSupabaseClient } from 'utils/supabase/server';

export async function POST(req: NextRequest) {
  try {
    const formData = await req.formData();
    const file = formData.get('file') as File | null;
    if (!file) {
      return NextResponse.json({ error: 'No file provided' }, { status: 400 });
    }

    const supabase = await createServerSupabaseClient();
    const { data, error } = await supabase.storage
      .from(process.env.NEXT_PUBLIC_STORAGE_BUCKET!)
      .upload(file.name, file, { upsert: true }); // safeName을 key로 사용
    if (error) {
      return NextResponse.json({ error: error.message }, { status: 500 });
    }
    return NextResponse.json({ data });
  } catch (err: any) {
    return NextResponse.json({ error: err.message || 'Unknown error' }, { status: 500 });
  }
}

이렇게 했더니 잘 동작하는 모습을 볼 수 있었다..!

하지만 파일 형식의 데이터가 왜 서버액션에서 지원이 안되는걸까 ㅠㅠ

profile
Frontend Developer 💡

0개의 댓글