> ## Documentation Index
> Fetch the complete documentation index at: https://sonamu.cartanova.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# 스토리지

> 파일 저장소 드라이버 설정

export const FileItem = ({name, description, children}) => {
  const isLeaf = !children;
  const ext = getFileExtension(name);
  const extStyle = ext ? getExtensionStyle(ext) : null;
  return <div style={{
    marginLeft: "30px"
  }}>
      <div style={{
    display: "flex",
    alignItems: "center",
    gap: "4px"
  }}>
        <span style={{
    position: "relative",
    display: "inline-block"
  }}>
          <span className="file-icon">{isLeaf && !name.endsWith("/") ? "📄" : "📁"}</span>
          {ext && <span style={{
    position: "absolute",
    bottom: "2px",
    right: "0px",
    fontSize: "5px",
    fontWeight: "bold",
    padding: "1px",
    borderRadius: "2px",
    lineHeight: "1",
    minWidth: "8px",
    textAlign: "center",
    ...extStyle
  }}>
              {ext}
            </span>}
        </span>
        <code>{name}</code>
        {description && <span className="description"> - {description}</span>}
      </div>
      {children}
    </div>;
};

export const FileTree = ({children}) => {
  return <div className="file-tree" style={{
    margin: "20px",
    marginLeft: "-30px"
  }}>
      {children}
    </div>;
};

Sonamu는 파일 업로드와 저장을 위한 통합 스토리지 시스템을 제공합니다. 로컬 파일 시스템(fs)과 AWS S3를 지원하며, 환경에 따라 쉽게 전환할 수 있습니다.

## 기본 구조

```typescript theme={null}
import path from "path";
import { defineConfig } from "sonamu";
import { drivers } from "sonamu/storage";

export default defineConfig({
  server: {
    storage: {
      drivers: {
        fs: drivers.fs({
          /* ... */
        }),
        s3: drivers.s3({
          /* ... */
        }),
      },
    },
  },
  // ...
});
```

## drivers

사용할 스토리지 드라이버들을 정의합니다. `"fs"`(로컬 파일 시스템)와 `"s3"`(AWS S3) 두 종류를 지원합니다.

**타입**: `Record<DriverKey, () => DriverContract>` (`DriverKey = "fs" | "s3"`)

```typescript theme={null}
import { drivers } from "sonamu/storage";

export default defineConfig({
  server: {
    storage: {
      drivers: {
        fs: drivers.fs({
          /* ... */
        }), // 로컬 파일 시스템
        s3: drivers.s3({
          /* ... */
        }), // AWS S3
      },
    },
  },
});
```

파일 저장 시 `saveToDisk(diskName, key)` 호출에서 사용할 드라이버를 명시적으로 지정합니다. 환경별로 다른 드라이버를 사용하려면 환경 변수를 활용하여 코드 레벨에서 분기할 수 있습니다.

### fs 드라이버

로컬 파일 시스템에 파일을 저장합니다. 개발 환경에 적합합니다.

```typescript theme={null}
import path from "path";
import { drivers } from "sonamu/storage";

export default defineConfig({
  server: {
    storage: {
      drivers: {
        fs: drivers.fs({
          location: path.join(import.meta.dirname, "/../public/uploaded"),
          visibility: "public",
          urlBuilder: {
            generateURL(key) {
              return `/api/public/uploaded/${key}`;
            },
            generateSignedURL(key) {
              return `/api/public/uploaded/${key}`;
            },
          },
        }),
      },
    },
  },
});
```

#### location

파일이 저장될 디렉토리 경로입니다.

**타입**: `string` (필수)

```typescript theme={null}
drivers.fs({
  location: path.join(import.meta.dirname, "/../public/uploaded"),
  // ...
});
```

**경로 예시**:

<Card>
  <FileTree>
    <FileItem name="project-root/">
      <FileItem name="api/">
        <FileItem name="src/">
          <FileItem name="sonamu.config.ts" description="여기서 실행" />
        </FileItem>
      </FileItem>

      <FileItem name="public/">
        <FileItem name="uploaded/" description="파일 저장 위치">
          <FileItem name="profile-images/" />

          <FileItem name="documents/" />
        </FileItem>
      </FileItem>
    </FileItem>
  </FileTree>
</Card>

#### visibility

파일의 접근 권한을 설정합니다.

**타입**: `"public" | "private"`

* `"public"`: 누구나 URL로 접근 가능
* `"private"`: 인증된 사용자만 접근 가능

```typescript theme={null}
drivers.fs({
  location: path.join(import.meta.dirname, "/../public/uploaded"),
  visibility: "public", // 공개 파일
  // ...
});
```

#### urlBuilder

파일 URL을 생성하는 함수들을 정의합니다.

```typescript theme={null}
drivers.fs({
  // ...
  urlBuilder: {
    // 일반 URL 생성
    generateURL(key) {
      return `/api/public/uploaded/${key}`;
    },

    // 서명된 URL 생성 (임시 접근)
    generateSignedURL(key, expiresIn) {
      // fs에서는 보통 일반 URL과 동일
      return `/api/public/uploaded/${key}`;
    },
  },
});
```

**generateURL**: 파일의 공개 URL을 생성합니다.

* **key**: 파일의 고유 키 (예: `"profile-images/user-123.jpg"`)
* **반환**: 접근 가능한 URL

**generateSignedURL**: 임시로 접근 가능한 서명된 URL을 생성합니다.

* **key**: 파일의 고유 키
* **expiresIn**: 만료 시간 (초, 선택적)
* **반환**: 임시 URL

### s3 드라이버

AWS S3에 파일을 저장합니다. 프로덕션 환경에 적합합니다.

```typescript theme={null}
import { drivers } from "sonamu/storage";

export default defineConfig({
  server: {
    storage: {
      drivers: {
        s3: drivers.s3({
          credentials: {
            accessKeyId: process.env.AWS_ACCESS_KEY_ID ?? "",
            secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY ?? "",
          },
          region: "ap-northeast-2",
          bucket: "my-app-uploads",
          visibility: "private",
        }),
      },
    },
  },
});
```

#### credentials

AWS 인증 정보를 설정합니다.

**타입**: (필수)

```typescript theme={null}
credentials: {
  accessKeyId: string;
  secretAccessKey: string;
}
```

```typescript theme={null}
drivers.s3({
  credentials: {
    accessKeyId: process.env.AWS_ACCESS_KEY_ID ?? "",
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY ?? "",
  },
  // ...
});
```

<Warning>AWS 인증 정보는 반드시 환경 변수로 관리하세요. 코드에 직접 작성하지 마세요!</Warning>

**.env**:

```bash theme={null}
AWS_ACCESS_KEY_ID=your_access_key_here
AWS_SECRET_ACCESS_KEY=your_secret_key_here
```

#### region

S3 버킷이 위치한 AWS 리전입니다.

**타입**: `string` (필수)

```typescript theme={null}
drivers.s3({
  // ...
  region: "ap-northeast-2", // 서울 리전
});
```

**주요 리전**:

* `ap-northeast-2` - 서울
* `us-east-1` - 버지니아 북부
* `us-west-2` - 오레곤
* `eu-west-1` - 아일랜드
* `ap-southeast-1` - 싱가포르

#### bucket

파일을 저장할 S3 버킷 이름입니다.

**타입**: `string` (필수)

```typescript theme={null}
drivers.s3({
  // ...
  bucket: "my-app-uploads",
});
```

**환경별로 다른 버킷 사용**:

```typescript theme={null}
drivers.s3({
  // ...
  bucket: process.env.S3_BUCKET ?? "my-app-dev",
});
```

#### visibility

S3 객체의 ACL(Access Control List)을 설정합니다.

**타입**: `"public" | "private"`

```typescript theme={null}
drivers.s3({
  // ...
  visibility: "private", // 인증 필요
});
```

* `"public"`: 퍼블릭 읽기 허용 (`public-read` ACL)
* `"private"`: 프라이빗 (`private` ACL, 기본값)

## 실전 예시

### 개발 환경: fs만 사용

```typescript theme={null}
import path from "path";
import { defineConfig } from "sonamu";
import { drivers } from "sonamu/storage";

export default defineConfig({
  server: {
    storage: {
      drivers: {
        fs: drivers.fs({
          location: path.join(import.meta.dirname, "/../public/uploaded"),
          visibility: "public",
          urlBuilder: {
            generateURL(key) {
              return `/api/public/uploaded/${key}`;
            },
            generateSignedURL(key) {
              return `/api/public/uploaded/${key}`;
            },
          },
        }),
      },
    },
  },
  // ...
});
```

### fs + S3 (환경별 전환)

두 드라이버를 모두 등록하고, 코드에서 환경 변수를 기반으로 사용할 드라이버를 선택합니다.

```typescript theme={null}
import path from "path";
import { defineConfig } from "sonamu";
import { drivers } from "sonamu/storage";

export default defineConfig({
  server: {
    storage: {
      drivers: {
        // 개발: 로컬 파일 시스템
        fs: drivers.fs({
          location: path.join(import.meta.dirname, "/../public/uploaded"),
          visibility: "public",
          urlBuilder: {
            generateURL(key) {
              return `/api/public/uploaded/${key}`;
            },
            generateSignedURL(key) {
              return `/api/public/uploaded/${key}`;
            },
          },
        }),

        // 프로덕션: AWS S3
        s3: drivers.s3({
          credentials: {
            accessKeyId: process.env.AWS_ACCESS_KEY_ID ?? "",
            secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY ?? "",
          },
          region: process.env.S3_REGION ?? "ap-northeast-2",
          bucket: process.env.S3_BUCKET ?? "my-app-prod",
          visibility: "private",
        }),
      },
    },
  },
  // ...
});
```

**.env.development**:

```bash theme={null}
DRIVE_DISK=fs
```

**.env.production**:

```bash theme={null}
DRIVE_DISK=s3
S3_REGION=ap-northeast-2
S3_BUCKET=my-app-prod-uploads
AWS_ACCESS_KEY_ID=your_access_key_here
AWS_SECRET_ACCESS_KEY=your_secret_key_here
```

<Note>현재 `DriverKey`는 `"fs"`와 `"s3"` 두 가지만 지원합니다. 공개/비공개 파일을 별도 버킷에 나누어 저장하는 다중 버킷 구성은 현재 지원되지 않습니다.</Note>

## 파일 업로드 사용

스토리지 설정 후 `@upload` 데코레이터와 `BufferedFile.saveToDisk()`를 사용하여 파일을 업로드할 수 있습니다.

```typescript theme={null}
import { type DriverKey } from "sonamu/storage";
import { Sonamu } from "sonamu";
import { upload } from "sonamu/decorators";
import { BaseModelClass } from "sonamu";

export class FileModel extends BaseModelClass {
  @upload()
  static async upload() {
    const ctx = Sonamu.getContext();
    const file = ctx.bufferedFiles[0];
    if (!file) throw new Error("업로드된 파일이 없습니다.");

    // 환경 변수로 드라이버 선택
    const diskName: DriverKey = process.env.DRIVE_DISK === "s3" ? "s3" : "fs";
    const url = await file.saveToDisk(diskName, `uploads/${Date.now()}-${file.filename}`);

    return { url };
  }
}
```

→ [파일 업로드 가이드](/ko/api-development/file-upload/setup)

## S3 버킷 설정

S3를 사용하기 전에 AWS 콘솔에서 버킷을 생성하고 설정해야 합니다.

### 1. 버킷 생성

```bash theme={null}
# AWS CLI로 버킷 생성
aws s3 mb s3://my-app-uploads --region ap-northeast-2
```

### 2. CORS 설정

프론트엔드에서 직접 업로드하려면 CORS를 설정합니다.

**S3 콘솔 → 버킷 → 권한 → CORS 구성**:

```json theme={null}
[
  {
    "AllowedHeaders": ["*"],
    "AllowedMethods": ["GET", "PUT", "POST", "DELETE"],
    "AllowedOrigins": ["https://myapp.com"],
    "ExposeHeaders": ["ETag"]
  }
]
```

### 3. IAM 권한

Sonamu가 S3에 접근하려면 적절한 IAM 권한이 필요합니다.

**최소 권한 정책**:

```json theme={null}
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:PutObject", "s3:GetObject", "s3:DeleteObject", "s3:ListBucket"],
      "Resource": ["arn:aws:s3:::my-app-uploads", "arn:aws:s3:::my-app-uploads/*"]
    }
  ]
}
```

## 주의사항

### 1. 환경 변수 보안

```typescript theme={null}
// ❌ 나쁜 예: 코드에 인증 정보 직접 작성
drivers.s3({
  credentials: {
    accessKeyId: "AKIAXXXXXXXXXXXXXXXX",
    secretAccessKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCY...",
  },
});

// ✅ 좋은 예: 환경 변수 사용
drivers.s3({
  credentials: {
    accessKeyId: process.env.AWS_ACCESS_KEY_ID ?? "",
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY ?? "",
  },
});
```

### 2. visibility 선택

```typescript theme={null}
// 공개 파일 (프로필 이미지, 상품 이미지 등)
visibility: "public";

// 프라이빗 파일 (개인 문서, 영수증, 계약서 등)
visibility: "private";
```

### 3. 파일 경로 설계

```typescript theme={null}
// ✅ 좋은 예: 체계적인 경로 구조
const key = `users/${userId}/profile/${Date.now()}.jpg`;
const key = `documents/${year}/${month}/${documentId}.pdf`;

// ❌ 나쁜 예: 평평한 구조
const key = `${Date.now()}.jpg`;
```

## 다음 단계

스토리지 설정을 완료했다면:

* [file-upload](/ko/api-development/file-upload/setup) - API에서 파일 업로드 구현
* [cache](/ko/configuration/sonamu-config/cache) - 캐시 설정
* [advanced-features/storage](/ko/advanced-features/storage/storage-drivers) - 고급 스토리지 기능
