Skip to content

S3 호환성

AWS SDK를 사용하여 Amazon S3에서 사용하는 것과 동일한 코드로 IPFS Ninja의 파일을 업로드, 다운로드 및 관리할 수 있습니다.

Endpoint

https://s3.ipfs.ninja

인증 정보

S3 API는 인증에 IPFS Ninja API 키를 사용합니다. API 키가 access key와 secret key 모두의 역할을 합니다.

인증 정보 얻는 방법

  1. Dashboard > API Keys로 이동
  2. Create API key를 클릭하고 이름을 지정합니다 (예: "S3 access")
  3. 즉시 전체 키를 복사하세요 — 키는 한 번만 표시되며 나중에 다시 확인할 수 없습니다

키는 다음과 같은 형태입니다:

bws_628bba35e9e0079d9ff9c392b1b55a7b
├──────────┘└──────────────────────────┘
 prefix (12 chars)    rest of key

AWS 인증 정보 매핑

AWS 매개변수예시
accessKeyIdAPI 키의 처음 12자bws_628bba35
secretAccessKeyAPI 키 전체 (36자 모두)bws_628bba35e9e0079d9ff9c392b1b55a7b
region항상 us-east-1us-east-1

WARNING

전체 API 키는 생성 시 한 번만 표시됩니다. 분실한 경우 키를 삭제하고 API Keys 페이지에서 새 키를 생성하세요.

빠른 시작

javascript
import { S3Client, PutObjectCommand, GetObjectCommand } from "@aws-sdk/client-s3";

const s3 = new S3Client({
  endpoint: "https://s3.ipfs.ninja",
  credentials: {
    accessKeyId: "bws_628bba35",
    secretAccessKey: "bws_628bba35e9e0079d9ff9c392b1b55a7b"
  },
  region: "us-east-1",
  forcePathStyle: true
});

// Upload a file
const put = await s3.send(new PutObjectCommand({
  Bucket: "my-project",
  Key: "hello.json",
  Body: JSON.stringify({ hello: "IPFS" }),
  ContentType: "application/json"
}));

console.log("CID:", put.Metadata?.cid);
// CID: bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi

Bucket = 폴더

S3 bucket은 IPFS Ninja의 폴더에 매핑됩니다. bucket에 파일을 업로드하면 해당 폴더에 저장됩니다. bucket의 객체를 나열하면 해당 폴더의 파일이 표시됩니다.

S3 작업IPFS Ninja 대응
CreateBucket새 폴더 생성
ListBuckets폴더 목록 표시
DeleteBucket폴더와 내부의 모든 파일 삭제
PutObject (bucket으로)폴더에 파일 업로드
ListObjectsV2 (bucket에서)폴더의 파일 목록 표시
javascript
import { ListBucketsCommand, CreateBucketCommand, PutObjectCommand } from "@aws-sdk/client-s3";

// Create a bucket (= create a folder)
await s3.send(new CreateBucketCommand({ Bucket: "nft-metadata" }));

// Upload a file into the folder
await s3.send(new PutObjectCommand({
  Bucket: "nft-metadata",      // ← folder name
  Key: "token-42.json",        // ← filename within the folder
  Body: JSON.stringify({ name: "My NFT #42" })
}));

// List buckets (= list your folders)
const { Buckets } = await s3.send(new ListBucketsCommand({}));
console.log(Buckets);
// [{ Name: "nft-metadata", CreationDate: "2026-04-13T..." }]

TIP

S3 API를 통해 생성한 폴더는 Dashboard에서 볼 수 있는 폴더와 동일합니다. S3 API, REST API 또는 웹 인터페이스 어디서든 파일을 정리할 수 있습니다 — 모두 같은 폴더 시스템을 공유합니다.

INFO

Amazon S3와 달리 IPFS Ninja의 폴더는 기본적으로 플랫합니다. 중첩된 구조를 만들려면 REST API의 폴더 endpoint에서 parentFolderId를 사용하세요. S3 API에서는 키 접두사(예: images/photo.png)를 사용하여 폴더 내 파일을 정리할 수 있습니다.

Bucket 이름은 전역적으로 고유합니다

Bucket 이름은 모든 고객에 걸친 전역 네임스페이스에 존재하며, AWS S3의 규칙을 그대로 따릅니다. 이는 다음을 의미합니다:

  • 특정 이름으로 bucket을 처음 생성한 사용자가 그 이름을 전역적으로 선점합니다.
  • 어느 계정에서든 같은 이름으로 이후에 CreateBucket을 호출하면 BucketAlreadyExists (409)가 반환됩니다.
  • 자신의 bucket을 다시 생성하려고 하면 BucketAlreadyOwnedByYou (409)가 반환됩니다.
  • 대시보드의 폴더 이름은 계정별로 관리되며 여전히 무엇이든 될 수 있습니다 — 전역 네임스페이스를 거치는 것은 S3에서 보이는 bucket 이름뿐입니다.

원하는 이름이 이미 사용 중이라면, Amazon S3에서 사용하는 것과 같은 방식으로 더 구체적인 이름(myapp-photos-2026, acme-nft-metadata)을 선택하세요.

지원되는 작업

PutObject

파일을 IPFS에 업로드합니다. 파일은 핀 고정되고, 보안 검사를 거치며, CID가 ETagx-amz-meta-cid 헤더로 반환됩니다.

일반 파일 대신 CAR 파일을 가져오려면 x-amz-meta-import: car 메타데이터 헤더를 추가하세요. 자세한 내용은 CAR 가져오기를 참조하세요.

javascript
import { PutObjectCommand } from "@aws-sdk/client-s3";
import fs from "fs";

const result = await s3.send(new PutObjectCommand({
  Bucket: "my-project",
  Key: "photo.png",
  Body: fs.readFileSync("photo.png"),
  ContentType: "image/png"
}));

console.log("CID:", result.ETag);
bash
# curl equivalent
curl -X PUT "https://s3.ipfs.ninja/my-project/photo.png" \
  --data-binary @photo.png \
  -H "Content-Type: image/png" \
  --aws-sigv4 "aws:amz:us-east-1:s3" \
  --user "bws_628bba35:bws_628bba35e9e0079d9ff9c392b1b55a7b"

GetObject

키(파일명) 또는 CID로 파일을 다운로드합니다.

javascript
import { GetObjectCommand } from "@aws-sdk/client-s3";

const result = await s3.send(new GetObjectCommand({
  Bucket: "my-project",
  Key: "photo.png"
}));

const body = await result.Body.transformToByteArray();
console.log("Size:", body.length);
console.log("CID:", result.Metadata?.cid);

HeadObject

콘텐츠를 다운로드하지 않고 파일 메타데이터를 가져옵니다.

javascript
import { HeadObjectCommand } from "@aws-sdk/client-s3";

const head = await s3.send(new HeadObjectCommand({
  Bucket: "my-project",
  Key: "photo.png"
}));

console.log("Size:", head.ContentLength);
console.log("Type:", head.ContentType);
console.log("CID:", head.Metadata?.cid);

DeleteObject

파일의 IPFS 핀을 해제하고 계정에서 삭제합니다.

javascript
import { DeleteObjectCommand } from "@aws-sdk/client-s3";

await s3.send(new DeleteObjectCommand({
  Bucket: "my-project",
  Key: "photo.png"
}));

ListObjectsV2

선택적 접두사 필터링 및 페이지네이션으로 bucket의 파일을 나열합니다.

javascript
import { ListObjectsV2Command } from "@aws-sdk/client-s3";

const list = await s3.send(new ListObjectsV2Command({
  Bucket: "my-project",
  Prefix: "images/",
  MaxKeys: 100
}));

for (const obj of list.Contents ?? []) {
  console.log(obj.Key, obj.Size, obj.ETag); // ETag = CID
}

Multipart Upload

멀티파트 업로드를 사용하여 대용량 파일(최대 5 GB)을 업로드합니다. AWS SDK가 자동으로 처리합니다:

javascript
import { Upload } from "@aws-sdk/lib-storage";
import fs from "fs";

const upload = new Upload({
  client: s3,
  params: {
    Bucket: "my-project",
    Key: "large-dataset.tar.gz",
    Body: fs.createReadStream("large-dataset.tar.gz"),
    ContentType: "application/gzip"
  },
  partSize: 10 * 1024 * 1024, // 10 MB per part
});

upload.on("httpUploadProgress", (progress) => {
  console.log(`Uploaded ${progress.loaded} of ${progress.total} bytes`);
});

const result = await upload.done();
console.log("CID:", result.ETag);

또는 파트를 수동으로 제어:

javascript
import {
  CreateMultipartUploadCommand,
  UploadPartCommand,
  CompleteMultipartUploadCommand
} from "@aws-sdk/client-s3";

// 1. Start
const { UploadId } = await s3.send(new CreateMultipartUploadCommand({
  Bucket: "my-project",
  Key: "big-file.bin"
}));

// 2. Upload parts
const part1 = await s3.send(new UploadPartCommand({
  Bucket: "my-project",
  Key: "big-file.bin",
  UploadId,
  PartNumber: 1,
  Body: chunk1
}));

// 3. Complete
const result = await s3.send(new CompleteMultipartUploadCommand({
  Bucket: "my-project",
  Key: "big-file.bin",
  UploadId,
  MultipartUpload: {
    Parts: [{ PartNumber: 1, ETag: part1.ETag }]
  }
}));

Python 예제

python
import boto3

s3 = boto3.client(
    "s3",
    endpoint_url="https://s3.ipfs.ninja",
    aws_access_key_id="bws_628bba35",
    aws_secret_access_key="bws_628bba35e9e0079d9ff9c392b1b55a7b",
    region_name="us-east-1"
)

# Upload
s3.put_object(
    Bucket="my-project",
    Key="data.json",
    Body=b'{"hello": "IPFS"}',
    ContentType="application/json"
)

# List files
response = s3.list_objects_v2(Bucket="my-project")
for obj in response.get("Contents", []):
    print(obj["Key"], obj["Size"])

# Download
result = s3.get_object(Bucket="my-project", Key="data.json")
print(result["Body"].read())

Go 예제

go
package main

import (
    "context"
    "fmt"
    "strings"

    "github.com/aws/aws-sdk-go-v2/aws"
    "github.com/aws/aws-sdk-go-v2/credentials"
    "github.com/aws/aws-sdk-go-v2/service/s3"
)

func main() {
    client := s3.New(s3.Options{
        BaseEndpoint: aws.String("https://s3.ipfs.ninja"),
        Region:       "us-east-1",
        Credentials:  credentials.NewStaticCredentialsProvider("bws_628bba35", "bws_628bba35e9e0...", ""),
        UsePathStyle: true,
    })

    _, err := client.PutObject(context.TODO(), &s3.PutObjectInput{
        Bucket:      aws.String("my-project"),
        Key:         aws.String("hello.txt"),
        Body:        strings.NewReader("Hello, IPFS!"),
        ContentType: aws.String("text/plain"),
    })
    if err != nil {
        panic(err)
    }
    fmt.Println("Uploaded!")
}

Configure CORS (browser SDK access)

브라우저 JavaScript(SPA, 지갑 앱, 대시보드 도구)에서 직접 S3 API를 호출하는 경우, 먼저 bucket에 CORS를 구성해야 합니다. 그렇지 않으면 브라우저가 preflight를 차단하고 업로드가 No 'Access-Control-Allow-Origin' header is present 오류로 실패합니다.

AWS S3와 동일한 형태입니다 — PutBucketCors / GetBucketCors / DeleteBucketCors 서브리소스:

PutBucketCors

javascript
import { S3Client, PutBucketCorsCommand } from "@aws-sdk/client-s3";

await s3.send(new PutBucketCorsCommand({
  Bucket: "my-bucket",
  CORSConfiguration: {
    CORSRules: [{
      AllowedOrigins: ["http://localhost:3000", "https://myapp.com"],
      AllowedMethods: ["GET", "HEAD", "PUT", "POST", "DELETE"],
      AllowedHeaders: ["*"],
      ExposeHeaders: ["ETag", "x-amz-meta-cid", "x-amz-request-id"],
      MaxAgeSeconds: 3600,
    }],
  },
}));

GetBucketCors

javascript
import { GetBucketCorsCommand } from "@aws-sdk/client-s3";

const { CORSRules } = await s3.send(new GetBucketCorsCommand({ Bucket: "my-bucket" }));
console.log(CORSRules);

DeleteBucketCors

javascript
import { DeleteBucketCorsCommand } from "@aws-sdk/client-s3";

await s3.send(new DeleteBucketCorsCommand({ Bucket: "my-bucket" }));

상한 및 기본값

  • bucket당 최대 5개 규칙 (스펙은 100개까지 허용하지만, 브라우저 preflight 응답을 간결하게 유지하기 위해 5개로 제한).
  • 직렬화된 전체 구성은 64 KB로 제한.
  • CORS 구성이 설정되어 있지 않으면 브라우저 preflight는 거부됩니다 — AWS S3의 기본 동작과 동일합니다. 실제로 요청을 보내는 오리진에 대해 명시적으로 규칙을 구성하세요.

실전 보안 참고 사항

CORS는 브라우저 측 편의 계층일 뿐, 보안 경계가 아닙니다. 모든 S3 API 호출은 여전히 API 키로 계산된 유효한 SigV4 서명을 필요로 합니다 — 관대한 CORS 구성이 있다고 해서 그 자격 증명 없이 누구나 bucket을 사용할 수 있게 되는 것은 아닙니다. CORS가 실제로 막는 것은: 의도하지 않은 오리진(예: dev.myapp.com의 오래된 앱 사본)이 브라우저 컨텍스트에서 서명된 요청을 보내는 것입니다.

대시보드 대안

파일 페이지에는 각 폴더의 작업 메뉴에 S3 CORS 항목이 있습니다. 동일한 기본 저장소이므로, PutBucketCors 코드를 작성하고 싶지 않다면 여기서 구성하세요.

Amazon S3와의 차이점

기능Amazon S3IPFS Ninja S3
스토리지 모델변경 가능한 객체콘텐츠 주소 지정(불변 CID)
덮어쓰기 동작객체를 제자리에서 교체새 CID 생성, 이전 CID는 여전히 접근 가능
Versioning지원미지원 (CID를 사용하여 버전 관리)
서버 측 암호화지원미지원 (콘텐츠가 IPFS에 있음)
수명 주기 정책지원미지원
Bucket 정책 / ACL지원gateway 액세스 모드 사용
Presigned URL지원서명된 업로드 토큰 사용
최대 객체 크기5 TB5 GB (multipart), 100 MB (single PUT)
리전다중 리전us-east-1만 지원
ETagMD5 해시IPFS CID
추가 헤더표준 S3x-amz-meta-cid (IPFS CID)
CID 형식해당 없음새 업로드는 최신 CIDv1(bafy…); 레거시 Qm…도 입력값으로 계속 유효
Bucket 네임스페이스전역 (AWS 전체)전역 (모든 IPFS Ninja 계정에 걸쳐) — 동일한 규칙
CORSPutBucketCors 지원PutBucketCors 지원 (5개 규칙 상한)

Amazon S3에서 마이그레이션

S3 클라이언트 설정을 교체하세요:

diff
 const s3 = new S3Client({
+  endpoint: "https://s3.ipfs.ninja",
   credentials: {
-    accessKeyId: "AKIA...",
-    secretAccessKey: "wJalrX..."
+    accessKeyId: "bws_628bba35",
+    secretAccessKey: "bws_628bba35e9e0..."
   },
   region: "us-east-1",
+  forcePathStyle: true
 });

기존의 PutObject, GetObject, ListObjectsV2, DeleteObject 호출은 변경 없이 그대로 작동합니다.

Filebase에서 마이그레이션

endpoint URL을 교체하세요:

diff
 const s3 = new S3Client({
-  endpoint: "https://s3.filebase.com",
+  endpoint: "https://s3.ipfs.ninja",
   credentials: {
-    accessKeyId: "FILEBASE_KEY",
-    secretAccessKey: "FILEBASE_SECRET"
+    accessKeyId: "bws_628bba35",
+    secretAccessKey: "bws_628bba35e9e0..."
   },
   region: "us-east-1",
   forcePathStyle: true
 });