[React] 클라이언트에서 이미지를 압축해보자
![[React] 클라이언트에서 이미지를 압축해보자](/_next/image?url=https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1759903597964%2F227ea5dc-8ff1-4999-9869-1bceb71b210a.png&w=3840&q=75)
우리는 서버 비용을 직접 부담하고 있기 때문에 항상 최적화와 비용 절감 방법을 고민하게 된다. 현재 자유게시판이 활성화가 많이 되지는 않았지만 사용자들이 업로드하는 이미지가 평균 5MB 이상일 경우를 대비해서 클라이언트에서 이미지 압축을 진행했다.
웹에서는 크게 두 가지 방법이 있었는데
Canvas API를 직접 사용하는 방법
browser-image-compression라이브러리를 활용하는 방법
이 있다.
여러 장점을 고려해 browser-image-compression을 선택했다. Web Worker를 지원해 UI가 막히지 않고 간단한 옵션으로 최대 용량과 크기를 조절할 수 있어 대규모 서비스에도 적합하다고 판단..!(아직 대규모 서비스는 아니지만.. 혹시 몰라 😬)
나는 현재 글 작성시 tiptap 에디터를 사용하고 있어서 이미지 업로드 부분에 적용해 주기로 했다.
설치
//yarn
yarn add browser-image-compression
//npm
npm install browser-image-compression
이미지 업로드 코드
import { forwardRef, useCallback, useEffect, useRef } from 'react'
import axios from 'axios'
import { authApi } from '../../../services/api/axios'
import EditorToolbar from './EditorToolbar'
import ImageBubbleMenu from './ImageBubbleMenu'
import useEditorInstance from '../../../hooks/writePost/useEditorInstance'
import { EditorContent } from '@tiptap/react'
import ToastService from '../../../services/toast/ToastService'
const MAX_FILE_SIZE = 5 * 1024 * 1024
const WriteEditor = forwardRef(({ value, initialContent, onChange, onAddFileId }, ref) => {
const handleFileChange = async (e) => {
const file = e.target.files[0]
if (!file) return
try {
const url = await uploadImage(file)
editor.chain().focus().setImage({ src: url }).run()
} catch (error) {
ToastService.error('이미지 업로드 실패. 다시 시도해주세요.')
}
}
return (
<div>
<input id='file-input' type='file' accept='image/*' onChange={handleFileChange} hidden />
</div>
)
})
WriteEditor.displayName = 'WriteEditor'
export default WriteEditor
수정
import { forwardRef, useCallback, useEffect, useRef } from 'react'
import axios from 'axios'
import { authApi } from '../../../services/api/axios'
import EditorToolbar from './EditorToolbar'
import ImageBubbleMenu from './ImageBubbleMenu'
import useEditorInstance from '../../../hooks/writePost/useEditorInstance'
import { EditorContent } from '@tiptap/react'
import ToastService from '../../../services/toast/ToastService'
import imageCompression from 'browser-image-compression'
const MAX_FILE_SIZE = 5 * 1024 * 1024
const WriteEditor = forwardRef(({ value, initialContent, onChange, onAddFileId }, ref) => {
const handleFileChange = async (e) => {
const file = e.target.files[0]
if (!file) return
try {
// 브라우저에서 압축
const options = { maxSizeMB: 1, maxWidthOrHeight: 800, useWebWorker: true }
const compressedFile = await imageCompression(file, options)
// 업로드
const url = await uploadImage(compressedFile)
// 에디터에 삽입
editor.chain().focus().setImage({ src: url }).run()
} catch (error) {
ToastService.error('이미지 업로드 실패. 다시 시도해주세요.')
}
}
이런식으로 적용을 해주고 직접 콘솔로 확인해본결과

90% 정도 압축된 것을 직접 확인했다.. 굉장한걸 👍🏻
이미지 압축은 생각보다 굉장히 쉽고 서버 비용을 고려할 때는 필수적인 최적화 과정임을 다시 한번 느꼈다.. !
![[책 추천] 프론트엔드 개발자라면 반드시 알아야 할 '웹 접근성' 이야기](/_next/image?url=https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1753777028409%2F0438e77e-a609-48d8-989a-68191214a5b1.png&w=3840&q=75)
![[Next.js] 나만의 학습 블로그 만들기#3-다국어 지원 (Feat.next-i18next)](/_next/image?url=https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1752472189546%2Fa25d48d1-715a-4c90-a530-7dded2981fe7.png&w=3840&q=75)
![[Next.js] 나만의 학습 블로그 만들기#4 - 댓글 기능(Feat. Giscus)](/_next/image?url=https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1752477512377%2Fe1e868c0-6968-49f0-8fc5-91083b01ef79.png&w=3840&q=75)