FileReader
자바스크립트에 있는 기능이다. 미리보기를 할 수 있지만 내 컴퓨터에 저장되는 거기 때문에 다른사람은 못본다. 이후 fileReader.readAsDataURL(파일) 을 하면 파일에서 URL을 만들어준다. fileReader.onload로 불러온다.
export default function ImageUploadPreviewPage() {
function onChangeFile(event) {
const myFile = event.target.files[0];
console.log(myFile);
const fileReader = new FileReader();
fileReader.readAsDataURL(myFile);
fileReader.onload = (data) => {
console.log(data.target.result);
};
}
return <input type="file" onChange={onChangeFile}></input>;
}
그럼 이렇게 긴 주소가 나오게 된다. 하단에 복사가 되는데 이 주소로 들어가면 미리보기로 볼 수 있게 된다. 이걸 그럼 state에 담아서 보여주자.
import { useState } from "react";
export default function ImageUploadPreviewPage() {
const [imageUrl, setimageUrl] = useState("");
function onChangeFile(event) {
const myFile = event.target.files[0];
console.log(myFile);
const fileReader = new FileReader();
fileReader.readAsDataURL(myFile);
fileReader.onload = (data) => {
console.log(data.target.result);
setimageUrl(data.target?.result);
};
}
return (
<>
<img src={imageUrl} />
<input type="file" onChange={onChangeFile}></input>
</>
);
}
이렇게하면 컴퓨터 안에서 url로 바꿔서 올리는 거기때문에 속도가 굉장히 빠르게 된다.
대신 게시물 등록하기 버튼을 눌렀을때는 그 url을 올려봤자 남들은 못본다.
최종적으로 uploadfile요청을 어딘가는 해야한다.
그럼 등록하는 버튼을 통해서 파일 업로드 + 업로드된 파일로 게시물 등록과정을 거쳐야한다.
업로드된 파일로 게시물 등록을 할때 setimageUrl는 미리보기용 가짜주소일뿐이라는걸 알아야한다. + 그러면서 state에 진짜 파일을 넣어줘야하는것
setmyfile(파일)을 통해서 업로드한다.
import { useState } from "react";
import { gql, useMutation } from "@apollo/client";
import {
IMutation,
IMutationCreateBoardArgs,
} from "../../src/commons/types/generated/types";
const UPLOAD_FILE = gql`
mutation uploadFile($file: Upload!) {
uploadFile(file: $file) {
url
}
}
`;
const CREATE_BOARD = gql`
mutation createBoard($createBoardInput: CreateBoardInput!) {
createBoard(createBoardInput: $createBoardInput) {
_id
images
}
}
`;
export default function ImageUploadPreviewPage() {
const [imageUrl, setimageUrl] = useState("");
const [myFile, setmyfile] = useState();
const [createBoard] = useMutation(CREATE_BOARD);
const [uploadFile] = useMutation(UPLOAD_FILE);
function onChangeFile(event) {
const file = event.target.files[0];
console.log(file);
const fileReader = new FileReader();
fileReader.readAsDataURL(file);
fileReader.onload = (data) => {
console.log(data.target.result);
setimageUrl(data.target?.result);
setmyfile(file);
};
}
async function onClickSumbmit() {
let myImageUrl = "";
// 1. 파일업로드
if (myFile) {
const result1 = await uploadFile({
variables: {
file: myFile,
},
});
myImageUrl = result1.data?.uploadFile.url || "";
}
// 2. 업로드된 파일로 게시물 등록
const result2 = await createBoard({
variables: {
createBoardInput: {
writer: "영희",
password: "1234",
title: "안녕하세요~~",
contents: "이미지 업로드 연습중임",
images: [myImageUrl],
},
},
});
console.log(result2.data?.createBoard._id);
}
return (
<>
<img src={imageUrl} />
<input type="file" onChange={onChangeFile}></input>
<button onClick={onClickSumbmit}> 등록하기</button>
</>
);
}
이렇게 등록을 누르면 아이디값이 나오게된다
근데 파일 하나 누르고 등록하면 된다.
근데 여러개면 미리보기 5개까지는 쉽게 될것이다 + 근데 등록할때는 파일 다섯개를 업로드파일로 하나하나 시켜줘야한다.그럼 파일업로드 5개를 기다렸다가 업로드시켜야됨 await부분이 늘어나게된다.
그래서 개선하면?
일단 Promise all 없이 다중 이미지 업로드를 만들어보자
const [myFiles, setmyfiles] = useState([]);
myFile을 Files로 바꾸고 여러개니까 ([])배열로 설정한다
setmyfiles((prev) => [...prev, file]);
setmyFiles에 이전파일들 + 추가한 파일로 채우겠다고 한다.
세개를 해야된다면 이렇게 해야될수도있다. 각각 0,1,2번째 myImageUrls가 위에 빈 배열로 들어가게되는것
if (myFiles.length) {
const start = performance.now();
await uploadFile({ variables: { file: myFiles[0] } });
await uploadFile({ variables: { file: myFiles[0] } });
await uploadFile({ variables: { file: myFiles[0] } });
await uploadFile({ variables: { file: myFiles[0] } });
await uploadFile({ variables: { file: myFiles[0] } });
await uploadFile({ variables: { file: myFiles[0] } });
await uploadFile({ variables: { file: myFiles[0] } });
await uploadFile({ variables: { file: myFiles[0] } });
await uploadFile({ variables: { file: myFiles[0] } });
await uploadFile({ variables: { file: myFiles[0] } });
const end = performance.now();
console.log(end - start);
}
}
이렇게 모두다 await를 써야된다면
const start = performance.now();
await Promise.all([
uploadFile({ variables: { file: myFiles[0] } }),
uploadFile({ variables: { file: myFiles[0] } }),
uploadFile({ variables: { file: myFiles[0] } }),
uploadFile({ variables: { file: myFiles[0] } }),
uploadFile({ variables: { file: myFiles[0] } }),
uploadFile({ variables: { file: myFiles[0] } }),
uploadFile({ variables: { file: myFiles[0] } }),
uploadFile({ variables: { file: myFiles[0] } }),
uploadFile({ variables: { file: myFiles[0] } }),
]);
const end = performance.now();
console.log(end - start);
Promise all은 한번에 올릴 수 있다. await도 한번만 쓰면 된다
속도도, 이게 더 빠름
5000/50 짜잔
pre-load
미리 로딩해놓고 보여주면 속도가 빠를것
useEffect를 사용해서 state에 담아주면 빠를 것 같다
import { useEffect, useState } from "react";
export default function ImagePreloadPage() {
const [myLoadedImage, setMyLoadedImage] = useState();
const divRef = useRef(null);
useEffect(() => {
const img = new Image();
img.src = "https://codebootcamp.co.kr/images/main/homeImage1.webp";
img.onload = () => {
setMyLoadedImage(img);
};
});
function onClickButton() {
divRef.current?.appendChild(myLoadedImage);
}
return (
<>
<h1>안녕하세요 사이트에 오신 것을 환연합니다!!</h1>
<div ref={divRef}></div>
<button onClick={onClickButton}>이미지 보여주기</button>
{/* <img src="https://codebootcamp.co.kr/images/main/homeImage1.webp" /> */}
</>
);
}
처음에 이미지는 볼 수 없지만
이미지 태그를 이용해서 이미지를 가져오고(이미 다운받은) onload를 사용해서 이미지를 가져왔을때 -> 이미지를 state를 넣은 모습이다.
그리고 버튼을 클릭하면 버튼 안에서 div안으로 이미지를 붙여주는거다.
이미지 로딩이 이미 다되어있기 때문에 실행시키면 따로 다운하는게 아니고 바로 보여줄 수 있다.
LazyLoading
https://www.npmjs.com/package/react-lazyload
처음부터 모든것을 다운받으려면 속도가 느려진다.
레이지로드를 사용해서 스크롤을 내릴 때 그 부분을 로딩하게 하는게 좋다.
사용법은 그냥 LazyLoad 로 감싸면 된다.
페이지 스피드 점검
https://pagespeed.web.dev/?utm_source=psi&utm_medium=redirect&hl=ko
(핸드폰도 체크가능)
이렇게 성능개선방법을 제시한다
이미지가 성능을 느리게 하는데 많은 영향을 끼침
확장자도 마찬가지다
++ 보너스
https://www.npmjs.com/package/react-dropzone
드래그&드롭 이미지 올리기 기능 라이브러리
https://www.npmjs.com/package/react-avatar-editor
사진조절기능 있는 라이브러리
https://www.npmjs.com/package/react-beautiful-dnd
컴포넌트들을 드래그&드롭 할 수 있게 하기
요약
이미지 업로드들은 성능개선이 중요함, 여러개를 업로들 할때는 promiseall을 사용하기
이미지만 업로드하는게 아니고 promise만 있으면 되서 텍스트도 상관이없음
race- 먼저끝난거 캐치하기
promiseall은 굉장히 중요
성능개선에 있어서 확장자(webp),useEffect로 미리 받아오기, appendChild로 보여주기(preload) + 쓸모있는 라이브러리