Vercel에서 서버리스 함수 메모리 설정
Vercel 서버리스 함수의 메모리 설정을 최적화하여 성능을 향상시키는 방법을 알아봅니다.
Vercel에서 서버리스 함수 메모리 설정
Introduction
Vercel 서버리스 함수의 메모리 설정은 함수의 성능과 비용에 직접적인 영향을 미칩니다. 이 글에서는 메모리 설정을 최적화하는 방법을 알아보겠습니다.
Environment
- Vercel 프로젝트 (Next.js 14+)
- Vercel Pro 플랜
- Node.js 18+
- PostgreSQL
Problem
메모리 부족으로 인한 함수 실패:
Error: JavaScript heap out of memory
FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memoryAnalysis
Vercel 함수 메모리 옵션:
- 128MB: 기본값, 경량 함수용
- 256MB: 중간 규모 함수
- 512MB: 무거운 처리용
- 1024MB: 대규모 데이터 처리 (Pro 이상)
- 3008MB: 최대 메모리 (Enterprise)
메모리와 CPU의 관계:
- 메모리가 증가하면 CPU도 비례하여 증가
- 더 많은 메모리는 더 빠른 실행 시간 보장
Solution
1. vercel.json에서 메모리 설정
{
"functions": {
"app/api/light/**/*.js": {
"memory": 128,
"maxDuration": 10
},
"app/api/medium/**/*.js": {
"memory": 256,
"maxDuration": 30
},
"app/api/heavy/**/*.js": {
"memory": 512,
"maxDuration": 60
}
}
}2. 함수별 메모리 설정
// app/api/heavy-process/route.js
export const runtime = 'nodejs';
export const maxDuration = 60;
// Vercel 설정에서 메모리를 별도로 설정해야 함
// 이 파일에서는 JavaScript에서 직접 설정 불가
export async function POST(request) {
const data = await request.json();
// 무거운 처리 로직
const result = await processLargeDataset(data);
return Response.json(result);
}3. 메모리 사용량 모니터링
// lib/memory-monitor.js
export function getMemoryUsage() {
const usage = process.memoryUsage();
return {
rss: `${Math.round(usage.rss / 1024 / 1024)} MB`,
heapTotal: `${Math.round(usage.heapTotal / 1024 / 1024)} MB`,
heapUsed: `${Math.round(usage.heapUsed / 1024 / 1024)} MB`,
external: `${Math.round(usage.external / 1024 / 1024)} MB`,
};
}
// 함수 실행 전후 메모리 측정
export function withMemoryMonitoring(fn) {
return async function(...args) {
const before = getMemoryUsage();
console.log('Memory before:', before);
const result = await fn(...args);
const after = getMemoryUsage();
console.log('Memory after:', after);
return result;
};
}4. 메모리 최적화 기법
// 메모리 효율적인 데이터 처리
async function processLargeDataset(data) {
const chunkSize = 1000;
const results = [];
// 청크 단위로 처리
for (let i = 0; i < data.length; i += chunkSize) {
const chunk = data.slice(i, i + chunkSize);
const processed = await processChunk(chunk);
results.push(...processed);
// 가비지 컬렉션 유도
if (global.gc) {
global.gc();
}
}
return results;
}
// 스트리밍 처리
async function processStream(stream) {
const chunks = [];
for await (const chunk of stream) {
chunks.push(chunk);
// 청크가 너무 커지면 처리
if (chunks.length > 100) {
await processChunks(chunks.splice(0, 50));
}
}
// 남은 청크 처리
if (chunks.length > 0) {
await processChunks(chunks);
}
}5. 캐싱을 통한 메모리 절약
// lib/memoize.js
const cache = new Map();
export function memoize(fn, ttl = 60000) {
return async function(...args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
const cached = cache.get(key);
if (Date.now() - cached.timestamp < ttl) {
return cached.value;
}
cache.delete(key);
}
const result = await fn(...args);
cache.set(key, {
value: result,
timestamp: Date.now()
});
return result;
};
}6. 환경 변수 설정
# .env.local
NODE_OPTIONS="--max-old-space-size=4096"7. 빌드 시 메모리 최적화
// package.json
{
"scripts": {
"build": "NODE_OPTIONS='--max-old-space-size=4096' next build"
}
}Lessons Learned
- 메모리 프로파일링: 함수의 메모리 사용량을 프로파일링하여 적절한 메모리 크기를 선택해야 합니다.
- 청크 처리: 대용량 데이터는 청크 단위로 나누어 처리하면 메모리 사용을 줄일 수 있습니다.
- 캐싱 활용: 자주 사용되는 데이터를 캐싱하면 반복적인 계산을 줄일 수 있습니다.
- 가비지 컬렉션: 주기적으로 가비지 컬렉션을 유도하면 메모리 사용을 최적화할 수 있습니다.
- 비용 고려: 더 많은 메모리는 더 높은 비용을 발생시키므로 적절한 균형이 필요합니다.
This blog does not accept any external sponsorships, affiliate marketing, or ad revenue.