From d95eed66152680d3b5268311629ba63f04529b81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=98=81=EA=B8=B8/KIM=20YOUNG=20GIL?= <80146176+Gilpop8663@users.noreply.github.com> Date: Thu, 14 Sep 2023 15:26:25 +0900 Subject: [PATCH] =?UTF-8?q?=EA=B2=8C=EC=8B=9C=EA=B8=80=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=20=EC=8B=9C=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=EC=9D=84=20=EB=B3=B4=EB=82=BC=20=EB=95=8C=20webp=20?= =?UTF-8?q?=EB=A1=9C=20=EC=95=95=EC=B6=95=ED=95=98=EC=97=AC=20=EC=84=B1?= =?UTF-8?q?=EB=8A=A5=20=EA=B0=9C=EC=84=A0=20(#614)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: (#555) browser-image-compression 설치 및 본문 이미지 훅에 적용 * feat: (#555) 선택지 옵션 사진을 webp로 변환하도록 구현 --- frontend/package.json | 1 + frontend/src/hooks/useContentImage.ts | 18 +++++++++++++----- frontend/src/hooks/useWritingOption.tsx | 22 ++++++++++++++++------ frontend/src/utils/resizeImage.ts | 17 +++++++++++++++++ 4 files changed, 47 insertions(+), 11 deletions(-) create mode 100644 frontend/src/utils/resizeImage.ts diff --git a/frontend/package.json b/frontend/package.json index 36966bd41..edfcc6404 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -20,6 +20,7 @@ }, "dependencies": { "@tanstack/react-query": "^4.29.19", + "browser-image-compression": "^2.0.2", "dotenv": "^16.3.1", "msw": "^1.2.3", "react": "^18.2.0", diff --git a/frontend/src/hooks/useContentImage.ts b/frontend/src/hooks/useContentImage.ts index 85dfd196e..8a859eed2 100644 --- a/frontend/src/hooks/useContentImage.ts +++ b/frontend/src/hooks/useContentImage.ts @@ -2,6 +2,8 @@ import { ChangeEvent, useRef, useState } from 'react'; import { MAX_FILE_SIZE } from '@components/PostForm/constants'; +import { convertImageToWebP } from '@utils/resizeImage'; + export const useContentImage = (imageUrl: string = '') => { const [contentImage, setContentImage] = useState(imageUrl); const contentInputRef = useRef(null); @@ -11,13 +13,23 @@ export const useContentImage = (imageUrl: string = '') => { if (contentInputRef.current) contentInputRef.current.value = ''; }; - const handleUploadImage = (event: ChangeEvent) => { + const handleUploadImage = async (event: ChangeEvent) => { const { files } = event.target; if (!files) return; const file = files[0]; + const webpFileList = await convertImageToWebP(file); + + event.target.files = webpFileList; + + const reader = new FileReader(); + + const webpFile = webpFileList[0]; + + reader.readAsDataURL(webpFile); + event.target.setCustomValidity(''); if (file.size > MAX_FILE_SIZE) { @@ -27,10 +39,6 @@ export const useContentImage = (imageUrl: string = '') => { return; } - const reader = new FileReader(); - - reader.readAsDataURL(file); - reader.onloadend = () => { setContentImage(reader.result?.toString() ?? ''); }; diff --git a/frontend/src/hooks/useWritingOption.tsx b/frontend/src/hooks/useWritingOption.tsx index 7211b24ce..4b465b0c6 100644 --- a/frontend/src/hooks/useWritingOption.tsx +++ b/frontend/src/hooks/useWritingOption.tsx @@ -2,6 +2,8 @@ import React, { ChangeEvent, useState } from 'react'; import { MAX_FILE_SIZE } from '@components/PostForm/constants'; +import { convertImageToWebP } from '@utils/resizeImage'; + const MAX_WRITING_LENGTH = 50; export interface WritingVoteOptionType { @@ -78,13 +80,26 @@ export const useWritingOption = (initialOptionList: WritingVoteOptionType[] = IN setOptionList(updatedOptionList); }; - const handleUploadImage = (event: React.ChangeEvent, optionId: number) => { + const handleUploadImage = async ( + event: React.ChangeEvent, + optionId: number + ) => { const { files } = event.target; if (!files) return; const file = files[0]; + const webpFileList = await convertImageToWebP(file); + + event.target.files = webpFileList; + + const reader = new FileReader(); + + const webpFile = webpFileList[0]; + + reader.readAsDataURL(webpFile); + event.target.setCustomValidity(''); if (file.size > MAX_FILE_SIZE) { @@ -94,11 +109,6 @@ export const useWritingOption = (initialOptionList: WritingVoteOptionType[] = IN return; } - const reader = new FileReader(); - - // readAsDataURL 메서드를 통해 파일을 모두 읽고 나면 reader의 loadend 이벤트에서 이미지 미리보기 결과를 확인할 수 있습니다. - reader.readAsDataURL(file); - reader.onloadend = () => { const updatedOptionList = optionList.map(optionItem => { if (optionItem.id === optionId) { diff --git a/frontend/src/utils/resizeImage.ts b/frontend/src/utils/resizeImage.ts new file mode 100644 index 000000000..383db9a9a --- /dev/null +++ b/frontend/src/utils/resizeImage.ts @@ -0,0 +1,17 @@ +import imageCompression from 'browser-image-compression'; + +export const convertImageToWebP = async (imageFile: File) => { + const compressedBlob = await imageCompression(imageFile, { + maxWidthOrHeight: 1280, + initialQuality: 0.5, + fileType: 'image/webp', + }); + + const outputWebpFile = new File([compressedBlob], `${Date.now().toString()}.webp`); + + const dataTransfer = new DataTransfer(); + + dataTransfer.items.add(outputWebpFile); + + return dataTransfer.files; +};