예제
이 페이지는 기본 작업부터 고급 기술까지 Sharp의 다양한 사용 예제를 제공합니다.
기본 예제
이미지 크기 조정
javascript
import sharp from 'sharp';
// 고정 크기로 조정
await sharp('input.jpg')
.resize(300, 200)
.toFile('resized.jpg');
// 종횡비 유지
await sharp('input.jpg')
.resize(300, null)
.toFile('resized.jpg');
// 다른 적합 모드 사용
await sharp('input.jpg')
.resize(300, 200, {
fit: 'cover', // 잘라서 맞추기
position: 'center' // 중앙 자르기
})
.toFile('cover.jpg');형식 변환
javascript
// JPEG를 PNG로 변환
await sharp('input.jpg')
.png()
.toFile('output.png');
// PNG를 WebP로 변환
await sharp('input.png')
.webp({ quality: 80 })
.toFile('output.webp');
// AVIF로 변환
await sharp('input.jpg')
.avif({ quality: 80 })
.toFile('output.avif');썸네일 생성
javascript
// 정사각형 썸네일 생성
await sharp('input.jpg')
.resize(150, 150, { fit: 'cover' })
.jpeg({ quality: 90 })
.toFile('thumbnail.jpg');
// 다양한 크기의 썸네일 생성
const sizes = [150, 300, 600];
const promises = sizes.map(size =>
sharp('input.jpg')
.resize(size, size, { fit: 'cover' })
.jpeg({ quality: 85 })
.toFile(`thumbnail-${size}.jpg`)
);
await Promise.all(promises);고급 예제
이미지 합성
javascript
// 이미지에 워터마크 추가
await sharp('input.jpg')
.composite([{
input: 'watermark.png',
top: 10,
left: 10
}])
.jpeg()
.toFile('with-watermark.jpg');
// 이미지 그리드 생성
const grid = await sharp({
create: {
width: 600,
height: 400,
channels: 4,
background: { r: 255, g: 255, b: 255, alpha: 1 }
}
})
.composite([
{ input: 'image1.jpg', top: 0, left: 0 },
{ input: 'image2.jpg', top: 0, left: 300 },
{ input: 'image3.jpg', top: 200, left: 0 },
{ input: 'image4.jpg', top: 200, left: 300 }
])
.jpeg()
.toFile('grid.jpg');필터 효과
javascript
// 여러 필터 적용
await sharp('input.jpg')
.blur(3) // 블러
.sharpen() // 선명화
.modulate({ // 색상 조정
brightness: 1.1,
saturation: 0.8
})
.jpeg({ quality: 85 })
.toFile('filtered.jpg');
// 빈티지 효과 생성
await sharp('input.jpg')
.modulate({
brightness: 0.9,
saturation: 0.7,
hue: 30
})
.tint({ r: 255, g: 200, b: 150 })
.jpeg({ quality: 80 })
.toFile('vintage.jpg');일괄 처리
javascript
import fs from 'fs';
import path from 'path';
async function processDirectory(inputDir, outputDir) {
const files = fs.readdirSync(inputDir);
const imageFiles = files.filter(file =>
/\.(jpg|jpeg|png|webp)$/i.test(file)
);
const promises = imageFiles.map(async file => {
const inputPath = path.join(inputDir, file);
const outputPath = path.join(outputDir, `processed-${file}`);
await sharp(inputPath)
.resize(800, 600, { fit: 'inside' })
.jpeg({ quality: 80 })
.toFile(outputPath);
console.log(`처리 완료: ${file}`);
});
await Promise.all(promises);
console.log('모든 파일 처리 완료!');
}
processDirectory('./input', './output');반응형 이미지
javascript
// 반응형 이미지 생성
const sizes = [
{ width: 320, suffix: 'sm' },
{ width: 640, suffix: 'md' },
{ width: 1024, suffix: 'lg' },
{ width: 1920, suffix: 'xl' }
];
const formats = ['jpeg', 'webp', 'avif'];
async function generateResponsiveImages(inputFile) {
const promises = [];
for (const size of sizes) {
for (const format of formats) {
const outputFile = `output-${size.suffix}.${format}`;
let pipeline = sharp(inputFile)
.resize(size.width, null, { fit: 'inside' });
switch (format) {
case 'jpeg':
pipeline = pipeline.jpeg({ quality: 80 });
break;
case 'webp':
pipeline = pipeline.webp({ quality: 80 });
break;
case 'avif':
pipeline = pipeline.avif({ quality: 80 });
break;
}
promises.push(pipeline.toFile(outputFile));
}
}
await Promise.all(promises);
console.log('반응형 이미지 생성 완료!');
}
generateResponsiveImages('input.jpg');성능 최적화 예제
스트림 처리
javascript
import fs from 'fs';
// 대용량 파일 처리
const pipeline = sharp()
.resize(800, 600)
.jpeg({ quality: 80 });
fs.createReadStream('large-input.jpg')
.pipe(pipeline)
.pipe(fs.createWriteStream('output.jpg'));
// 여러 파일 처리
const processFile = (inputFile, outputFile) => {
return new Promise((resolve, reject) => {
sharp(inputFile)
.resize(300, 200)
.jpeg({ quality: 80 })
.pipe(fs.createWriteStream(outputFile))
.on('finish', resolve)
.on('error', reject);
});
};
const files = ['file1.jpg', 'file2.jpg', 'file3.jpg'];
const promises = files.map((file, index) =>
processFile(file, `output-${index}.jpg`)
);
await Promise.all(promises);메모리 최적화
javascript
// 작은 파일에 Buffer 사용
const buffer = await sharp('input.jpg')
.resize(300, 200)
.jpeg({ quality: 80 })
.toBuffer();
// 대용량 파일에 스트림 사용
const stream = sharp('large-input.jpg')
.resize(800, 600)
.jpeg({ quality: 80 });
// 청크 단위 처리
const chunkSize = 1024 * 1024; // 1MB
const chunks = [];
stream.on('data', chunk => {
chunks.push(chunk);
});
stream.on('end', () => {
const buffer = Buffer.concat(chunks);
fs.writeFileSync('output.jpg', buffer);
});동시성 제어
javascript
// 동시성 수 제한
async function processWithConcurrency(files, concurrency = 3) {
const results = [];
for (let i = 0; i < files.length; i += concurrency) {
const batch = files.slice(i, i + concurrency);
const batchPromises = batch.map(file =>
sharp(file)
.resize(300, 200)
.jpeg({ quality: 80 })
.toFile(`processed-${file}`)
);
const batchResults = await Promise.all(batchPromises);
results.push(...batchResults);
}
return results;
}
const files = ['file1.jpg', 'file2.jpg', 'file3.jpg', 'file4.jpg'];
await processWithConcurrency(files, 2);유틸리티 예제
이미지 정보 추출
javascript
async function getImageInfo(file) {
const metadata = await sharp(file).metadata();
const stats = await sharp(file).stats();
return {
filename: file,
format: metadata.format,
width: metadata.width,
height: metadata.height,
size: metadata.size,
channels: metadata.channels,
isOpaque: stats.isOpaque,
dominantColor: stats.dominant
};
}
const info = await getImageInfo('input.jpg');
console.log('이미지 정보:', info);이미지 비교
javascript
async function compareImages(file1, file2) {
const [stats1, stats2] = await Promise.all([
sharp(file1).stats(),
sharp(file2).stats()
]);
return {
file1: stats1,
file2: stats2,
dominantDiff: {
r: Math.abs(stats1.dominant.r - stats2.dominant.r),
g: Math.abs(stats1.dominant.g - stats2.dominant.g),
b: Math.abs(stats1.dominant.b - stats2.dominant.b)
}
};
}
const comparison = await compareImages('image1.jpg', 'image2.jpg');
console.log('이미지 비교 결과:', comparison);이미지 검증
javascript
async function validateImage(file) {
try {
const metadata = await sharp(file).metadata();
const validation = {
isValid: true,
format: metadata.format,
width: metadata.width,
height: metadata.height,
size: metadata.size,
errors: []
};
// 크기 확인
if (metadata.width > 5000 || metadata.height > 5000) {
validation.errors.push('이미지 크기가 너무 큼');
}
// 파일 크기 확인
if (metadata.size > 10 * 1024 * 1024) { // 10MB
validation.errors.push('파일 크기가 너무 큼');
}
// 형식 확인
const allowedFormats = ['jpeg', 'png', 'webp'];
if (!allowedFormats.includes(metadata.format)) {
validation.errors.push('지원되지 않는 형식');
}
if (validation.errors.length > 0) {
validation.isValid = false;
}
return validation;
} catch (error) {
return {
isValid: false,
errors: [error.message]
};
}
}
const validation = await validateImage('input.jpg');
console.log('검증 결과:', validation);전체 애플리케이션 예제
javascript
import sharp from 'sharp';
import fs from 'fs';
import path from 'path';
class ImageProcessor {
constructor(options = {}) {
this.options = {
quality: 80,
maxWidth: 1920,
maxHeight: 1080,
...options
};
}
async processImage(inputPath, outputPath, options = {}) {
try {
const metadata = await sharp(inputPath).metadata();
// 크기 조정 계산
const { width, height } = this.calculateDimensions(
metadata.width,
metadata.height,
options
);
// 이미지 처리
await sharp(inputPath)
.resize(width, height, { fit: 'inside' })
.jpeg({ quality: this.options.quality })
.toFile(outputPath);
return {
success: true,
originalSize: { width: metadata.width, height: metadata.height },
newSize: { width, height },
outputPath
};
} catch (error) {
return {
success: false,
error: error.message
};
}
}
calculateDimensions(originalWidth, originalHeight, options = {}) {
const { maxWidth = this.options.maxWidth, maxHeight = this.options.maxHeight } = options;
let width = originalWidth;
let height = originalHeight;
if (width > maxWidth) {
height = (height * maxWidth) / width;
width = maxWidth;
}
if (height > maxHeight) {
width = (width * maxHeight) / height;
height = maxHeight;
}
return { width: Math.round(width), height: Math.round(height) };
}
async batchProcess(inputDir, outputDir) {
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
const files = fs.readdirSync(inputDir);
const imageFiles = files.filter(file =>
/\.(jpg|jpeg|png|webp)$/i.test(file)
);
const results = [];
for (const file of imageFiles) {
const inputPath = path.join(inputDir, file);
const outputPath = path.join(outputDir, `processed-${file}`);
const result = await this.processImage(inputPath, outputPath);
results.push({ file, ...result });
}
return results;
}
}
// 사용 예제
const processor = new ImageProcessor({ quality: 85 });
// 단일 파일 처리
const result = await processor.processImage('input.jpg', 'output.jpg');
// 일괄 처리
const results = await processor.batchProcess('./input', './output');
console.log('처리 결과:', results);