# 파일

```
POST   /storage/v1/bucket                    {"id":"avatars","public":true}
POST   /storage/v1/object/avatars/<이름>      본문: 파일 바이트
GET    /storage/v1/object/avatars/<이름>
DELETE /storage/v1/object/avatars/<이름>
```

## 공개 버킷과 비공개 버킷

`public: true`인 버킷의 파일은 누구나 받을 수 있고 CDN이 캐시해요.
그렇지 않은 버킷은 올린 사람만 받을 수 있고 캐시되지 않아요.

프로필 사진처럼 이미 공개된 것은 공개 버킷, 그 외에는 비공개가 기본이에요.

Supabase용으로 쓴 파일 규칙도 그대로 써요. `storage.foldername(name)`, `storage.filename(name)`, `storage.extension(name)`과 `storage.buckets`의 `name`·`owner` 열이 있어서, `(storage.foldername(name))[1] = auth.uid()::text` 같은 조건을 콘솔의 [접근 규칙] 화면, Supabase에서 옮기기, 또는 SQL 편집기·`run_sql`·마이그레이션의 `create policy … on storage.objects`로 넣으면 이 프로젝트의 이름으로 바뀌어 동작해요. SQL로 넣으면 나머지 SQL이 끝난 뒤 One Server가 그 규칙을 만들어요.

## S3로 쓰기

AWS SDK, aws CLI, rclone처럼 S3를 쓰는 도구로 같은 버킷을 다뤄요. REST로 올린 파일이 S3에 보이고, 그 반대도 같아요.

```
endpoint:        https://<ref>.<zone>/storage/v1/s3
region:          ap-northeast-2
forcePathStyle:  true
```

접근 키는 `create_s3_key`로 만들고(비밀 접근 키는 그 응답에만 있어요), `list_s3_keys`로 보고, `revoke_s3_key`로 폐기해요.
접근 키는 서비스 역할이라 접근 규칙(RLS)을 무시해요. 서버에만 두세요.

앱 사용자의 권한으로 쓰려면 세션 토큰 방식이에요: 접근 키 ID에 프로젝트 ref, 비밀 접근 키에 공개 키(`bw_publishable_…`), 세션 토큰에 사용자의 액세스 토큰을 넣어요.
그 사용자의 접근 규칙이 그대로 적용돼요.

ListBuckets, CreateBucket, DeleteBucket, HeadBucket, ListObjectsV2, GetObject(Range), HeadObject, PutObject, CopyObject, DeleteObject(s), 멀티파트 업로드를 지원해요.
버전 관리·ACL·태그·수명 주기는 NotImplemented로 답해요.

## Supabase에서 파일 옮기기

Supabase Storage도 S3로 열 수 있어서, rclone 하나로 버킷을 통째로 옮겨요.
경로가 그대로라 표에 저장된 파일 경로(`avatars/123.png`)는 바꾸지 않아도 돼요.

1. 여기에 같은 이름의 버킷을 먼저 만들어요(`create_bucket`). 공개 여부도 Supabase와 같게 골라요.
2. Supabase 대시보드의 Storage 설정에서 S3 접근 키를 만들고, 여기서는 `create_s3_key`로 만들어요.
3. rclone 설정(`~/.config/rclone/rclone.conf`)에 두 곳을 적어요.

```
[supabase]
type = s3
provider = Other
endpoint = https://<supabase-ref>.supabase.co/storage/v1/s3
region = <Supabase 프로젝트의 리전>
access_key_id = <Supabase 접근 키 ID>
secret_access_key = <Supabase 비밀 접근 키>

[oneend]
type = s3
provider = Other
endpoint = https://<ref>.<zone>/storage/v1/s3
region = ap-northeast-2
access_key_id = <create_s3_key의 접근 키 ID>
secret_access_key = <create_s3_key의 비밀 접근 키>
```

4. 버킷마다 복사해요. 다시 실행하면 바뀐 파일만 보내요.

```sh
rclone copy supabase:avatars oneend:avatars --progress
rclone check supabase:avatars oneend:avatars   # 빠진 파일이 없는지 확인
```

aws CLI만 있으면 내려받았다가 올려요.

```sh
aws s3 sync s3://avatars ./avatars --endpoint-url https://<supabase-ref>.supabase.co/storage/v1/s3
aws s3 sync ./avatars s3://avatars --endpoint-url https://<ref>.<zone>/storage/v1/s3
```

파일 하나의 최대 크기는 요금제를 따라요(무료 50MB, Pro 5GB). 공개 주소는 `https://<ref>.<zone>/storage/v1/object/public/<버킷>/<경로>`로 바뀌니, 표에 전체 주소를 저장했다면 그 주소만 고쳐요.

## 이어 올리기(TUS)

큰 파일은 TUS로 올리면 연결이 끊겨도 이어서 보낼 수 있어요. tus-js-client나 Uppy를 그대로 써요.

```
endpoint:  https://<ref>.<zone>/storage/v1/upload/resumable
headers:   Authorization: Bearer <key or access token>, x-upsert: true
metadata:  bucketName, objectName, contentType, cacheControl
```

하루 동안 이어 받지 않은 업로드는 만료되고 지워져요.
일반 업로드도 S3·TUS로 올리는 파일 하나도 요금제 한도(무료 50MB, Pro 5GB)를 따르고, 버킷 한도가 더 작으면 그걸 따라요.

## 도구로 파일 다루기

```
list_objects        버킷 안의 폴더 보기 (prefix, search)
upload_object       파일 하나 올리기 (2MB까지, 바이너리는 base64)
download_object     파일 하나 받기 (2MB까지)
delete_object       파일 하나 지우기
create_signed_url   비공개 파일을 정해진 시간 동안 받을 수 있는 주소 (최대 7일)
create_upload_url   큰 파일(이미지·영상)을 PUT으로 올릴 주소 (2시간, 한 파일)
update_bucket       버킷의 공개 여부, 파일 크기 한도, 받을 형식 바꾸기
empty_bucket        버킷 안의 파일 모두 지우기 (confirm에 버킷 이름)
delete_bucket       빈 버킷 지우기 (confirm에 버킷 이름)
```

더 큰 파일은 위의 `/storage/v1` 주소로 올리고 받아요.

이미지·영상은 내용을 도구 인자로 보내지 말고 `create_upload_url`로 주소를 받아
그 `url`에 PUT해요: `curl -X PUT -H 'content-type: video/mp4' --data-binary @./intro.mp4 '<url>'`.
한 번에 `max_bytes`(요금제의 파일 하나 한도: 무료 50MB, Pro 5GB. 버킷 한도가 더 작으면 그것)까지 올려요. 느린 회선에서 큰 파일은 끊긴 곳부터 잇는 이어 올리기(TUS)가 편해요.
REST로는 `POST /v1/projects/<ref>/buckets/<버킷>/upload-urls`이에요.

버킷을 만들 때(`create_bucket`)나 나중에(`update_bucket`) `file_size_limit`(예: `"5MB"`)와 `allowed_mime_types`(예: `["image/*"]`)로 받을 파일을 정해요.
버킷을 지우려면 먼저 `empty_bucket`으로 비우고, `delete_bucket`의 `confirm`에 버킷 이름을 그대로 넣어요. `empty_bucket`도 `confirm`에 버킷 이름을 받아요.
되돌릴 수 없는 도구는 모두 이렇게 `confirm`에 지울 것의 이름을 받아요: `empty_bucket`, `delete_queue`, `purge_queue`, `reset_branch`, `delete_branch`, `remove_function`, `remove_domain`, `delete_gpu_server`, `disable_extension`, `remove_user`(이메일 또는 id), `reset_database_password`·`set_spend_cap`(끌 때)·`rotate_signing_key`(`grace_hours: 0`일 때)는 프로젝트 이름. 틀리거나 비우면 무엇을 넣어야 하는지 알려 주며 거절해요.
REST로는 `PATCH /v1/projects/<ref>/buckets/<버킷>`, `POST /v1/projects/<ref>/buckets/<버킷>/empty`, `DELETE /v1/projects/<ref>/buckets/<버킷> {confirm}`이에요.

## 저장소 설정, 이전 버전, 배포에 연결한 버킷

```
get_storage_settings      파일 하나 최대 크기, 이미지 변환, S3 주소, 버킷마다 버전 보관
set_storage_settings      프로젝트 전체의 파일 크기 한도와 이미지 변환 (빠진 항목은 그대로)
set_s3_enabled            S3 주소 켜기·끄기 (키는 남아요)
set_bucket_versioning     버킷이 덮어쓴·지운 파일을 이전 버전으로 남길지, 며칠 둘지
list_object_versions      이전 버전 목록 (name을 주면 그 파일만)
restore_object_version    이전 버전을 지금 파일로 되돌리기
remove_object_version     이전 버전 영구 삭제 (confirm에 파일 이름)
list_bucket_connections   배포에 연결한 버킷과 넣는 환경 변수
connect_bucket            버킷 하나만 여는 S3 키를 배포의 <접두어>_BUCKET 등으로 넣기
disconnect_bucket         연결의 키를 폐기하고 변수 빼기 (confirm에 접두어)
list_storage_policies     파일 표(objects)·버킷 표(buckets)의 접근 규칙
create_storage_policy     콘솔의 틀(public-read, signed-in-read, own-folder …)이나 직접 쓴 규칙 만들기
remove_storage_policy     규칙 삭제 (confirm에 규칙 이름, 기본 규칙은 못 지워요)
```

파일 표의 규칙은 run_sql로 만들 수 없어서 `create_storage_policy`를 써요. 조건식의 `auth.uid()`는 `uid()`로 바뀌어요.
production에 넣는 연결과 끊기는 소유자·관리자만, 나머지는 개발자 이상이 할 수 있어요. 무료 요금제에는 없어요.

---

https://end.blackall.ai/docs/storage?lang=ko
