프로필 이미지 업로드 하기

oweaj·2023년 9월 16일
0

React

목록 보기
4/4

로그인한 사용자의 계정 프로필에 이미지를 편집하는 기능을 구현해 보면서 해당 이미지 파일을 드래그 앤 드롭으로도 변경할 수 있도록 업로드를 만들어보았다.

FileReader

FileReader 객체는 웹 애플리케이션이 비동기적으로 데이터를 읽기 위하여 읽을 파일을 가리키는 File 혹은 Blob 객체를 이용해 파일의 내용을 읽고 사용자의 컴퓨터에 저장하는 것을 가능하게 해준다.

Blob은 Binary Large OBject의 약자로 주로 이미지, 비디오, 사운드 등과 같은 멀티미디어 등의 데이터를 다룰 때 사용한다.


이미지 업로드 하기

// 파일 선택 업로드
const handleUpload = (e) => {
	const file = e.target.files[0]
	const fileReader = new FileReader();

    fileReader.onload = () => {
    	image(fileReader.result);
    };

    fileReader.readAsDataURL(file);
};

input type의 file을 이용해서 이미지 파일을 등록하고 e.target.files를 콘솔찍어보면 아래와 같이 FileList 객체를 불러온다.

여기서 사용자가 등록한 이미지 파일은 e.target.files[0]으로 접근해서 readAsDataURL을 이용해 주어진 파일의 데이터 URL의 파일 내용을 Base64로 인코딩하여 URL 문자열 형태로 읽어준다.

등록된 이미지 파일 읽기가 성공적으로 이루어지면 onload 이벤트가 실행되며 이미지 파일 데이터들이 저장된 result를 활용하면 된다.

파일 드래그 추가하기

// drag 이벤트 
const handleDrag = (e: DragEvent<HTMLLabelElement>, type: string) => {
	e.preventDefault();
    e.stopPropagation();

    if (type === "enter") {
      setActive(true);
    } else if (type === "leave") {
      setActive(false);
    } else if (type === "drop") {
      setIsDrag(true);
      handleUpload(e.dataTransfer.files[0]);
    }
};

파일을 영역에 drop을 하게되면 해당 파일의 새 창이 열리는데 이 기본 동작을 e.preventDefault()이 중지시키는 역할을 하고 e.stopPropagation()는 드래그 앤 드롭 요소에 대한 이벤트가 상위 요소로 전파되지 않도록 하고 다른 부모 요소에 드래그 앤 드롭 이벤트를 방해하지 않도록 도와준다.

// 파일 선택 업로드
const handleUpload = (file: any) => {
    const fileReader = new FileReader();

    fileReader.onload = () => {
      image(fileReader.result);
    };

    fileReader.readAsDataURL(file);
};

처음 윗부분에 나온 파일 선택 업로드 코드 함수에 파라미터를 설정해 업로드 함수로 활용했다.

프로필 업로드 마무리

const ProfileModal = ({ image }: imgInfo) => {
  const [active, setActive] = useState(false);
  const [isDrag, setIsDrag] = useState(false);

  // 파일 선택 업로드
  const handleUpload = (file: any) => {
    const fileReader = new FileReader();

    fileReader.onload = () => {
      image(fileReader.result);
    };

    fileReader.readAsDataURL(file);
  };

  // 이미지 파일 onchange
  const handleChange = (e: ChangeEvent<HTMLLabelElement> | any) => {
    handleUpload(e.target.files[0]);
  };

  // 이미지 파일 드래그
  const handleDrag = (e: DragEvent<HTMLLabelElement>, type: string) => {
    e.preventDefault();
    e.stopPropagation();

    if (type === "enter") {
      setActive(true);
    } else if (type === "leave") {
      setActive(false);
    } else if (type === "drop") {
      setIsDrag(true);
      handleUpload(e.dataTransfer.files[0]);  // dataTransfer 드래그 된 파일 처리 
    }
  };

  return (
    <label
      className={`labelImg hover:border-gray-500 ${isDrag ? "bg-green-100" : active ? "border-gray-500 bg-gray-100" : "border-gray-300"}`}
      onChange={handleChange}
      onDrop={(e) => handleDrag(e, "drop")}
      onDragEnter={(e) => handleDrag(e, "enter")}
      onDragLeave={(e) => handleDrag(e, "leave")}
      onDragOver={(e) => handleDrag(e, "over")}
    >
      <input type="file" name="file" className="hidden" />
      <RiImageAddLine className="w-24 h-16" />
      <p className="text-lg font-medium">{isDrag ? "이미지 파일 drag 완료!!" : "여기를 클릭 또는 이미지 파일을 드롭하세요."}</p>
    </label>
  );
};

profile
생각말고 실천 🚀

0개의 댓글