Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 57 additions & 31 deletions src/feature/create-album/utils/heicToJpeg.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,29 @@
import Toast from '@/global/components/toast/Toast';
import { heicTo } from 'heic-to';

// 배치 병렬 처리를 위한 헬퍼 함수
async function convertSingleHeicFile(file: File): Promise<File> {
try {
const jpegBlob = await heicTo({
blob: file,
type: 'image/jpeg',
quality: 0.9,
});
return new File([jpegBlob], file.name.replace(/\.(heic|heif)$/i, '.jpg'), {
type: 'image/jpeg',
lastModified: file.lastModified,
});
} catch (e) {
Toast.alert(`${file.name} 파일의 변환에 실패했습니다.`);
return file;
}
}

// 메인 스레드 양보를 위한 delay 함수
function delay(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}

export async function convertHeicFilesToJpeg(files: File[]): Promise<File[]> {
const heicFiles = files.filter(
(file) =>
Expand All @@ -11,43 +34,46 @@ export async function convertHeicFilesToJpeg(files: File[]): Promise<File[]> {
return files;
}

let convertedCount = 0;
const totalCount = heicFiles.length;
let convertedCount = 0;

// 초기 메시지 표시
Toast.alert(`변환 중... 0/${totalCount}`);

const convertedFiles: File[] = [];

for (const file of files) {
if (/heic|heif/i.test(file.type) || /\.heic$|\.heif$/i.test(file.name)) {
try {
const jpegBlob = await heicTo({
blob: file,
type: 'image/jpeg',
quality: 0.9,
});
const convertedFile = new File(
[jpegBlob],
file.name.replace(/\.(heic|heif)$/i, '.jpg'),
{
type: 'image/jpeg',
lastModified: file.lastModified,
},
);
convertedFiles.push(convertedFile);

// 변환 완료마다 진행률 업데이트
convertedCount++;
Toast.alert(`변환 중... ${convertedCount}/${totalCount}`);
} catch (e) {
Toast.alert(`${file.name} 파일의 변환에 실패했습니다.`);
convertedFiles.push(file);
}
} else {
convertedFiles.push(file);
// 파일을 HEIC와 non-HEIC로 분리
const heicFileSet = new Set(heicFiles);
const nonHeicFiles = files.filter((file) => !heicFileSet.has(file));
Comment on lines +43 to +45
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

heicFileSetnonHeicFiles 변수가 선언되었지만 코드 내에서 사용되지 않고 있습니다. 또한, 관련 주석 // 파일을 HEIC와 non-HEIC로 분리도 현재 구현 방식과는 맞지 않아 혼란을 줄 수 있습니다. 최종 결과는 files.map을 통해 올바르게 생성되므로, 이 불필요한 코드 블록을 제거하여 가독성을 높이는 것이 좋습니다.


// 배치 크기 (한 번에 3개씩 병렬 처리)
const BATCH_SIZE = 3;
const convertedHeicFiles: File[] = [];

// 배치별로 처리
for (let i = 0; i < heicFiles.length; i += BATCH_SIZE) {
const batch = heicFiles.slice(i, i + BATCH_SIZE);

// 배치 내에서는 병렬 처리
const batchResults = await Promise.all(
batch.map((file) => convertSingleHeicFile(file)),
);

convertedHeicFiles.push(...batchResults);
convertedCount += batch.length;

// 진행률 업데이트
Toast.alert(`변환 중... ${convertedCount}/${totalCount}`);

// 다음 배치 전에 메인 스레드에 양보 (UI 반응성 확보)
if (i + BATCH_SIZE < heicFiles.length) {
await delay(10);
}
}

return convertedFiles;
// 원본 파일 순서 유지하면서 병합
const fileMap = new Map<File, File>();
heicFiles.forEach((original, index) => {
fileMap.set(original, convertedHeicFiles[index]);
});

return files.map((file) => fileMap.get(file) || file);
}
Loading