시작하기에 앞서
사용자의 프로필 사진 데이터를 아마존 S3에 이미지를 업로드 하여 사용하고 있었는데 이미지 업로드 시에 별도의 제한 없이 업로드할 수 있도록 생각했지만 서버에서 1MB 이상은 업로드할 수 없는 문제가 있었습니다.
업로드 가능한 용량을 프론트에서 제한하거나 서버에서 제한을 수정하는 것으로 문제를 해결할 수도 있겠지만 프로필 사진의 경우 항상 같은 크기로 사용자들에게 노출되며 그다지 큰 해상도가 필요하지 않았습니다.
따라서 큰 이미지를 굳이 서버에 저장하고 커다란 이미지를 내려 받는 것을 비효율적이라고 생각했습니다.
그러므로 이미지를 업로드하기 전 압축해서 서버에 업로드하도록 합시다!
이미지를 압축하는 방법은 여러가지 있습니다.
브라우저에서 이미지를 압축하는 방법과 서버에서 압축하는 방법에 대하여 아래와 같은 장단점을 고려해볼 수 있을 것 같습니다.
서버에서 이미지를 압축하는 방법
장점
- 클라이언트의 성능에 영향을 받지 않습니다. 이미지 작업이 서버에서 이루어지므로 브라우저와 사용자 디바이스 사양에 영향을 덜 받습니다.
- 일관성과 안정성을 확보할 수 있습니다. 모든 클라이언트가 동일한 방식으로 이미지를 압축하고 처리할 수 있게 되므로 이미지 품질과 크기에 대한 일관성과 안정성을 유지하는 것이 가능합니다.
- 추가적인 보안 기능, 이미지를 업로드하기 전에 서버에서 보안 검사를 수행하여 악의적인 파일 업로드를 방지하는 등의 추가적인 보안 기능을 구현하는 것이 가능합니다.
단점
서버 자원 부하 이미지 처리는 CPU 자원가 메모리를 소모하는 작업일 수 있으므로, 많은 이미지 처리 요청이 동시에 발생하면 서버 자원 부하가 증가할 수 있습니다.
브라우저에서 이미지를 압축하는 방법
장점
- 이미지 처리가 브라우저에서 이루어지므로, 서버로 이미지를 업로드하기 전에 이미지를 빠르게 압축하고 처리할 수 있습니다. 따라서 사용자에게 더 빠른 응답 시간을 제공할 수 있습니다.
- 이미지 처리 작업이 브라우저에서 이루어지므로, 서버의 부하를 줄일 수 있습니다.
단점
- 브라우저 성능 혹은 디바이스의 성능에 따라 이미지 처리 속도가 다를 수 있습니다. 또한 일부 구형 브라우저에서는 지원되지 않을 수도 있습니다.
- 다양한 브라우저와 버전에서 이미지를 처리하기 때문에, 이미지 품질과 크기에 대한 일과성을 유지하기 어려울 수 있습니다.
현재 진행 중인 프로젝트에서는 사용자의 프로필 이미지를 저장하는 것에 이미지 저장 기능을 사용하고 있습니다.
주로 사용되는 이미지 크기는 250p 이하이므로 이미지 품질에 크게 목을 맬 필요가 없다고 생각되었습니다.
또한 업로드할 파일은 이미지 파일로 제한하는 것으로 보안 문제는 크게 고민하지 않아도 될 것으로 판단했습니다.
플리커와 같은 사진 작업물을 원본 혹은 원본에 매우 근접한 상태로 압축해야하는 경우라면 서버에서 압축을 진행하는 것이 좋겠지만 250p 정도의 해상도로 압축하여 사용할 프로필 이미지라면 브라우저에서 압축을하더라도 품질에 대해서는 걱정할 필요가 없겠습니다.
따라서 서버의 부하를 줄일 수 있고 빠른 서버 응답을 확보하기 위해 브라우저에서 압축하는 방식을 사용하기로 결정했습니다.
어떤 방법으로 이미지를 압축할 것인가?
이미지를 압축할 수 있는 방법은 다양하지만 이미 잘 만들어진 라이브러리가 많으므로 어떤 라이브러리를 사용할지 골라봅시다.
단순하게 이미지를 압축할 예정이므로 다양한 옵션을 제공할 필요는 없습니다. 간편하게 사용할 수 있는 것으로 골라본 결과 react-image-file-resizer
가 가장 간편하게 사용할 수 있을 것이라고 판단되었습니다.
설치
yarn add react-image-file-resizer
기존의 이미지 처리를 위한 코드
const saveImgFile = async (e) => {
const file = await e.target.files[0];
setProfileData((prevData) => ({
...prevData,
imgFile: URL.createObjectURL(file),
uploadImage: file,
}));
};
수정한 코드
const resizeFile = (file) =>
new Promise((resolve) => {
Resizer.imageFileResizer(file, 500, 500, "JPEG", 100, 0, (uri) => {
resolve(uri);
},"file");
});
const saveImgFile = async (e) => {
const file = await e.target.files[0];
// 파일 형식이 지원되는지 확인합니다. (JPEG, PNG, 또는 WEBP)
const supportedFormats = ["image/jpeg", "image/png", "image/webp"];
if (!supportedFormats.includes(file.type)) {
alert(
"지원되지 않는 이미지 형식입니다. JPEG, PNG 또는 WEBP 형식의 이미지를 업로드해주세요."
);
return;
}
try {
const compressedFile = await resizeFile(file);
// 압축된 이미지를 서버에 업로드하거나 사용할 수 있습니다.
// imgFile이 이미지 파일인 경우에만 createObjectURL 함수를 사용합니다.
if (imgFile instanceof File || imgFile instanceof Blob) {
URL.revokeObjectURL(imgFile); // 기존에 생성한 URL을 해제합니다.
}
setProfileData((prevData) => ({
...prevData,
imgFile: URL.createObjectURL(compressedFile),
uploadImage: compressedFile,
}));
} catch (error) {
console.error("이미지 리사이징 및 압축 중 오류가 발생했습니다:", error);
// 오류를 적절하게 처리합니다. (예: 사용자에게 오류 메시지를 표시)
}
};
이미지 크기는 추후 조금 더 큰 이미지를 사용하게 될수도 있으므로 현재 사용하는 크기보다 조금 넉넉하게 설정했습니다.
imageFileResizer
함수는 이미지를 리사이즈하고 압축하는 데 사용되는 메서드로서 다음과 같은 인자들을 받습니다. 각각의 인자들의 설명은 아래와 같습니다.
- file: (필수) 리사이즈하고 압축할 대상 이미지 파일입니다. 주로 사용자가
<input type="file" />
을 통해 선택한 파일이 전달됩니다. - maxWidth: (옵션) 이미지의 가로 너비를 제한할 값입니다. 이미지의 원본 가로 너비가 이 값보다 크다면, 리사이징을 수행하여 가로 너비를 지정한 값으로 맞춥니다. 이 값이 설정되지 않으면 이미지의 가로 너비는 변경되지 않습니다.
- maxHeight: (옵션) 이미지의 세로 높이를 제한할 값입니다. 이미지의 원본 세로 높이가 이 값보다 크다면, 리사이징을 수행하여 세로 높이를 지정한 값으로 맞춥니다. 이 값이 설정되지 않으면 이미지의 세로 높이는 변경되지 않습니다.
- outputType: (옵션) 압축 후 이미지의 출력 형식을 지정합니다. 주로 'JPEG', 'PNG', 'WEBP'와 같은 값이 사용됩니다. 예를 들어 'JPEG'로 설정하면 이미지가 JPEG 형식으로 압축됩니다. 기본값은 입력 이미지의 형식과 동일하게 설정됩니다.
- quality: (옵션) 압축 품질을 지정합니다. 0부터 1 사이의 값으로 표현되며, 값이 1에 가까울수록 고화질 압축을 의미합니다. 이 값이 1에 가까울수록 이미지 품질이 높아지지만, 이미지 크기가 커질 수 있습니다. 기본값은 0.92입니다.
- rotate: (옵션) 이미지 회전 각도를 지정합니다. 0, 90, 180, 270 중 하나의 값을 설정할 수 있습니다. 기본값은 0으로, 이미지의 회전이 수행되지 않습니다.
- callback: (옵션) 압축된 이미지를 처리한 후 호출되는 콜백 함수입니다. 콜백 함수의 인자로 압축된 이미지의 데이터 URI가 전달됩니다.
- outputType:(옵션) 압축된 이미지를 출력할 타입을 설정합니다. 기본값은 "BASE64"입니다.
이렇게 하면 브라우저에서 이미지를 압축할 수 있습니다.
압축한 이미지는 적절하게 서버에 저장하거나 미리보기 등으로 활용할 수 있습니다.
'Develop > Web Front End' 카테고리의 다른 글
케스케이딩 원칙 이해하기 (0) | 2024.01.20 |
---|---|
MobX - 간단하고 확장 가능한 상태 관리 (0) | 2023.12.03 |
[React] 전역 상태 관리가 필요한 이유 (0) | 2023.08.01 |
[React] 텍스트 타이핑하는 애니메이션 만들기 (0) | 2023.07.14 |
[React] Local Storage 이용해서 브라우저에 데이터 저장하기 (0) | 2023.04.24 |