Vercel Blob 스토리지 활용
Vercel Blob 스토리지를 활용하여 파일 저장 및 관리하는 방법을 알아봅니다.
Vercel Blob 스토리지 활용
Introduction
Vercel Blob 스토리지는 서버리스 환경에서 파일을 저장하고 관리하는 솔루션입니다. Next.js 애플리케이션에서 이미지, 문서 등 다양한 파일을 효율적으로 관리하는 방법을 알아보겠습니다.
Environment
- Vercel 프로젝트 (Next.js 14+)
- Vercel Blob Storage
- Node.js 18+
- TypeScript
Problem
서버리스 환경에서 파일 저장 시 발생하는 문제:
// 이 코드는 Vercel에서 작동하지 않습니다
const fs = require('fs');
app.post('/upload', (req, res) => {
const file = req.files.file;
// 로컬 파일 시스템에 저장 불가
fs.writeFile(`./uploads/${file.name}`, file.data, (err) => {
if (err) {
return res.status(500).json({ error: 'Upload failed' });
}
res.json({ success: true });
});
});Analysis
Vercel Blob 스토리지의 특징:
- 서버리스 친화적: 로컬 파일 시스템 없이 파일 저장 가능
- 자동 확장: 파일 크기에 따라 자동으로 확장
- 글로벌 배포: 전 세계에서 빠른 접근 가능
- 비용 효율적: 사용한 만큼만 비용 발생
Solution
1. Vercel Blob 설치 및 설정
npm install @vercel/blob// .env.local
BLOB_READ_WRITE_TOKEN=your-blob-token2. 파일 업로드 구현
// app/api/upload/route.js
import { put } from '@vercel/blob';
import { NextResponse } from 'next/server';
export async function POST(request) {
const formData = await request.formData();
const file = formData.get('file');
if (!file) {
return NextResponse.json(
{ error: 'No file provided' },
{ status: 400 }
);
}
const blob = await put(file.name, file, {
access: 'public',
});
return NextResponse.json({
url: blob.url,
pathname: blob.pathname,
});
}3. 파일 목록 조회
// app/api/files/route.js
import { list } from '@vercel/blob';
import { NextResponse } from 'next/server';
export async function GET() {
const { blobs } = await list();
return NextResponse.json({
files: blobs.map(blob => ({
url: blob.url,
pathname: blob.pathname,
size: blob.size,
uploadedAt: blob.uploadedAt,
})),
});
}4. 파일 삭제
// app/api/delete/route.js
import { del } from '@vercel/blob';
import { NextResponse } from 'next/server';
export async function POST(request) {
const { url } = await request.json();
if (!url) {
return NextResponse.json(
{ error: 'URL required' },
{ status: 400 }
);
}
await del(url);
return NextResponse.json({ success: true });
}5. 클라이언트 컴포넌트에서 사용
// components/FileUpload.jsx
'use client';
import { useState } from 'react';
import { upload } from '@vercel/blob/client';
export default function FileUpload() {
const [uploading, setUploading] = useState(false);
const [fileUrl, setFileUrl] = useState(null);
const handleUpload = async (e) => {
const file = e.target.files[0];
if (!file) return;
setUploading(true);
try {
const newBlob = await upload(file.name, file, {
access: 'public',
handleUploadUrl: '/api/upload',
});
setFileUrl(newBlob.url);
} catch (error) {
console.error('Upload failed:', error);
} finally {
setUploading(false);
}
};
return (
{uploading && Uploading...
}
{fileUrl && (
File uploaded:
{fileUrl}
)}
);
}6. 이미지 최적화와 결합
// lib/image-storage.js
import { put } from '@vercel/blob';
import sharp from 'sharp';
export async function uploadOptimizedImage(file) {
const buffer = Buffer.from(await file.arrayBuffer());
// 이미지 최적화
const optimized = await sharp(buffer)
.resize(1920, 1080, {
fit: 'inside',
withoutEnlargement: true,
})
.webp({ quality: 80 })
.toBuffer();
// Blob에 업로드
const blob = await put(`images/${Date.now()}.webp`, optimized, {
access: 'public',
contentType: 'image/webp',
});
return blob.url;
}7. 메타데이터 관리
// lib/blob-metadata.js
import { list } from '@vercel/blob';
export async function getFilesWithMetadata(prefix = '') {
const { blobs } = await list({ prefix });
return blobs.map(blob => ({
...blob,
metadata: blob.customMetadata || {},
}));
}
export async function updateMetadata(url, metadata) {
// Vercel Blob은 메타데이터 업데이트를 지원하지 않으므로
// 별도의 데이터베이스에 메타데이터를 저장하는 방식 사용
// 예시: Prisma나 Redis에 메타데이터 저장
}8. 에러 핸들링
// lib/blob-utils.js
import { put, list, del } from '@vercel/blob';
export async function safeUpload(name, file, options = {}) {
try {
const blob = await put(name, file, {
access: 'public',
...options,
});
return { success: true, blob };
} catch (error) {
console.error('Upload failed:', error);
return { success: false, error: error.message };
}
}
export async function safeList(options = {}) {
try {
const result = await list(options);
return { success: true, ...result };
} catch (error) {
console.error('List failed:', error);
return { success: false, error: error.message };
}
}
export async function safeDelete(url) {
try {
await del(url);
return { success: true };
} catch (error) {
console.error('Delete failed:', error);
return { success: false, error: error.message };
}
}Lessons Learned
- 보안: 파일 업로드 시 적절한 검증과 보안 처리가 필요합니다.
- 비용 관리: Blob 스토리지 사용량을 모니터링하여 비용을 관리해야 합니다.
- 이미지 최적화: 업로드 시 이미지를 최적화하면 저장 공간과 대역폭을 절약할 수 있습니다.
- 백업 전략: 중요 파일은 백업 전략을 수립해야 합니다.
- CDN 활용: Blob 스토리지는 CDN을 통해 빠른 파일 전달을 제공합니다.
This blog does not accept any external sponsorships, affiliate marketing, or ad revenue.