누구나 접근할 수 있는 "인터넷 주소"
즉, 로그인하지 않아도 이미지나 파일에 접근할 수 있는 공개 링크
https://abc.supabase.co/storage/v1/object/public/images/mycat.jpg
이런 주소가 생성되면, 이 링크를 다른 사람한테 줘도, 그 사람은 바로 이미지를 볼 수 있음
Supabase의 Storage는 파일을 저장하는 "클라우드 폴더"
거기서 getPublicUrl()이라는 함수를 쓰면 →
해당 파일에 접근 가능한 공개 주소 (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;
};
Supabase의 Storage 기능을 이용해, 사용자가 업로드한 이미지 파일을 저장하고
해당 이미지의 공개 URL(publicUrl) 을 반환하는 유틸 함수를 만들었다.
파일명은 중복을 방지하기 위해 Date.now() 와 crypto.randomUUID() 를 조합해 고유하게 생성했고,
지정한 경로에 이미지를 업로드한 후 getPublicUrl() 을 통해 public URL을 받아오는 흐름이다.
업로드나 URL 생성 중 에러가 발생할 경우에는 null을 반환하도록 예외 처리를 했다.
성공적으로 업로드되면 data.publicUrl을 imageUrl로 받아 최종적으로 반환한다.
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 전부 만들어 주어야 한다
시간 제한이 있는 URL을 만들어서 정해진 시간 동안만 해당 파일에 접근할 수 있도록 하는 방식으로, 기업 환경에서는 대부분 public URL보다 보안에 강한 signed URL(= 인증된 URL)을 쓴다
const { data } = await supabase.storage
.from('private')
.createSignedUrl('report.pdf', 60); // 60초 유효
이렇게 하면, 60초 동안만 열리는 링크가 생성돼. 그 후엔 자동으로 접근 불가!
| 상황 | 쓰는 방식 |
|---|---|
| 쇼핑몰 상품 썸네일 | public URL (누구나 봐야 하니까) |
| 직원용 월급명세서 PDF | signed URL (로그인한 직원만 열람 가능) |
| 온라인 강의 영상 | signed URL (유료 사용자만 제한 시간 동안 재생 가능) |
| 구분 | Public URL | Signed URL (Private URL) |
|---|---|---|
| 🔓 접근 | 누구나 접근 가능 | 토큰/시간 제한된 인증된 사용자만 가능 |
| 🕒 만료 시간 | 없음 (영구적으로 유효) | 있음 (예: 1분, 5분 후 만료) |
| 🔐 보안 | 없음 → 이미지 링크 퍼지면 누구나 열람 가능 | 있음 → 링크 유출돼도 일정 시간 후 무효 |
| 🔧 사용 예 | 썸네일, 프로필 이미지, 블로그 게시물 등 | 민감 정보, 유료 콘텐츠, 내부 자료, 비공개 파일 등 |
| 📦 Supabase 함수 | getPublicUrl() | createSignedUrl() |