[TIL] 개인 : Supabase Storage에 이미지 업로드하고 public URL 반환하는 함수 만들기

최유나·2025년 7월 21일
0

TIL

목록 보기
35/36

public URL

✅ public URL이란?

누구나 접근할 수 있는 "인터넷 주소"
즉, 로그인하지 않아도 이미지나 파일에 접근할 수 있는 공개 링크

🔍 예시

https://abc.supabase.co/storage/v1/object/public/images/mycat.jpg

이런 주소가 생성되면, 이 링크를 다른 사람한테 줘도, 그 사람은 바로 이미지를 볼 수 있음

📦 Supabase에서 말하는 public URL은?

Supabase의 Storage는 파일을 저장하는 "클라우드 폴더"
거기서 getPublicUrl()이라는 함수를 쓰면 →
해당 파일에 접근 가능한 공개 주소 (public URL) 을 만들어줌

🧁 요약하자면

public URL = "누구나 볼 수 있는 파일 링크"
로그인 필요 없음
블로그, 프로필 이미지, 썸네일 등에 자주 사용됨

public URL 반환 유틸


/**
 * Supabase Storage에 이미지를 업로드하고 public URL을 반환
 * @param {File} file - 업로드할 이미지 파일 (input[type="file"]의 파일 객체)
 * @param {string} bucket - 저장할 버킷 이름 (기본값: 'images')
 * @param {string} folder - 저장할 폴더 경로 (기본값: 'public')
 * @returns {string|null} 업로드된 이미지의 public URL 또는 null
 */

export const getImageURL = async (file, bucket = 'images', folder = 'public') => {
  let imageUrl = null;
  if (!file) return null;

  try {
    const ext = file.name.split('.').pop();
    const filename = `${Date.now()}-${crypto.randomUUID()}.${ext}`;
    const filepath = `${folder}/${filename}`;

    const { error: uploadError } = await supabase.storage.from(bucket).upload(filepath, file);
    
    if (uploadError) {
      console.error(`[이미지 업로드 실패], ${uploadError.message}`);
      return null;
    }

    const { data, error: urlError } = supabase.storage.from(bucket).getPublicUrl(filepath);
    if (urlError) {
      console.error(`[이미지 URL 가져오기 실패], ${urlError.message}`);
      return null;
    }

    imageUrl = data.publicUrl;
  } catch (e) {
    console.error('[이미지 업로드 오류]', e.message);
    return null;
  }

  return imageUrl;
};
  • file.name에서 확장자 추출 후 UUID 기반으로 파일명 생성
  • 업로드 후 에러 체크
  • getPublicUrl()로 공개 URL 받아서 반환
  • 실패 시 null 반환

SupabaseStorage 기능을 이용해, 사용자가 업로드한 이미지 파일을 저장하고
해당 이미지의 공개 URL(publicUrl) 을 반환하는 유틸 함수를 만들었다.

파일명은 중복을 방지하기 위해 Date.now()crypto.randomUUID() 를 조합해 고유하게 생성했고,
지정한 경로에 이미지를 업로드한 후 getPublicUrl() 을 통해 public URL을 받아오는 흐름이다.

업로드나 URL 생성 중 에러가 발생할 경우에는 null을 반환하도록 예외 처리를 했다.
성공적으로 업로드되면 data.publicUrlimageUrl로 받아 최종적으로 반환한다.

사용법

const handleUpload = async (e) => {
  const file = e.target.files[0];
  const url = await getImageURL(file);
  if (url) {
    console.log('업로드 성공:', url);
  }
};


const { _data, error: profileError } = await supabase.from('profiles').upsert({
  id: user.id,
  nick_name: formData.nick_name,
  website_url: formData.website_url,
  updated_at: new Date().toISOString()
});

upsert나 insert로 데이터를 덮어 씌우거나 삽입한다
테이블 컬럼 이름은 되도록 소문자 + 스네이크 케이스(nick_name)로 일관되게 쓰는 것이 좋고 프론트 단에서는 카멜케이스로 구분하는게 좋다

주의점

수퍼베이스 스토리지는 공개로 해 놓아야 하며

정책은 select, insert, update, delete 전부 만들어 주어야 한다

🔐 Signed URL

시간 제한이 있는 URL을 만들어서 정해진 시간 동안만 해당 파일에 접근할 수 있도록 하는 방식으로, 기업 환경에서는 대부분 public URL보다 보안에 강한 signed URL(= 인증된 URL)을 쓴다

const { data } = await supabase.storage
  .from('private')
  .createSignedUrl('report.pdf', 60); // 60초 유효

이렇게 하면, 60초 동안만 열리는 링크가 생성돼. 그 후엔 자동으로 접근 불가!

✅ 기업에서 signedUrl을 쓰는 이유

  • 유저별 접근 권한을 제어하고 싶을 때
  • 유료 콘텐츠나 민감한 자료를 보호해야 할 때
  • 외부에 링크 유출되는 상황을 방지하고 싶을 때

✅ 실제예시

상황쓰는 방식
쇼핑몰 상품 썸네일public URL (누구나 봐야 하니까)
직원용 월급명세서 PDFsigned URL (로그인한 직원만 열람 가능)
온라인 강의 영상signed URL (유료 사용자만 제한 시간 동안 재생 가능)

✅ public URL vs private URL (signed URL) 차이

구분Public URLSigned URL (Private URL)
🔓 접근누구나 접근 가능토큰/시간 제한된 인증된 사용자만 가능
🕒 만료 시간없음 (영구적으로 유효)있음 (예: 1분, 5분 후 만료)
🔐 보안없음 → 이미지 링크 퍼지면 누구나 열람 가능있음 → 링크 유출돼도 일정 시간 후 무효
🔧 사용 예썸네일, 프로필 이미지, 블로그 게시물 등민감 정보, 유료 콘텐츠, 내부 자료, 비공개 파일 등
📦 Supabase 함수getPublicUrl()createSignedUrl()

✅ 결론

  • 공개 이미지: getPublicUrl()로 public URL 쓰면 편하고 빠름
  • 보안이 필요한 파일: createSignedUrl()로 signed URL 써야 함

0개의 댓글