220426

solsolsol·2022년 4월 26일
0

TIL

목록 보기
31/32

이미지 업로드

기존의 파일 업로드는 파일을 올리면 벡엔드에서 주소를 받아오는 형태였다. 이 방법에는 두 가지 문제가 있다.

  1. 이미지 미리보기가 뜨는 데 시간이 걸린다
  2. 필요없는 데이터가 DB에 저장된다

이 문제를 해결하기 위해서 각각의 방법을 사용할 수 있다.

  1. 자바스크립트로 브라우저에서 URL 만들기
  2. 게시물 등록과 파일 업로드를 동시에 실행

실습

준비물

  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);
      }
    };

📚 FileReader 더 알아보기

등록/업로드 동시 실행

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.all 메서드는 모든 promise 가 실행될 때까지 기다린다. promise 와 promise.all 을 사용해서 파일을 업로드 한다고 했을 때 어떤 차이가 있는지 살펴보자.

promise

  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 시작!!!"); // 이름이 똑같아야 측정가능
  };

promise.all


  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 을 사용했을 때 시간이 절반 가까이 단축된 걸 확인할 수 있다.

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);
          })
      )
    );

LazyLoad - PreLoad

LazyLoad

스크롤이 내려감에 따로 필요한 이미지들을 조금씩 다운 받는다. react-lazy-load 라이브러리 사용해서 구현할 수 있다.

PreLoad

다른 페이지에 있을 때 크기가 큰 이미지를 미리 다운 받아 대기했다가 사용자가 이미지가 있는 페이지를 클릭하면 로딩 없이 이미지를 보여준다.

useEffect(() => {
    const img = new Image();
    img.src =
      "이미지 주소";
    img.onload = () => {
      // 이미지가 전부 로딩되면 setImgTag 에 저장
      setImgTag(img); // imgTag 는 이미지가 다운로드 된 이미지 태그가 된다
    };
  }, []);

0개의 댓글

관련 채용 정보