본문 바로가기
나도 공부한다/React

React 파일 업로드시 확장자 제한하기 (input accept에 의존하면 안되는 이유)

by 꾸빵이 2024. 5. 19.

문제 상황

분명 이렇게 accept로 제한해뒀는데... 다른 파일도 잘 업로드 되는 문제가 발생했다.

 <input
          type="file"
          hidden
          accept="image/* .jpg, .png, .jpeg"
          ref={fileInput}
          onChange={onChange}
        />

 

 

해결 과정

accept 속성은 브라우저에서 사용자가 파일 선택 대화상자를 통해 선택할 수 있는 파일 유형을 제한한다.

따라서 처음에 파일 업로드 버튼을 눌렀을 때 나오는 파일창에서는 형식이 '사용자 지정파일(accept로 설정해둔 형식들)'로 제한되어있다. 하지만 이렇게 사용자가 형식을 '모든 파일'로 바꿔버리면  accept 설정이 소용없어진다. 

 

TCPschool에서도 accept 속성을 유효성 검사를 도구로 사용해서는 안 되며, 업로드된 파일은 서버에서 검증되어야 한다고 써놨다.

 

 

결과

따라서 이렇게 클라이언트쪽에서 확장자를 직접 검사하는 함수를 만들거나, 서버에서 특정 파일만 받을 수 있게 처리해야한다.

이미 서버에서도 형식을 제한해놨지만 맞지 않는 형식인데도 API를 호출하는건 자원 낭비라고 생각해서 클라이언트단에서도 유효성 검사를 하는 코드를 작성했다.

  const MAX_IMAGE_SIZE_BYTES = 1024 * 1024 * 10
  const ALLOWED_EXTENSIONS = ['jpg', 'jpeg', 'png']

  const isValidExtension = (filename: string) => {
    const extension = filename.split('.').pop()?.toLowerCase()
    return extension ? ALLOWED_EXTENSIONS.includes(extension) : false
  }

  const handleImgChange = async (file: File) => {
    if (!isValidExtension(file.name)) {
      alert(
        '허용되지 않은 파일 형식입니다. jpg, jpeg, png 파일만 업로드해주세요.',
      )
      return
    }

    if (file.size > MAX_IMAGE_SIZE_BYTES) {
      alert('파일 크기는 10MB 이하여야합니다.')
      return
    }

    if (file instanceof File) {
      try {
        const response = await imageChange(file)
        if (response.status === 200) {
          const newImg = response.data.profileImgUrl

          setUserDetails((prevState) => {
            if (prevState) {
              return {
                ...prevState,
                profileImg: newImg,
              }
            }
            return prevState
          })
        }
      } catch (error) {
        throw error
      }
    }
  }

  const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    if (e.target.files) {
      const file = e.target.files[0]
      if (file) {
        handleImgChange(file)
      }
    }
  }