기존의 파일 업로드는 파일을 올리면 벡엔드에서 주소를 받아오는 형태였다. 이 방법에는 두 가지 문제가 있다.
이 문제를 해결하기 위해서 각각의 방법을 사용할 수 있다.
실습
준비물
const [file1, setFile1] = useState<File>(); const [imageUrl, setImageUrl] = useState(""); const [uploadFile] = useMutation(UPLOAD_FILE)
JS로 URL 만들기
JS 내장 객체인 FileReader 를 이용한다.
const fileReader = new FileReader(); // file 을 읽어서 url 형태로 만들어준다 fileReader.readAsDataURL(file); // 파일 다 읽었을 때 실행 할 함수 fileReader.onload = (data) => { if (typeof data.target?.result === "string") { // 파일의 크기만큼 url 생성 setImageUrl(data.target?.result); } };
등록/업로드 동시 실행
const onClickSubmit = async () => { const result1 = await uploadFile({ variables: { file: file1 }, }); // DB에 저장되는 값은 간략하게 정리된 url const imageUrl = result1.data.uploadFile.url; const result2 = await createBoard({ variables: { createBoardInput: { writer password title contents // 여기에 setImageUrl 값을 넣어도 되긴 하지만 url을 전부 가져와서 사이즈가 너무 크다 images: [imageUrl], }, }, }); };
promise.all 메서드는 모든 promise 가 실행될 때까지 기다린다. promise 와 promise.all 을 사용해서 파일을 업로드 한다고 했을 때 어떤 차이가 있는지 살펴보자.
const onClickPromise = async () => {
console.time("Promise 시작!!!"); // 걸리는 시간 측정하기
const result1 = await new Promise((resolve, reject) => {
setTimeout(() => {
resolve("https://dog1.jpg");
}, 3000);
});
console.log(result1);
const result2 = await new Promise((resolve, reject) => {
setTimeout(() => {
resolve("https://dog2.jpg");
}, 1000);
});
console.log(result2);
const result3 = await new Promise((resolve, reject) => {
setTimeout(() => {
resolve("https://dog3.jpg");
}, 2000);
});
console.log(result3);
console.timeEnd("Promise 시작!!!"); // 이름이 똑같아야 측정가능
};
const onClickPromiseAll = async () => {
const result = await Promise.all([
new Promise((resolve, reject) => {
setTimeout(() => {
resolve("https://dog1.jpg");
}, 3000);
}),
new Promise((resolve, reject) => {
setTimeout(() => {
resolve("https://dog2.jpg");
}, 1000);
}),
new Promise((resolve, reject) => {
setTimeout(() => {
resolve("https://dog3.jpg");
}, 2000);
}),
]);
};
console.time() 과 console.timeEnd 를 사용해서 처음부터 끝까지 실행했을 때의 시간을 비교해보면 Promise.all 을 사용했을 때 시간이 절반 가까이 단축된 걸 확인할 수 있다.
map을 이용해 더 간결하게 코드를 작성해줄 수 있다.
const result = await Promise.all(
["https://dog1.jpg", "https://dog2.jpg", "https://dog3.jpg"].map(
(el) =>
new Promise((resolve, reject) => {
setTimeout(() => {
resolve(el);
}, 3000);
})
)
);
스크롤이 내려감에 따로 필요한 이미지들을 조금씩 다운 받는다. react-lazy-load 라이브러리 사용해서 구현할 수 있다.
다른 페이지에 있을 때 크기가 큰 이미지를 미리 다운 받아 대기했다가 사용자가 이미지가 있는 페이지를 클릭하면 로딩 없이 이미지를 보여준다.
useEffect(() => {
const img = new Image();
img.src =
"이미지 주소";
img.onload = () => {
// 이미지가 전부 로딩되면 setImgTag 에 저장
setImgTag(img); // imgTag 는 이미지가 다운로드 된 이미지 태그가 된다
};
}, []);