[FE] 새로고침 해도 이미지를 유지하는 방법
FE · FE

[FE] 새로고침 해도 이미지를 유지하는 방법

· 3min read

프로젝트를 진행하던 중, 이미지 업로드 로직을 짜게 되었다.

내가 기존에 objectUrl을 localStorage에 저장하여 사용하던 방식은, 말 그대로 미리보기만 남겨둔 채 이미지 파일은 사라지는 방식이었다.그래서 새로고침을 하면 이미지는 남아있지만, 서버에 전송 했을 때는 null값이 전송되는 이슈가 있었다. (S3에 이미지를 보낼 때는 파일을 보내야 한다.)

1. localStorage에 저장하기

가장 먼저 생각난 방식이지만, 용량 제한이 마음에 걸렸다. 요즘 사진들의 용량을 생각하면 꽤나 많은 유저들이 용량 제한에 걸릴 것이다.

2. 업로드 요청 시 서버에 저장, form Submit시 정식(?) 포스팅 완료

서버로의 부담을 최소화하기 위해 가장 먼저 제외한 방식이다.

3. indexedDB 사용하기

처음 써보는 방식이었다. 하지만, 나름 써보면 좋을 것 같다는 생각이 들었다. 우선 내가 찾아본 indexdDB의 가장 큰 장점은 바로 용량이 엄청나게 크다는 것이었다(브라우저마다 편차는 있지만 디스크 용량의 50% 정도라고 한다.). 또한, localStorage와는 달리 파일을 그대로 저장할 수 있다는 점이다. 이미지 File 객체가 그대로 들어간다는 것이 내게는 가장 매력적이었다.

직접 사용해보기

indexdDB를 사용하는 과정은 아래와 같았다.

  1. DB를 연다.
  2. objectStore를 생성한다.
  3. transaction을 시작하고 readwrite권한 부여
  4. 이미지를 저장한다.

idb 설치하기

$ npm i idb를 통해 설치하면 된다. 내가 설치한 버전은 8.0.1 버전이었다.

DB 열기

  const initDB = async () => {
    if (db) return db;

    db = await openDB(DB_NAME, DB_VERSION, {
      upgrade(db) {
        if (!db.objectStoreNames.contains(STORE_NAME)) {
          db.createObjectStore(STORE_NAME, {
            keyPath: "id",
            autoIncrement: true,
          });
        }
      },
    });
    return db;
  };

이미지 저장하기

  const storeImage = async (file: File) => {
    try {
      const db = await initDB();
      const tx = db.transaction(STORE_NAME, "readwrite");
      const store = tx.objectStore(STORE_NAME);
      const imageData = {
        id: Date.now(),
        file,
      };

      await store.put(imageData);
      await tx.done;
    } catch (error) {
      console.error("이미지를 저장하지 못했습니다. 에러:", error);
      throw error;
    }
  };

이미지 불러오기

  const loadImage = async () => {
    try {
      const db = await initDB();
      const tx = db.transaction(STORE_NAME, "readonly");
      const store = tx.objectStore(STORE_NAME);

      const allImages = await store.getAll();
      await tx.done;
      return allImages.length > 0 ? allImages[0].file : null;
    } catch (error) {
      console.error("이미지를 불러오지 못했습니다. 에러:", error);
      throw error;
    }
  };

이미지 삭제하기

  const deleteImage = async () => {
    try {
      const db = await initDB();
      const tx = db.transaction(STORE_NAME, "readwrite");
      const store = tx.objectStore(STORE_NAME);

      const images = await store.getAll();
      if (images.length > 0) {
        await store.delete(images[0].id);
      }
      await tx.done;
    } catch (error) {
      console.error("이미지를 삭제하지 못했습니다. 에러:", error);
      throw error;
    }
  };

마치며

사실 정확하게 숙지하지는 못했던 것 같다. 우선 문서 살펴보며 동작하는데는 성공했으나, 추가로 공부해야 하는 부분이다.