All'alba vincerò

At dawn, I will win!

Javascript

[JS] URL.createObjectURL() / URL.revokeObjectURL(): 임시 URL 생성 / 해제

나디아 Nadia 2024. 8. 26. 00:27

📌 URL.createObjectURL()

: Blob 객체나 File 객체를 사용하여 URL을 생성할 수 있게 해주는 메서드

  • 자바스크립트의 웹 API 중 하나
  • 브라우저에서 동적으로 생성한 파일 데이터를 참조할 필요가 있을 때 사용한다.
  • 특징
    • 일회성 URL
      : URL.createObjectURL()로 생성된 URL은 페이지가 열려 있는 동안만 유효하다.

      브라우저가 해당 페이지를 새로 고침하거나 닫으면 더 이상 사용할 수 없다.
    • 메모리 사용
      : 생성된 URL은 메모리를 사용하므로, 사용이 끝난 후 URL.revokeObjectURL() 메서드를 호출하여 URL을 해제해야 한다.
    • 브라우저 호환성
      : 거의 모든 현대적인 브라우저에서 지원된다.
  • 사용 상황
    • 파일 업로드: 사용자가 업로드한 파일을 미리보기할 때 사용
    • 동적 이미지 생성: JavaScript로 생성한 이미지를 웹 페이지에 표시할 때 사용
    • 파일 다운로드: 클라이언트 사이드에서 생성된 데이터를 파일로 다운로드할 때 사용

 

const objectURL = URL.createObjectURL(object);

 

 

⏩ Blob 객체(Binary Large Object)

: 대량의 이진 데이터(바이너리 데이터)를 표현하는 객체

  • 이미지, 비디오, 텍스트 파일 등 다양한 형태의 데이터를 포함할 수 있다.
  • 이진 데이터로 텍스트, 이미지, 비디오 등 다양한 형식을 저장할 수 있다.
  • 파일 시스템 API와 함께 사용되며, 파일 데이터를 메모리 내에서 처리할 수 있다.

 

 

⏩ File 객체

: Blob 객체의 서브클래스

  • 브라우저에서 파일을 업로드할 때 사용한다.
    사용자가 파일 입력 필드에서 파일을 선택하거나 드래그 앤 드롭할 때 이 객체를 사용
  • 사용자가 선택한 파일에 대한 메타데이터(파일 이름, 파일 크기 등)를 포함하며, Blob의 기능을 제공한다.

 

 

⏩ Object URL

: URL.createObjectURL() 메서드를 사용하여 생성된 URL

  • Blob 객체나 File 객체를 참조한다.
  • 브라우저에서 해당 데이터에 접근할 수 있도록 해준다.
  • 생성된 페이지가 열려 있는 동안만 유효다.
    페이지가 새로 고쳐지거나 닫히면 URL은 더 이상 유효하지 않다.
  • 사용이 끝난 후 URL.revokeObjectURL() 메서드를 호출하여 Object URL을 해제하고 메모리를 정리해야 한다.

 


 

📌 URL.revokeObjectURL()

: 브라우저에서 생성된 Object URL을 해제하는 데 사용되는 메서드

  • Object URL
    : URL.createObjectURL()을 통해 생성된 URL
    ➡︎ 브라우저에서 Blob 객체나 File 객체에 접근할 수 있게 해준다.
    👉 이 URL은 메모리를 사용하기 때문에 더 이상 필요하지 않을 때에는 해제해야 한다.
  • 비동기 작업이 아니라서 호출 즉시 URL을 해제한다.

 

URL.revokeObjectURL(objectURL);
  • 매개변수
    • objectURL
      : createObjectURL()로 생성한 URL

 


 

✅ 사용

1. Object URL 생성

  • URL.createObjectURL() 메서드를 호출하여 Blob 객체로부터 Object URL을 생성
const [file, setFile] = useState(null); // 사용자가 선택한 파일을 저장

const newObjectURL = URL.createObjectURL(file);

 

 

 

2. Object URL 사용

  • 생성된 URL을 img 태그의 src, a 태그의 href,  다른 HTML 요소의 속성에 사용하여 데이터를 표시하거나 다운로드할 수 있다.
const [objectURL, setObjectURL] = useState(''); // objectURL 상태 업데이트

setObjectURL(newObjectURL);

 

 

 

3. Object URL 해제

  • URL.revokeObjectURL() 메서드를 호출하여 더 이상 사용하지 않는 Object URL을 해제
URL.revokeObjectURL(newObjectURL);

 

 

 

예제) 노트 제목, 내용, 커버 이미지 파일을 입력받는 폼 컴포넌트

import { useImmer } from 'use-immer';
import { Form } from 'react-router-dom';
import { AppButton } from '@/components';
import { oneOf } from 'prop-types';

// 타입 검사
NoteForm.propTypes = {
  method: oneOf('get', 'post', 'put', 'patch', 'delete').isRequired,
};


// 초기 상태 정의
const INITIAL_FORM_DATA = {
  title: '',
  description: '',
  thumbnail: null,
};


function NoteForm({ method }) {
  // 현재 상태
  const [formData, setFormData] = useImmer(INITIAL_FORM_DATA);

  // 폼의 입력 필드에서 값이 변경될 때 호출되는 함수
  const handleChange = (e) => {
    // 해당 필드의 값을 새로 입력된 값으로 설정
    setFormData((draft) => {
      draft[e.target.name] = e.target.value;
    });
  };

  // 파일 입력 필드에서 파일이 선택될 때 호출되는 함수
  const handleThumbnail = (e) => {
    // 선택된 파일의 목록
    const [file] = e.target.files;

    // 파일 객체를 참조하는 임시 URL을 생성
    // - 이 URL은 브라우저에서 파일을 직접 접근할 수 있게 해준다.
    const imageUrl = URL.createObjectURL(file);

    // 상태를 업데이트하여 선택한 파일의 URL을 thumbnail 필드에 저장
    setFormData((draft) => {
      draft.thumbnail = imageUrl;
    });
  };

  // 폼을 초기 상태로 리셋하는 함수
  const handleReset = () => {
    // INITIAL_FORM_DATA로 상태를 설정하여 폼 초기화
    setFormData(INITIAL_FORM_DATA);
  };

  // 폼의 제목과 설명이 모두 비어 있지 않은지 확인
  // - 폼의 제출 버튼을 활성화할지 여부를 결정하는 데 사용
  const isReady =
    formData.title.trim().length > 0 && formData.description.trim().length > 0;


  return (
    <Form
      method={method}
      encType="multipart/form-data"
      onReset={handleReset}
      className="flex flex-col gap-5 w-full max-w-sm"
    >
      <div className="flex flex-col gap-1 w-full">
        <label htmlFor="title" className="text-accent">
          제목
        </label>
        <input
          type="text"
          name="title"
          id="title"
          className="border border-accent/20 rounded py-2 px-3 text-accent"
          placeholder="노트 제목"
          defaultValue={formData.title}
          onChange={handleChange}
        />
      </div>
      <div className="flex flex-col gap-1">
        <label htmlFor="description" className="text-accent">
          내용
        </label>
        <textarea
          rows={3}
          cols={20}
          name="description"
          id="description"
          className="border border-accent/20 rounded py-2 px-3 min-h-24 text-accent"
          placeholder="노트 내용을 작성합니다."
          defaultValue={formData.description}
          onChange={handleChange}
        />
      </div>
      <div className="flex flex-col gap-1">
        <span className="text-accent">커버</span>

        <div className="flex items-center justify-center w-full">
          <div className="focus-within:border-accent flex flex-col items-center justify-center w-full h-44 border-2 border-gray-300 border-dashed rounded-lg cursor-pointer bg-gray-50 dark:bg-gray-700 hover:bg-gray-100 dark:border-gray-600 dark:hover:border-gray-500 dark:hover:bg-gray-600">
            <label
              htmlFor="cover"
              className="overflow-hidden cursor-pointer relative w-full flex flex-col items-center justify-center pt-5 pb-6"
            >
              <p className="text-sm text-gray-500 dark:text-gray-400">
                <span className="font-semibold">클릭 업로드</span> 하거나,
                드래그 앤 드롭 하세요.
              </p>
              <p className="text-xs text-gray-500 dark:text-gray-400">
                JPG, PNG, WEBP, AVIF
              </p>
              {formData.thumbnail && (
                <div className="absolute top-0 flex rounded-md justify-center w-full h-full bg-accent/90">
                  <img
                    className="h-full aspect-auto"
                    src={formData.thumbnail}
                    alt=""
                  />
                </div>
              )}
            </label>
            <input
              id="cover"
              name="cover"
              type="file"
              accept="image/jpg,image/png,image/webp,image/avif"
              className="sr-only"
              onChange={handleThumbnail}
            />
          </div>
        </div>
      </div>
      
      <div className="flex gap-2">
        <AppButton
          disabled={!isReady}
          submit
          buttonProps={{ className: 'flex-1' }}
        >
          저장
        </AppButton>
        <AppButton
          reset
          buttonProps={{
            className: 'flex-1 bg-slate-400',
          }}
        >
          취소
        </AppButton>
      </div>
    </Form>
  );
}

export default NoteForm;

 

 


 

 

URL.createObjectURL() - Web API | MDN

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

developer.mozilla.org

 

 

URL.revokeObjectURL() - Web API | MDN

URL.revokeObjectURL() 정적 메서드는 이전에 URL.createObjectURL()을 통해 생성한 객체 URL을 해제합니다. 객체 URL을 더는 쓸 일이 없을 때 사용해서, 브라우저가 이제 해당 객체를 메모리에 들고 있지 않아

developer.mozilla.org