지원 드라이버
fs (File System)
로컬 파일 시스템
s3 (AWS S3)
AWS S3 호환 스토리지
fs 드라이버
로컬 파일 시스템에 파일을 저장합니다.기본 설정
복사
import { drivers, type SonamuConfig } from "sonamu";
export const config: SonamuConfig = {
server: {
storage: {
default: 'fs',
drivers: {
fs: drivers.fs({
location: './uploads', // 파일 저장 위치
urlBuilder: {
generateURL: (key) => `/uploads/${key}`,
}
}),
}
}
}
};
설정 옵션
- location
- urlBuilder
- visibility
파일 저장 위치권장: 프로젝트 루트 기준 상대 경로
복사
fs: drivers.fs({
location: './uploads', // 상대 경로
})
fs: drivers.fs({
location: '/var/app/uploads', // 절대 경로
})
URL 생성 규칙필수: fs 드라이버는 반드시 urlBuilder 설정 필요예시:
복사
fs: drivers.fs({
location: './uploads',
urlBuilder: {
generateURL: (key) => `/uploads/${key}`,
}
})
복사
// key: 'avatars/user-1.png'
// URL: '/uploads/avatars/user-1.png'
파일 접근 권한 (Unix 권한)기본값:
복사
fs: drivers.fs({
location: './uploads',
visibility: 'public', // 또는 'private'
urlBuilder: {
generateURL: (key) => `/uploads/${key}`,
}
})
public (0644)public: 모든 사용자 읽기 가능private: 소유자만 읽기 가능 (0600)
실전 예제
개발 환경
복사
export const config: SonamuConfig = {
server: {
storage: {
default: 'fs',
drivers: {
fs: drivers.fs({
location: './uploads',
urlBuilder: {
generateURL: (key) => `/uploads/${key}`,
}
}),
}
}
}
};
복사
await disk.put('avatars/user-1.png', buffer);
// 파일 위치: ./uploads/avatars/user-1.png
// URL: /uploads/avatars/user-1.png
정적 파일 서빙
Fastify에서 정적 파일을 서빙해야 합니다:복사
// sonamu.config.ts
import path from "path";
export const config: SonamuConfig = {
server: {
plugins: {
static: {
root: path.join(process.cwd(), 'uploads'),
prefix: '/uploads/',
}
},
storage: {
default: 'fs',
drivers: {
fs: drivers.fs({
location: './uploads',
urlBuilder: {
generateURL: (key) => `/uploads/${key}`,
}
}),
}
}
}
};
복사
http://localhost:3000/uploads/avatars/user-1.png
→ ./uploads/avatars/user-1.png 파일 서빙
s3 드라이버
AWS S3 또는 호환 스토리지에 파일을 저장합니다.기본 설정
복사
import { drivers, type SonamuConfig } from "sonamu";
export const config: SonamuConfig = {
server: {
storage: {
default: 's3',
drivers: {
s3: drivers.s3({
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
},
region: process.env.AWS_REGION!,
bucket: process.env.AWS_BUCKET!,
}),
}
}
}
};
필수 환경변수
복사
AWS_ACCESS_KEY_ID=AKIA...
AWS_SECRET_ACCESS_KEY=...
AWS_REGION=ap-northeast-2
AWS_BUCKET=my-app-uploads
설정 옵션
- credentials
- region
- bucket
- urlBuilder
AWS 자격증명획득 방법:
복사
s3: drivers.s3({
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
},
// ...
})
- AWS Console → IAM
- 사용자 생성
- S3 권한 부여 (
AmazonS3FullAccess) - Access Key 생성
AWS 리전주요 리전:
복사
s3: drivers.s3({
region: 'ap-northeast-2', // 서울
// ...
})
ap-northeast-2: 서울ap-northeast-1: 도쿄us-east-1: 버지니아 북부us-west-2: 오레곤
S3 버킷 이름버킷 생성:
복사
s3: drivers.s3({
bucket: 'my-app-uploads',
// ...
})
- AWS Console → S3
- “버킷 만들기” 클릭
- 고유한 이름 입력
- 리전 선택
- 퍼블릭 액세스 차단 설정
URL 생성 규칙 (선택)기본값: S3의 기본 URL 사용CloudFront 사용 시:
복사
s3: drivers.s3({
bucket: 'my-bucket',
urlBuilder: {
generateURL: (key) =>
`https://my-bucket.s3.amazonaws.com/${key}`,
}
})
복사
https://bucket.s3.region.amazonaws.com/key
복사
urlBuilder: {
generateURL: (key) =>
`https://d111111abcdef8.cloudfront.net/${key}`,
}
실전 예제
기본 S3 설정
복사
export const config: SonamuConfig = {
server: {
storage: {
default: 's3',
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',
}),
}
}
}
};
복사
await disk.put('avatars/user-1.png', buffer);
// S3 위치: s3://my-app-uploads/avatars/user-1.png
// URL: https://my-app-uploads.s3.ap-northeast-2.amazonaws.com/avatars/user-1.png
CloudFront CDN 연동
복사
export const config: SonamuConfig = {
server: {
storage: {
default: 's3',
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',
urlBuilder: {
generateURL: (key) =>
`https://${process.env.CLOUDFRONT_DOMAIN}/${key}`,
}
}),
}
}
}
};
복사
CLOUDFRONT_DOMAIN=d111111abcdef8.cloudfront.net
- 빠른 전송 속도 (엣지 로케이션)
- 대역폭 비용 절감
- 캐싱
퍼블릭 버킷 설정
S3 버킷을 퍼블릭으로 설정하려면:복사
// S3 버킷 정책 (AWS Console → S3 → 버킷 → 권한 → 버킷 정책)
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-app-uploads/*"
}
]
}
S3 호환 스토리지
Cloudflare R2
복사
export const config: SonamuConfig = {
server: {
storage: {
default: 'r2',
drivers: {
r2: drivers.s3({
credentials: {
accessKeyId: process.env.R2_ACCESS_KEY_ID!,
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,
},
endpoint: `https://${process.env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com`,
region: 'auto',
bucket: process.env.R2_BUCKET!,
urlBuilder: {
generateURL: (key) =>
`https://${process.env.R2_PUBLIC_DOMAIN}/${key}`,
}
}),
}
}
}
};
- 무료 egress (전송 비용 없음)
- S3 API 호환
- Cloudflare 네트워크
MinIO (Self-hosted)
복사
export const config: SonamuConfig = {
server: {
storage: {
default: 'minio',
drivers: {
minio: drivers.s3({
credentials: {
accessKeyId: process.env.MINIO_ACCESS_KEY!,
secretAccessKey: process.env.MINIO_SECRET_KEY!,
},
endpoint: 'http://localhost:9000',
region: 'us-east-1',
bucket: 'uploads',
forcePathStyle: true, // MinIO 필수
}),
}
}
}
};
드라이버 비교
| 특징 | fs | S3 |
|---|---|---|
| 비용 | 무료 (디스크 공간) | 유료 (저장 + 전송) |
| 확장성 | 제한적 (서버 디스크) | 무제한 |
| 속도 | 매우 빠름 | 빠름 (네트워크) |
| 백업 | 수동 | 자동 (버전 관리) |
| CDN | 별도 설정 필요 | CloudFront 연동 |
| 용도 | 개발, 소규모 | 프로덕션, 대규모 |
실전 전략
개발/프로덕션 분리
복사
const isDevelopment = process.env.NODE_ENV === 'development';
export const config: SonamuConfig = {
server: {
storage: {
default: isDevelopment ? 'fs' : 's3',
drivers: {
// 개발: 로컬
fs: drivers.fs({
location: './uploads',
urlBuilder: {
generateURL: (key) => `/uploads/${key}`,
}
}),
// 프로덕션: S3
s3: drivers.s3({
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
},
region: process.env.AWS_REGION!,
bucket: process.env.AWS_BUCKET!,
}),
}
}
}
};
다중 버킷 전략
복사
export const config: SonamuConfig = {
server: {
storage: {
default: 'private',
drivers: {
// 개인 문서 (private)
private: drivers.s3({
credentials: { /* ... */ },
region: 'ap-northeast-2',
bucket: 'my-app-private',
}),
// 공개 파일 (public)
public: drivers.s3({
credentials: { /* ... */ },
region: 'ap-northeast-2',
bucket: 'my-app-public',
}),
// 백업
backup: drivers.s3({
credentials: { /* ... */ },
region: 'ap-northeast-2',
bucket: 'my-app-backup',
}),
}
}
}
};
복사
// 프로필 사진 → public
await file.saveToDisk('avatars/user-1.png', 'public');
// 개인 문서 → private (기본)
await file.saveToDisk('documents/user-1/doc.pdf');
// 백업 → backup
await Sonamu.storage.use('backup').put('backup.json', data);
주의사항
드라이버 설정 시 주의사항:
-
환경변수 관리: 절대 하드코딩 금지
복사
// ❌ 하드코딩 credentials: { accessKeyId: 'AKIA...', secretAccessKey: '...', } // ✅ 환경변수 credentials: { accessKeyId: process.env.AWS_ACCESS_KEY_ID!, secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!, } -
fs urlBuilder 필수: fs 드라이버는 반드시 urlBuilder 필요
복사
// ❌ urlBuilder 없음 fs: drivers.fs({ location: './uploads', }) // ✅ urlBuilder 설정 fs: drivers.fs({ location: './uploads', urlBuilder: { generateURL: (key) => `/uploads/${key}`, } }) -
S3 권한 확인: IAM 사용자에게 S3 권한 부여
복사
// 최소 권한 { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:PutObject", "s3:GetObject", "s3:DeleteObject" ], "Resource": "arn:aws:s3:::my-bucket/*" } ] } -
버킷 이름 고유성: S3 버킷 이름은 전역적으로 고유해야 함
복사
// ❌ 이미 사용 중인 이름 bucket: 'uploads' // 에러! // ✅ 고유한 이름 bucket: 'my-app-uploads-prod-2024' -
리전 일치: bucket과 region이 일치해야 함
복사
// ❌ 리전 불일치 region: 'us-east-1', bucket: 'my-bucket' // ap-northeast-2에 생성됨 // ✅ 리전 확인 region: 'ap-northeast-2', bucket: 'my-bucket' // ap-northeast-2에 생성됨
