이미지 미리보기

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

일단 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) + 쓸모있는 라이브러리

profile
개발자 새싹🌱 The only constant is change.

0개의 댓글