Skip to content

Commit

Permalink
feat : optimizedPhoto
Browse files Browse the repository at this point in the history
- 사진 최적화 기능 추가
- avif, webp 파일 업로드 시 네트워크 400에러 발생
- 업로드 제한 관련 서버 응답 코드 : 504
    - 에러 메시지 : "이미지 파일만 업로드 가능합니다."
  • Loading branch information
Minsoek96 committed Dec 12, 2024
1 parent 94f8390 commit 468a4ce
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 40 deletions.
88 changes: 58 additions & 30 deletions src/_app/pages/CreateMessagePage/Steps/PhotoInputStep/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,55 +4,79 @@ import StepHeader from "@/components/Funnel/StepHeader";
import useToast from "@/hooks/useToast";
import { Photo } from "@/types/client";
import { isNill, isUndefined } from "@/utils";
import { convertToWebP } from "@/utils/convertToWebP";
import clsx from "clsx";
import { ChangeEvent, useRef } from "react";
import { ChangeEvent, useCallback, useRef } from "react";
import { useWindowSize } from "react-use";

interface Props {
photo: Photo | undefined;
setPhoto: (newPhoto: Photo) => void;
setPhoto: (newPhoto: Photo | undefined) => void;
maxSizeMB?: number;
maxWidth?: number;
maxHeight?: number;
quality?: number;
}
const PhotoInputStep = ({ photo, setPhoto }: Props) => {
const PhotoInputStep = ({
photo,
setPhoto,
maxSizeMB = 5,
maxWidth = 1920,
maxHeight = 1080,
quality = 0.8,
}: Props) => {
const { setGlobalLoading } = useLoadingOverlay();
const inputRef = useRef<HTMLInputElement>(null);
const { showToast } = useToast();
const onClickUpload = () => {
if (isNill(inputRef.current)) {
return;
}

const handleImageConvert = useCallback(
async (file: File): Promise<Photo> => {
return convertToWebP(file, { maxWidth, maxHeight, quality });
},
[maxWidth, maxHeight, quality]
);

const onClickUpload = () => {
if (isNill(inputRef.current)) return;
inputRef.current.click();
};
const onChange = async (event: ChangeEvent<HTMLInputElement>) => {
if (isNill(event.target) || isNill(event.target.files)) {
showToast("이미지를 불러오는 데 실패했어요.", "error");

return;
}
const onChange = async (event: ChangeEvent<HTMLInputElement>) => {
try {
if (isNill(event.target) || isNill(event.target.files)) {
showToast("이미지를 불러오는 데 실패했어요.", "error");
return;
}
const file = event.target.files[0];
setGlobalLoading(true);

const file = event.target.files[0];
setGlobalLoading(true);
const maxSize = maxSizeMB * 1024 * 1024; // 10MB
if (file.size > maxSize) {
setGlobalLoading(false);
showToast("이미지 용량이 너무 커요.", "error");
return;
}

const maxSize = 5 * 1024 * 1024; // 10MB
if (file.size > maxSize) {
const optimizedPhoto = await handleImageConvert(file);
setPhoto(optimizedPhoto);
} catch (error) {
showToast("이미지 처리 중 오류가 발생했어요.", "error");
} finally {
setGlobalLoading(false);
showToast("이미지 용량이 너무 커요.", "error");

return;
if (inputRef.current) {
inputRef.current.value = "";
}
}
};

const fileUrl = URL.createObjectURL(file);

const img = new window.Image();
img.src = fileUrl;
img.onload = () =>
setPhoto({
file,
url: fileUrl,
});
setGlobalLoading(false);
const handleRemoveImage = () => {
if (photo?.url) {
URL.revokeObjectURL(photo.url);
}
setPhoto(undefined);
};


const { height } = useWindowSize();

return (
Expand Down Expand Up @@ -84,12 +108,16 @@ const PhotoInputStep = ({ photo, setPhoto }: Props) => {
<img
src={photo.url}
className={clsx("w-full h-full rounded-[15px] object-contain")}
alt="업로드된 이미지"
onDoubleClick={handleRemoveImage}
/>
)}
</div>
<div className="h-[18px]" />
<p className="text-[#A1A1A1] text-[14px] text-center">
사진은 최대 1장, 5mb까지 업로드 가능해요.
{isUndefined(photo)
? `사진은 최대 1장, ${maxSizeMB}mb까지 업로드 가능해요.`
: "이미지를 더블클릭하면 제거할 수 있어요."}
</p>
</div>
<input
Expand Down
18 changes: 8 additions & 10 deletions src/utils/convertToWebP.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ const drawImageToCanvas = (
return canvas;
};


//TODO : 서버 정책으로 인한 avif ,webp 등 400에러
//code : 504 , message : 이미지 파일만 업로드 가능합니다.

/**
* Canvas를 WebP 형식으로 변환
* @param canvas - 변환할 Canvas
Expand All @@ -91,9 +95,9 @@ const canvasToWebP = (

const webpFile = new File(
[blob],
fileName.replace(/\.[^/.]+$/, ".webp"),
fileName.replace(/\.[^/.]+$/, ".jpeg"),
{
type: "image/webp",
type: "image/jpeg",
}
);
const webpUrl = URL.createObjectURL(blob);
Expand All @@ -103,7 +107,7 @@ const canvasToWebP = (
url: webpUrl,
});
},
"image/webp",
"image/jpeg",
quality
);
});
Expand Down Expand Up @@ -131,13 +135,7 @@ const fileToObjectUrl = (file: File): Promise<string> =>

/**
* 이미지 파일을 WebP 형식으로 변환
*
* @description
* 이 함수는 다음과 같은 최적화를 수행합니다:
* 1. 이미지 크기를 최대 허용 크기에 맞게 조정
* 2. WebP 형식으로 변환하여 파일 크기 최적화
* 3. 지정된 품질로 압축
*
*
* @param file - 변환할 이미지 파일
* @param config - 변환 설정
* @param config.maxWidth - 최대 허용 너비
Expand Down

0 comments on commit 468a4ce

Please sign in to comment.