[리팩토링] 파이어베이스 서비스 코드 분리

2023. 9. 15. 13:55Project Tours/Tour on Plantopia

프로젝트 중에는 서비스 코드가 혼재 되어 있었다. 이미 데이터 관련 부분을 api 폴더에 분리했지만 이미지 부분를 처리하는 부분은 남아 있었는데 사실 이 코드는 그냥 따온 것이라 어떻게 해야할지 고민이 되었다. 일단 코드를 이해하는 것이 첫 번째 과제이다.

  const cleanFileName = (fileName: string) => {
    const cleanedName = fileName.replace(/[^\w\s.-]/gi, '');
    return cleanedName;
  };

  const readFileAsDataURL = (file: File) => {
    return new Promise<string>((resolve, reject) => {
      const reader = new FileReader();
      reader.onload = e => resolve(e.target?.result as string);
      reader.onerror = reject;
      reader.readAsDataURL(file);
    });
  };

  const handleFileSelect = async (
    event: React.ChangeEvent<HTMLInputElement>,
  ) => {
    const file = event.target.files?.[0];
    if (!file) return;

    try {
      const previewUrl = await readFileAsDataURL(file);
      setPreviewImg(previewUrl);
      const storagePath = `myplant_imgs/${cleanFileName(file.name)}`;
      const imageRef = ref(storage, storagePath);
      const snapshot = await uploadBytes(imageRef, file);
      const url = await getDownloadURL(snapshot.ref);
      setImgUrl(url);
    } catch (error) {
      console.error('파일 업로드 에러:', error);
    }
    event.target.value = '';
  };

https://developer.mozilla.org/ko/docs/Web/API/FileReader/readAsDataURL

 

FileReader.readAsDataURL() - Web API | MDN

readAsDataURL 메서드는 컨텐츠를 특정 Blob 이나 File에서 읽어 오는 역할을 합니다. 읽어오는 read 행위가 종료되는 경우에, readyState (en-US) 의 상태가 DONE이 되며, loadend (en-US) 이벤트가 트리거 됩니다.

developer.mozilla.org

 

모르는 내용은 webAPI 내용이었고 방식이 정해진 사진 file 올리기 였다. 이전에 미디어 파일 다룬 경험이 있어서 그런지 이제 webAPI하고 가까워진 느낌이다.

 

  const handleImg = async (event: React.ChangeEvent<HTMLInputElement>) => {
    const file = event.target.files?.[0];
    if (!file) return;
    const imgUrls = await handleFileSelect(file);
    if (!imgUrls) return;
    setPreviewImg(imgUrls?.previewUrl);
    setImgUrl(imgUrls?.url);
    event.target.value = '';
  };

컴포넌트에는 이것만 넣었다. 중요한 것은 상태 관리는 컴포넌트 내에서만하고 나머지 데이터 처리는 외부 함수를 통해 처리한다는 것이다.

const cleanFileName = (fileName: string) => {
  const cleanedName = fileName.replace(/[^\w\s.-]/gi, '');
  return cleanedName;
};

const readFileAsDataURL = (file: File) => {
  return new Promise<string>((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = e => resolve(e.target?.result as string);
    reader.onerror = reject;
    reader.readAsDataURL(file);
  });
};

export const handleFileSelect = async (file: File) => {
  try {
    const previewUrl = await readFileAsDataURL(file);
    const storagePath = `myplant_imgs/${cleanFileName(file.name)}`;
    const imageRef = ref(storage, storagePath);
    const snapshot = await uploadBytes(imageRef, file);
    const url = await getDownloadURL(snapshot.ref);
    return { previewUrl, url };
  } catch (error) {
    console.error('파일 업로드 에러:', error);
  }
};

외부 함수로 다 빼주었다. 고민이 되는 것은 new Promise를 await으로 바꿔야하나 싶었는데 mdn 예시에도 그대로 나와있어서 그냥 놔두었다. 정확히 바꿀 수 있을지는 미지수.