TIL[19일차].이미지 업로드(+이미지 검증)

남예지·2022년 11월 24일
0

TIL

목록 보기
15/47

클릭하면 함수작동

eslint 오류를 해결할 수 있다.

또 하이오드 function과 프로미스 관련 내용은 뒤에 더 깊게 배우게 된다.

프리보드 파이어베이스부분
이미지나 동영상은 파이어베이스 storage라는 서비스에 저장

다시 복습하면
rest-api : react-query
graphql-api : apollo
uuid : universally unique IDentifier
npm에서 검색하기

import { v4 as uuidv4 } from 'uuid';

쓰는 이유는 겹치지 않는 값을 넣기 위해. 그런데 별로 좋은 방법은 아니다.매번 바뀌는 값이기 때문에 성능상 문제가 있음. (나중에 다시 배움)

이미지 업로드

이미지 파일을 저장하면 백앤드 서버로 이동하는데 이미 백앤드 개발자가 만들어놓은 uploadFile에 저장된다.
그러면 클라우드 storage라는 곳에 파일을 저장.
여기는 파일을 저장하면 파일을 다운받을 수 있는 주소를 제공한다.
백앤드에서 다운로드 urlurl을 브라우저로 넘겨준다.
우리가 게시글을 업로드하면 createBoard와 다운로드 URL을 같이 넘겨주고 백앤드는 DB에 정보를 넘겨준다.

조회 시 fetchBoard를 해서 온다.

<img src="다운로드 url" />
이렇게 들어간다. 주소는 문자열이다.

우리가 고민해봐야할게
왜 파일은 용량이 클까?
"영희"가 저장된 txt파일은 용량이 작은데

파일이 용량이 작으면 데이터 베이스에 들어갈 수 있나요?
사실 용량이 커도 들어갈수는 있습니다. 다만 파일은 blob이라는 타입으로 들어간다.

사진을 엄청 크게 확대하면 모자이크처럼 픽셀 하나하나가 보이는데 이런 조각조각마다 색을 줄 수 있다. 이게 컴퓨터 입장에서는 다 숫자다보니 용량이 클 수 밖에 없다.
그럼 데이타베이스에 넣기는 비효율적이라 클라우드에 넣고 다운로드 url만 가지고 오는 것이다.

img태그의 작동방식

<img src="qqq.jpg" />
이렇게 되어있는 Img태그는 (폰트나 이미지) 다 로드 된 뒤에 다운받으러 가기 때문에 로딩이 느리다.

일단 사용할 api가 uploadFile 이라서 yarn add apollo-upload-client를 설치해주어야 함

  • 이미지 업로드 실습
  1. 이미지 업로드 버튼 <input type="file" />
    이렇게 뜸
  2. onChange 함수를 만들어 바인딩 해줌
  3. input에 multiple이라는 속성을 true로 해줘야 여러장 선택 가능
  4. 같은 맥락으로 event.target.files 로 받아와야함. filesdls 인 이유는 3과 같다.
  5. console.log(file) 을 찍어보면 파일 이름과 수정날짜, 타입 등등이 들어온다.
  6. 백앤드 api로 업로드 해야함 uploadFile 쿼리 작성
  7. useMutation을 사용해서 입력함
  8. 입력한 useMutation 안에 file을 result에 넣고
  9. useState를 써서 imageUrl을 만들어주고, result.data?.uploadFile.url을 가져온다. (이게 저장한 이미지 url주소)
  10. 이 주소를 img 태그의 src에 스토리지 경로 뒤에 적어준다.
  11. 이미지 업로드 끝!

가장 간단한 경로이다.

import { gql, useMutation } from "@apollo/client";
import { ChangeEvent, useState } from "react";
import {
  IMutation,
  IMutationUploadFileArgs,
} from "../../src/commons/types/generated/types";

const UPLOAD_FILE = gql`
  mutation uploadFile($file: Upload!) {
    uploadFile(file: $file) {
      url
    }
  }
`;
export default function ImageUploadPage() {
  // 이미지업로드 state
  const [imageUrl, setImageUrl] = useState("");

  const [uploadFile] = useMutation<
    Pick<IMutation, "uploadFile">,
    IMutationUploadFileArgs
  >(UPLOAD_FILE);

  const onChangeFile = async (event: ChangeEvent<HTMLInputElement>) => {
    const file = event.target.files?.[0]; // files. <input type="file" multiple /> 일 때 여러개 드래그 가능.

    const result = await uploadFile({ variables: { file } });
    console.log(result.data?.uploadFile.url);
    setImageUrl(result.data?.uploadFile.url ?? "");
  };

  return (
    <>
      <input type="file" onChange={onChangeFile} multiple={true} />
      <img src={`https://storage.googleapis.com/${imageUrl}`} alt="" />
    </>
  );
}

useRef

input태그 디자인이 맘에 들지 않아 좀더 꾸미고싶다면, 직접 div 버튼을 만들어서 이걸 눌렀을 때 아까 만든 input태그가 눌리는 것처럼 만들 수 있다.
바로 useRef를 통해서!
useRef는 react에서 HTML 태그에 접근을 도와주는 역할을 하고 있다.
사용법 : const inputEl = useRef();
useRouter와 비슷하다.

구조는 간단하다.
위의 코드에 3가지만 더 넣어주면 된다.
1. useRef
2. 대신 누를 태그
3. 태그에 넣을 onClick 함수

// 1.
const fileRef = useRef<HTMLInputElement>(null);

// 3. 
const onClickImage = () => {
    fileRef.current?.click();
  };

//2.
return(
	<div
    	style={{ width: "50px", height: "50px", backgroundColor: "gray" }}
    	onClick={onClickImage}>
    	이미지선택
    </div>
    
    // 인풋태그에는 style로 display: none 안보이게 하고 ref 바인딩
    <input
        style={{ display: "none" }}
        type="file"
        onChange={onChangeFile}
        ref={fileRef}
      />
)

ref={qqq}
qqq에 저장됨

이미지 검증

이미지를 jpeg나 png만 받고 싶다거나, 5MB 이하 파일만 받고 싶을 때
이미지 검증을 통해 원하는 이미지만 걸러서 볼 수 있다.
위에 코드를 또 사용해보자면 onChangeFile함수 안에

// 파일의 사이즈가 없다
  if (typeof file === "undefined") {
    alert("파일이 없습니다!");
    return ;
  }

이런식으로 if문을 넣어 조건을 걸고 만약 조건에 걸린다면 reture으로 함수를 끝내준다.

  // 1024*1024 는 1MB ,
  if (file?.size > 5 * 1024 * 1024) {
    alert("파일의 용량이 너무 큽니다. (제한: 5MB)");
    return;
  }

여기서 CS 상식
1024*1024 는 1MB, 왜 1000이 아니라 1024일까?
2*2*2*2*2*2*2* 컴퓨터는 0,1로 변환해서 읽는데 이는 켜고 끄는 방식으로 2를 계속 곱하게 된다. 그래서 1024 단위로 나가게 됨.

뭔가 복습을 하면서 멘토님이 그리는 큰 그림을 이해하게 되었다. 공부 3시간 복습 3시간은 정말 필수인듯

자꾸 치지만 말고 앞에 한것과 뒤에 한것의 차이와 뭐가 더 왜 좋은지를 좀 더 생각해야겠다는 생각이 들었다.

profile
총총

0개의 댓글