티스토리 뷰

▶이미지 파일 업로드 기능 구현 - 아이콘 클릭으로 업로드하고 화면에 띄우자

이번 글에서는 프로젝트 진행 중에 겪었던 어려움을 해결한 방법에 대해 기록해 보려고 합니다 :)

다음 글에서는 업로드 후 해당 이미지 파일과 JSON 데이터를 함께 post 요청을 보내는 것까지 해볼 예정입니다.

 

🔎 이미지 파일 업로드


보통 이미지 파일 업로드를 하기 위해 다음과 같이 작성합니다.

<input type='file' />

해당 input 태그는 보이지 않게 하고 겉에 있는 div에 클릭 이벤트를 주어 이미지 파일 업로드 기능을 구현했는데요.

자세하게는 input 태그에 useRef를 걸어두고 겉에 있는 div에 클릭 이벤트가 발생하면 Ref를 통해 current.click() 이 되도록 코드를 작성했습니다.

input 태그는 보이지 않게 하고 아이콘을 보이도록 함으로써 사용자 입장에서는 아이콘을 누르면 파일 업로드 창이 뜨도록 화면을 구성했습니다.

 

이제 아이콘을 클릭 → 파일 업로드 창 → 이미지 선택

하게 되면 input 태그의 onChange 이벤트 함수가 실행되게 되는데요.

 

이때 이미지를 업로드 해보면서 콘솔을 찍어보면 아래와 같은데요.

사진을 여러 번 업로드 하더라도 그게 자동으로 누적이 되는 것은 아니였습니다.

이미지를 하나 업로드 하더라도 FileList 형태로 값이 들어오기 때문에 이 중에서 실제 이미지 파일에 해당하는 [0]로 state를 변경합니다.

const photoInput = useRef<HTMLInputElement>(null);
  const [imgFile, setImgFile] = useState<any>('');

const handleClick = () => {
  if (photoInput.current) photoInput.current.click();
};

const saveImgFile = (e: React.ChangeEvent) => {
  if (photoInput.current) {
    if (photoInput.current.files) {
      const file = photoInput.current.files[0];
      setImgFile(file);
    };
  };
};

return(
  <div onClick={handleClick}>
    <input
      type="file"
      accept="image/jpg, image/jpeg, image/png"
      onChange={saveImgFile}
      ref={photoInput}
      style={{ display: 'none' }}
     />
    <AiFillCamera
      size="30"
      style={{ position: 'absolute', marginTop: '50px', marginLeft: '-20px' }}
    ></AiFillCamera>
  </div>
)

 

🔎 업로드한 이미지 화면에 띄우기


const reader = new FileReader();
reader.readAsDataURL(file);
reader.onloadend = () => {
  setView(reader.result);
};

 

📌 FileReader() 객체

첫 번째 줄에서 사용한 FileReder 객체는 비동기적으로 파일의 내용을 읽어들이는 데 사용됩니다.

두 번째 줄에서 사용한 메소드 readAsDataURL은 바이너리 파일을 Base64 Encode 문자열로 반환합니다.

세 번째 줄에서 사용한 이벤트 핸들러 onloaded는 읽기 동작이 끝났을 때마다 발생하는 핸들러입니다.

 

최종적으로 읽기 동작이 끝나면 reader.result를 통해 파일의 내용을 반환하여 화면이 바뀔 수 있도록 state를 set해주었습니다.

 

728x90
SMALL

🔎 최종 코드 및 실행 화면


const photoInput = useRef<HTMLInputElement>(null);
  const [imgFile, setImgFile] = useState<any>('');

const handleClick = () => {
  if (photoInput.current) photoInput.current.click();
};

const saveImgFile = (e: React.ChangeEvent) => {
  if (photoInput.current) {
    if (photoInput.current.files) {
      const file = photoInput.current.files[0];
      setImgFile(file);
      
      const reader = new FileReader();
      reader.readAsDataURL(file);
      reader.onloadend = () => {
      setView(reader.result);
      };
    };
  };
};

return(
  <div onClick={handleClick}>
    <input
      type="file"
      accept="image/jpg, image/jpeg, image/png"
      onChange={saveImgFile}
      ref={photoInput}
      style={{ display: 'none' }}
     />
    <AiFillCamera
      size="30"
      style={{ position: 'absolute', marginTop: '50px', marginLeft: '-20px' }}
    ></AiFillCamera>
  </div>
)

실행 화면

728x90
LIST
250x250
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/01   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함