이미지 성능 최적화
[기존 방식]
[미리보기]
[Promise.all로 한번에 변환하기]
[LazyLoad, PreLoad, 이미지 관련 라이브러리]
이 내용은 이미지 업로드에만 국한되는 내용이 아니다!
다른 기능에도 잘 활용해보자~!
1) input태그에서 파일을 올리면(onChange가 일어나면) uploadFile API를 요청했다.
2) 백엔드에서는 그 파일을 Storage에 저장하고 url을 받아와서 API 응답으로 프론트에 돌려줬다.
3) 받은 url을 img 태그를 이용해서 미리보기를 보여줬다.
4) createBoard API에 url을 보내서 등록했다.
uploadFile을 통해 파일을 업로드하고 url을 받아오는 데 시간이 걸렸다.
이미지를 선택했다가 등록을 하지 않더라도 Storae에 선택했던 이미지가 모두 저장되어 불필요한 Storage 비용이 발생된다.
uploadFile
API로 보내서 간단한 url로 변환한다.createBoard
) 게시글을 등록한다.import { gql, useMutation } from "@apollo/client";
import { ChangeEvent, useState } from "react";
const CREATE_BOARD = gql`
mutation createBoard($createBoardInput: CreateBoardInput!) {
createBoard(createBoardInput: $createBoardInput) {
_id
}
}
`;
const UPLOAD_FILE = gql`
mutation uploadFile($file: Upload!) {
uploadFile(file: $file) {
url
}
}
`;
export default function ImageUploadPreviewPage() {
const [file1, setFile1] = useState<File>();
const [imageUrl, setImageUrl] = useState("");
const [uploadFile] = useMutation(UPLOAD_FILE);
const [createBoard] = useMutation(CREATE_BOARD);
const onChangeFile = (e: ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0]; // 파일이 없을 수도 있으니 옵셔널 체이닝 사용(배열도 옵셔널체이닝을 쓸 수 있다.)
if (!file) {
// 파일 검증
alert("파일이 없습니다.");
return;
}
const fileReader = new FileReader(); // JS의 내장기능이다.
fileReader.readAsDataURL(file); // file은 Blob(이진 형태의 큰 데이터)
// blob 타입의 file을 url 형태로 만든다. 이 url을 DB에 바로 저장할 수 있지만 크기가 너무 크니까 ㄴㄴ..
fileReader.onload = (data) => {
// file을 다 읽으면 읽어진 결과물이 data로 들어오고 이 함수가 실행된다.
if (typeof data.target?.result === "string") {
// useState 초기값으로 string("")을 넣었기 때문에 이 if문을 넣지 않으면 undefined나 null일 경우 때문에 에러가 난다.
console.log(data.target?.result); // file을 url 형태로 읽은 결과물이다.
setImageUrl(data.target?.result); // 미리보기를 위한 *임시 url* (Blob 형태)
setFile1(file); // uploadFile API에 보내기 위한 *File*
}
};
};
const onClickSubmit = async () => {
// 1) uploadFile에 *file*을 보내서 업로드용 url을 얻어낸다.
const result1 = await uploadFile({ variables: { file: file1 } });
const imageUrl = result1.data.uploadFile.url;
// 2) 게시글을 등록한다.
const result2 = await createBoard({
variables: {
createBoardInput: {
writer: "juhee",
title: "Ttttttttitle",
contents: "Connnnnntents",
password: "1234",
images: [imageUrl],
},
},
});
console.log(result2.data.createBoard._id);
};
return (
<div>
<input type="file" onChange={onChangeFile} />
<img src={imageUrl} />
{/* 미리보기 주소가 들어간다. */}
<button onClick={onClickSubmit}>게시글 등록하기</button>
</div>
);
}