Supabase Storage는 이미지, PDF, 동영상 같은 파일을 저장하고 클라이언트에 제공하는 객체 스토리지 서비스다. S3 호환 API 위에 구축되어 있으며, Row Level Security(RLS)와 동일한 방식으로 접근 권한을 제어할 수 있다. 별도의 스토리지 인프라 없이 Supabase 프로젝트 하나로 DB와 파일을 함께 관리할 수 있다는 점이 가장 큰 장점이다.
Client
│
▼
Supabase Storage API
│
├── Bucket (논리적 컨테이너)
│ ├── public/ ← Public Bucket
│ └── private/ ← Private Bucket
│
└── Storage Policy (RLS 기반 접근 제어)
Bucket은 파일을 담는 최상위 컨테이너다. Public Bucket의 파일은 URL만 있으면 누구나 접근할 수 있고, Private Bucket의 파일은 Signed URL 또는 인증된 요청을 통해서만 접근 가능하다.
Bucket — S3의 버킷 개념과 동일. 프로젝트 내 여러 개 생성 가능.
대시보드 또는 클라이언트 코드로 생성할 수 있다.
import { createClient } from '@supabase/supabase-js'
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY)
// Public Bucket 생성
const { data, error } = await supabase.storage.createBucket('avatars', {
public: true,
})
// Upload
const { error: uploadError } = await supabase.storage
.from('avatars')
.upload('user-123/profile.png', file, {
contentType: 'image/png',
upsert: true, // 덮어쓰기 허용
})
// Download (Blob 반환)
const { data: blob, error: downloadError } = await supabase.storage
.from('avatars')
.download('user-123/profile.png')
// Delete
const { error: deleteError } = await supabase.storage
.from('avatars')
.remove(['user-123/profile.png'])
upsert: true — 동일 경로에 파일이 이미 존재할 때 오류 대신 덮어쓴다.
| 구분 | Public URL | Signed URL |
|---|---|---|
| 접근 권한 | 누구나 | 토큰 소지자만 |
| 만료 시간 | 없음 | 설정 가능 (초 단위) |
| 용도 | 프로필 이미지, 공개 에셋 | 계약서, 청구서, 비공개 파일 |
| Bucket 조건 | Public Bucket 필요 | Private Bucket 가능 |
// Public URL (즉시 반환, 네트워크 요청 없음)
const { data } = supabase.storage
.from('avatars')
.getPublicUrl('user-123/profile.png')
console.log(data.publicUrl)
// Signed URL (60초 유효)
const { data: signed, error } = await supabase.storage
.from('documents')
.createSignedUrl('invoices/2026-10.pdf', 60)
console.log(signed?.signedUrl)
Signed URL — 만료 시간이 지나면 해당 URL로 파일에 접근할 수 없다. 민감한 파일에 적합.
Storage Policy는 SQL로 작성하는 RLS 규칙이다. 인증된 사용자만 자신의 파일을 업로드할 수 있도록 제한하는 예시다.
-- 인증된 사용자만 자신의 폴더에 업로드 허용
CREATE POLICY "Users can upload their own files"
ON storage.objects
FOR INSERT
TO authenticated
WITH CHECK (
bucket_id = 'avatars' AND
(storage.foldername(name))[1] = auth.uid()::text
);
-- 모든 사용자가 avatars 버킷 파일 조회 가능
CREATE POLICY "Public read access"
ON storage.objects
FOR SELECT
USING (bucket_id = 'avatars');
storage.foldername — 파일 경로에서 폴더 세그먼트를 배열로 반환하는 Supabase 내장 함수.
Pro 플랜 이상에서 URL 파라미터로 이미지를 즉시 리사이즈할 수 있다.
const { data } = supabase.storage
.from('avatars')
.getPublicUrl('user-123/profile.png', {
transform: {
width: 200,
height: 200,
resize: 'cover',
},
})
별도의 이미지 처리 서버 없이 CDN 엣지에서 변환이 이루어지므로 응답 속도가 빠르다.