데이터베이스의 저장공간은 한정되어 있기 때문에 모든 이미지 파일을 저장하는 것은 불가능하다. 따라서 상대적으로 큰 용량을 담을 수 있는 데이터베이스, 클라우드 스토리지
를 이용하여 파일 전송하고 백엔드 데이터베이스에는 해당 파일의 주소값을 저장하여 필요할 때마다 주소를 불러와서 사용하는 방식으로 이미지를 업로드한다.
브라우저에서 사진등록을 누르면 백엔드에 있는 uploadFile API가 작동하여 파일을 클라우드로 파일을 전송하게 된다. 그럼 클라우드에서 백엔드로 이미지를 조회할 수 있는 이미지 주소
를 전달하여 데이터베이스에 저장된다.
그렇게 브라우저에서 이미지를 요청할 때마다 파일 자체를 불러오는게 아닌 백엔드에 저장된 이미지 주소
를 통해 클라우드에 저장되어 있는 이미지를 조회하게 된다.
🚀 아래의 라이브러리 설치 먼저!
yarn add apollo-upload-client
yarn add @types/apollo-upload-client --dev
// app.tsx파일
// import 추가하기
import {createUploadLink} from "apollo-upload-client"
// 세팅 함수 부분
const uplodLink = createUploadLink({
uri : "백엔드 주소"
})
const client = new ApolloClient({
link : ApolloLink.from([uplodLink]),
cache : new inMemoryCache(),
})
react에서는 getElementId가 아닌 "useRef"로 HTML태그에 접근 할 수 있다. useState나 useEffet처럼 react에서 가져와야 사용이 가능하며 아래의 fileRef를 태그에 연결해주어 해당 태그에서 불러와 사용할 수 있다.
import { useState, useRef } from "react";
import { gql, useMutation } from "@apollo/client";
import { checkValidationFile } from "../../../src/commons/libraries/validationFile";
import type { ChangeEvent } from "react";
import type {
IMutation,
IMutationUploadFileArgs,
} from "../../../src/commons/types/generated/types";
const UPLOAD_FILE = gql`
mutation uploadFile($file: Upload!) {
uploadFile(file: $file) {
url
}
}
`;
export default function ImageUploadPage(): JSX.Element {
const [imageUrl, setImageUrl] = useState("");
const fileRef = useRef<HTMLInputElement>(null);
const [uploadFile] = useMutation<
Pick<IMutation, "uploadFile">,
IMutationUploadFileArgs
>(UPLOAD_FILE);
const onChangeFile = async (
event: ChangeEvent<HTMLInputElement>
): Promise<void> => {
const file = event.target.files?.[0]; // 배열로 들어오는 이유: <input type="file" multiple /> 일 때, 여러개의 파일 드래그 가능
console.log(file);
const isValid = checkValidationFile(file);
if (!isValid) return;
const result = await uploadFile({ variables: { file } });
console.log(result.data?.uploadFile.url);
setImageUrl(result.data?.uploadFile.url ?? "");
};
const onClickImage = (): void => {
fileRef.current?.click();
};
return (
<>
<div
style={{
margin: "20px",
width: "100px",
height: "100px",
backgroundColor: "crimson",
borderRadius: "20px",
textAlign: "center",
color: "white",
fontWeight: "bold",
paddingTop: "25px",
cursor: "pointer",
}}
onClick={onClickImage}
>
PIC <br />
UPLOAD
</div>
<input
style={{ display: "none" }}
type="file"
onChange={onChangeFile}
ref={fileRef}
accept="image/jpeg,image/png"
/>
<img src={`https://storage.googleapis.com/${imageUrl}`} />
</>
);
}
export const checkValidationFile = (file?: File): boolean => {
if (typeof file === "undefined") {
alert("파일이 없습니다!");
return false;
}
if (file?.size > 5 * 1024 * 1024) {
alert("파일 용량이 너무 큽니다. (제한: 5MB");
return false;
}
if (!file.type.includes("jpeg") && !file.type.includes("png")) {
alert("jpeg 또는 png 파일만 업로드 가능합니다!");
return false;
}
return true;
};
💡TIP
사이즈 보는 방법
MB(메가바이트) KB(키로바이트) B(바이트)
1024B = 1KB
1024KB = 1MB