ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 다양한 파일 확장자 파일 미리보기 지원하기
    React.js, Next.js 2023. 4. 5. 21:25

     

    목차

    • 들어가며
    • text, image, pdf, video 대응하기
    • 리팩터링 with ChatGPT

    들어가며

    실무에서 폼 양식을 작성할 때, 다양한 타입의 파일을 지원하게 됩니다. 아쉽게도 우리는 단순히 객체로만 파일을 가지고 있는 것이 아닌, UX를 위해 파일의 미리 보기를 요구받을 수 있습니다. 따라서 이번에는 다양한 확장자 파일에 대하여 미리 보기를 지원하는 방법 및 로직을 알아봅니다.

    text, image, pdf, video 대응하기

    import React, { useState } from 'react';
    
    const Test = () => {
      const [text, setText] = useState<string>('');
    
      const handleFileInputChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
        const file = event.target.files?.[0];
    
        if (!file) return;
    
        const { type } = file;
    
        if (type === 'text/plain') {
          // Handle text file
          const reader = new FileReader();
          reader.onload = (event: ProgressEvent<FileReader>) => {
            setText(event.target?.result as string);
          };
          reader.readAsText(file);
        } else if (type.startsWith('image/')) {
          // Handle image file
          const url = URL.createObjectURL(file);
    			console.log('--url:', url)
          setText(
            `<img src="${url}" alt="${file.name}" style="width:auto; max-height:200px"  />`
          );
        } else if (type === 'application/pdf') {
          // Handle PDF file
          const url = URL.createObjectURL(file);
    			console.log('--url:', url)
    
          setText(
            `<embed src="${url}" type="application/pdf" style="width:auto; height:200px" />`
          );
        } else if (type === 'video/mp4' || type === 'video/quicktime') {
          const url = URL.createObjectURL(file);
    
          setText(`<video src="${url}" controls style="width:auto; height:200px" />`);
        } else {
          console.log('-- type: ', type);
          // Handle other file types
          setText(`Unsupported file type: ${type} about ${file.name}`);
        }
      };
    
      return (
        <div>
          <input type="file" onChange={handleFileInputChange} />
          <p dangerouslySetInnerHTML={{ __html: text }} />
        </div>
      );
    };
    
    export default Test;
    

    URL.createObjectURL( )

    URL.createObjectURL() 메서드는 지정된 객체를 나타내는 URL을 생성합니다. 이 URL은 해당 객체를 나타내는 URL이지 실제 파일의 위치가 아닙니다. 이 URL을 사용하면 브라우저에서 객체를 로드 및 표시할 수 있습니다. 이 메서드를 사용하여 Blob, File, MediaSource, BlobEvent 등과 같은 객체를 나타내는 URL을 생성할 수 있습니다.

    Blob이란, 파일과 같은 큰 데이터를 처리할 때 사용하는 객체입니다. URL.createObjectURL() 메소드는 Blob 객체를 인자로 넘겨주면 해당 파일을 나타내는 URL을 생성합니다. 이를 통해 브라우저에서 파일을 로드 및 표시할 수 있습니다. Blob 객체는 일반적으로 파일 업로드, 이미지 및 비디오 처리 등에 사용됩니다.

    url

    -- url: blob:<http://localhost:3000/ad078bd3-a5e7-49c7-966b-fde8e0018f9b>
    

    리팩터링 with ChatGPT

    case 1: Refactoring with using switch case statement

    import React, { useState } from 'react';
    
    const Test = () => {
      const [text, setText] = useState<string>('');
    
      const handleFileInputChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
        const file = event.target.files?.[0];
    
        if (!file) {
          return;
        }
    
        const { type } = file;
    
        const handleTextFile = (file: File) => {
          const reader = new FileReader();
          reader.onload = (event: ProgressEvent<FileReader>) => {
            setText(event.target?.result as string);
          };
          reader.readAsText(file);
        };
    
        const handleImageFile = (file: File) => {
          const url = URL.createObjectURL(file);
    
          setText(`<img src="${url}" alt="${file.name}" style="width:auto; max-height:200px"  />`);
        };
    
        const handlePdfFile = (file: File) => {
          const url = URL.createObjectURL(file);
    
          setText(`<embed src="${url}" type="application/pdf" style="width:auto; height:200px" />`);
        };
    
        const handleVideoFile = (file: File) => {
          const url = URL.createObjectURL(file);
    
          setText(`<video src="${url}" controls style="width:auto; height:200px" />`);
        };
    
        const handleUnsupportedFile = (file: File) => {
          console.log('-- type: ', type);
          setText(`Unsupported file type: ${type} about ${file.name}`);
        };
    
        switch (type) {
          case 'text/plain':
            handleTextFile(file);
            break;
          case 'image/jpeg':
          case 'image/png':
          case 'image/gif':
            handleImageFile(file);
            break;
          case 'application/pdf':
            handlePdfFile(file);
            break;
          case 'video/mp4':
          case 'video/quicktime':
            handleVideoFile(file);
            break;
          default:
            handleUnsupportedFile(file);
            break;
        }
      };
    
      return (
        <div>
          <input type='file' onChange={handleFileInputChange} />
          <p dangerouslySetInnerHTML={{ __html: text }} />
        </div>
      );
    };
    
    export default Test;
    

    이 코드에서는,

    • 더 나은 가독성과 유지관리를 위해 다른 파일 형식을 처리하기 위한 논리를 별도의 기능으로 추출했습니다
    • 파일 형식에 따라 호출할 적절한 함수를 결정하기 위해 'switch' 문을 추가했습니다
    • 더 나은 형식 안전을 위해 함수와 변수에 타입스크립트 주석을 추가했습니다

    case 2: Make short with using Record

    import React, { useState } from 'react';
    
    const Test = () => {
      const [text, setText] = useState<string>('');
    
      const handleFileInputChange = async (event: React.ChangeEvent<HTMLInputElement>): Promise<void> => {
        const file = event.target.files?.[0];
    
        if (!file) return;
    
        const { type } = file;
    
        const url = URL.createObjectURL(file);
    
        const fileHandlers: Record<string, () => void> = {
          'text/plain': () => setText(await file.text()),
          'image/jpeg': () => setText(`<img src="${url}" alt="${file.name}" style="width:auto; max-height:200px"  />`),
          'image/png': () => setText(`<img src="${url}" alt="${file.name}" style="width:auto; max-height:200px"  />`),
          'image/gif': () => setText(`<img src="${url}" alt="${file.name}" style="width:auto; max-height:200px"  />`),
          'application/pdf': () => setText(`<embed src="${url}" type="application/pdf" style="width:auto; height:200px" />`),
          'video/mp4': () => setText(`<video src="${url}" controls style="width:auto; height:200px" />`),
          'video/quicktime': () => setText(`<video src="${url}" controls style="width:auto; height:200px" />`),
          default: () => setText(`Unsupported file type: ${type} about ${file.name}`),
        };
    
        const handler = fileHandlers[type] || fileHandlers.default;
        handler();
      };
    
      return (
        <div>
          <input type='file' onChange={handleFileInputChange} />
          <p dangerouslySetInnerHTML={{ __html: text }} />
        </div>
      );
    };
    
    export default Test;
    

     

    이 코드의 단축 버전에서는

    • 딕셔너리 오브젝트를 사용하여 파일 형식을 해당 처리 함수에 매핑함으로써 서로 다른 파일 형식을 처리하는 논리를 단순화했습니다
    • 파일 리더 API를 이용해 텍스트 파일을 처리하는 비동기 기능도 추가했습니다
    • 또한 다양한 이미지 파일 형식을 처리하기 위한 코드를 통합하여 'setText' 호출을 단축했습니다

    결과 보기

    레퍼런스

    URL.createObjectURL() - Web API | MDN

     

    URL.createObjectURL() - Web API | MDN

    URL.createObjectURL() 정적 메서드는 주어진 객체를 가리키는 URL을 DOMString으로 반환합니다. 해당 URL은 자신을 생성한 창의 document가 사라지면 함께 무효화됩니다.

    developer.mozilla.org

    Blob - Web API | MDN

     

    Blob - Web API | MDN

    Blob 객체는 파일류의 불변하는 미가공 데이터를 나타냅니다. 텍스트와 이진 데이터의 형태로 읽을 수 있으며, ReadableStream으로 변환한 후 스트림 메서드를 사용해 데이터를 처리할 수도 있습니다

    developer.mozilla.org

     

    댓글

Designed by Tistory.