Day 19

김정동·2021년 11월 25일
0

이미지 등록하기

브라우저에서 파일이 업로드 되는 과정을 알아야한다.

브라우저에서 파일을 업로드하면 (파일에 태그?)
백엔드서버에서 uploadFile을 통해서 데이터베이스가 아닌 storage에 저장이 된다.

그럼 storage에서는 주소를 준다
이후 백엔드에서 브라우저로 주소를 준다.

이후 이런 주소는 이런 정보를 또 따른데에 쓰이는데 쓰인다
새로운 게시물에서 제목, 내용, 이미지 등등을 저장할때
createBoard 로
writer : "이름"
title: "제목"
contents:"내용"
images:["주소1","주소2"...]
이렇게 모두 텍스트로 데이터베이스에 저장된다.

이렇게 된다

즉 파일 자체는 storage, 이후 주소를 받아와서 데이터베이스에 저장할때는 파일에 접근할 수 있는 텍스트로만 저장된다는 것임.

불러오는 순서도 알면 좋다

<div>
	<input/>
    <img src=''/>
</div>

html, css, javascript를 불러오는동안 이미지 칸은 빈 칸으로 그려온 다음 이후에 이미지를 가져오는 것이다. 이렇게 하지않고 그냥 그리면 빈화면을 오랫동안 보고 있어야할 것이다.
이후 이미지를 받아서 그려주는 셈이다

이미지 업로드 실습

export default function ImageUploadPage() {
  function onChangeFile(event) {
    const myFile = event.target.files[0];
    console.log(myFile);
  }
  return <input type="file" onChange={onChangeFile} />;
}

이렇게 이미지가 올라간 모습을 볼 수 있다.
이걸 백엔드서버로 보내야된다
playground를 이용해 uploadfile을 쓸것

이제 onChange를 통해 저장한 myFile은 스토리지로 보낸 실제 파일을 의미한다. 그럼 거기서 주소를 받아오는데 이 주소가 State(예시 : myimages) 에 저장해야한다. 이유는 이 주소를 myimages에 담아야 데이터베이스에 보낼 수 있기 때문

++ 업로드를 위해서는 아폴로업로드 클라이언트가 필요하다, 타입도 추가했다

yarn add apollo-upload-client
yarn add -D @types/apollo-upload-client

이후 app.tsx 를 수정한다
아폴로링크추가

import {createUploadLink} from 'apollo-upload-client'
import { ApolloClient, ApolloProvider, InMemoryCache, ApolloLink} from "@apollo/client";
function MyApp({ Component, pageProps }: AppProps) {

  const uploadLink = createUploadLink({
    uri: "http://backend04.codebootcamp.co.kr/graphql",

  })

  const client = new ApolloClient({
    link: ApolloLink.from([uploadLink]),
    cache: new InMemoryCache(),
  });

  return (
    <ApolloProvider client={client}>
      <Global styles={globalStyles} />
      <Layout>
        <Component {...pageProps} />
      </Layout>
    </ApolloProvider>
  );
}

이렇게 파일업로드뿐만 아니고 데이터베이스에 올라간 모습을 보면 성공이다.
https://storage.googleapis.com/codecamp-file-storage/2021/11/25/31.%20Registration3.PNG
저렇게 준 주소로 들어가게되면 이미지가 업로드 된 것을 확인할 수 있다

그래서 storage가 뭐지??

우리는 구글의 컴퓨터를 빌려서 파일을 저장하고 있는것이다. 지금은 코드캠프에서 빌려준 것을 사용하고있는 것이다. 실제 파일은 이곳에 저장된다.

이후 이걸 주소로 받게된다

그럼 또 게시물 등록에 이걸 올려서 state에 저장하고 같이 백엔드로 올리는 작업을 해보자

++ 파일 사이즈 검증부분만들어보기
파일이 없거나 용량이 너무 크면 제한을 걸어야한다. 또한 확장자 또한 제한을 걸 수 있다.

간단히 ImageUploadPage에 넣는다

if(!myFile?.size){
        alert("파일이 없습니다!");
        return;
    }

    if(myFile?.size > 5 * 1024 * 1024){
        alert("파일용량이 5MB 이상입니다!!");
        return;
    }

    if(!myFile.type.includes("jpeg") && !myFile.type.includes("png")){
        alert("jpeg 또는 png만 업로드 가능합니다!");
        return;
    }

그러면 업로드과정에서 이미지까지 데이터베이스에 넣어보자

import { gql, useMutation } from "@apollo/client";
import { ChangeEvent, useState } from "react";

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 ImageUploadPage() {
  const [myWriter, setMyWriter] = useState("");
  const [myPapssword, setPapssword] = useState("");
  const [myTitle, setMyTitle] = useState("");
  const [myContents, setContents] = useState("");
  const [myImages, setmyImages] = useState<string[]>([]);
  const [createBoard] = useMutation(CREATE_BOARD);
  const [uploadFile] = useMutation(UPLOAD_FILE);

  function onChangeMyWriter(event: ChangeEvent<HTMLInputElement>) {
    setMyWriter(event.target.value);
  }

  function onChangemyPapssword(event: ChangeEvent<HTMLInputElement>) {
    setPapssword(event.target.value);
  }

  function onChangemyTitle(event: ChangeEvent<HTMLInputElement>) {
    setMyTitle(event.target.value);
  }

  function onChangemyContents(event: ChangeEvent<HTMLInputElement>) {
    setContents(event.target.value);
  }

  async function onClickSubmit() {
    const result = await createBoard({
      variables: {
        createBoardInput: {
          writer: myWriter,
          password: myPapssword,
          title: myTitle,
          contents: myContents,
          images: myImages,
        },
      },
    });
    console.log(result);
  }

  async function onChangeFile(event: ChangeEvent<HTMLInputElement>) {
    const myFile = event.target.files?.[0];
    console.log(myFile);

    if (!myFile?.size) {
      alert("파일이 없습니다!");
      return;
    }

    if (myFile?.size > 5 * 1024 * 1024) {
      alert("파일용량이 5MB 이상입니다!!");
      return;
    }

    if (!myFile.type.includes("jpeg") && !myFile.type.includes("png")) {
      alert("jpeg 또는 png만 업로드 가능합니다!");
      return;
    }

    const result = await uploadFile({
      variables: {
        file: myFile,
      },
    });
    console.log(result.data.uploadFile.url);
    setmyImages([result.data.uploadFile.url]);
  }

  return (
    <>
      <h1>이미지 업로드 !!!</h1>
      <input type="file" onChange={onChangeFile} />
      <br />
      작성자: <input type="text" onChange={onChangeMyWriter} />
      <br />
      비밀번호: <input type="password" onChange={onChangemyPapssword} />
      <br />
      제목: <input type="text" onChange={onChangemyTitle} />
      <br />
      내용: <input type="text" onChange={onChangemyContents} />
      <br />
      <button onClick={onClickSubmit}>등록하기</button>
    </>
  );
}

이렇게 되면 데이터베이스의 writer, title, password와 같이 img또한 배열로 잘 저장된 것이다.

와우

++ 이미지 미리보기 기능을 더 이쁘게 꾸며보자
사실 이 창은 바꿀 수 없어서 이걸 가리고 새로 만든것과 연결시켜줘야한다.

import { gql, useMutation } from "@apollo/client";
import { ChangeEvent, useRef, useState } from "react";

const UPLOAD_FILE = gql`
  mutation uploadFile($file: Upload!) {
    uploadFile(file: $file) {
      url
    }
  }
`;

export default function ImageUploadPage() {
  const fileRef = useRef<HTMLInputElement>(null);
  const [uploadFile] = useMutation(UPLOAD_FILE);
  const [myImages, setmyImages] = useState<string[]>([]);
  async function onChangeFile(event: ChangeEvent<HTMLInputElement>) {
    const myFile = event.target.files?.[0];
    console.log(myFile);

    const result = await uploadFile({
      variables: {
        file: myFile,
      },
    });
    console.log(result.data.uploadFile.url);
    setmyImages([result.data.uploadFile.url]);
  }
  function onClickMyimage() {
    fileRef.current?.click();
  }

  return (
    <>
      <div
        style={{ width: "50px", height: "50px", backgroundColor: "gray" }}
        onClick={onClickMyimage}
      >
        이미지 선택
      </div>
      <img src={`https://storage.googleapis.com/${myImages[0]}`} />
      <input
        style={{ display: "none" }}
        type="file"
        ref={fileRef}
        onChange={onChangeFile}
      />
      ;
    </>
  );
}

이렇게하면 파일도 미리볼 수 있다. 짜잔-

이제 이거 콤보로 적용시키면...된다.

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

0개의 댓글