[Fend] react-hook-form > 프로필 이미지 업로드

oaksusu·2024년 5월 24일
0

Fend

목록 보기
3/5
post-thumbnail

1. 원하는 기능

  • 프로필이미지 업로드
  • 프로필이미지 미리보기

2. 파일 업로드 마크업

  1. input 태그 display:none; 시키기
  2. label의 htmlFor 속성에 input id값을 주기 (📍이러면, label을 눌러도 파일 업로드 가능함)
  3. button 태그 추가
// 예시
<div>
	<Input
		id="profile"
        label="프로필"
        type="file"
        style={{display: 'none'}}
        {...register("profile")}
	/>
	<img src="" alt="" />
	<button type="button">업로드</button>
</div>

3. 프로필이미지 업로드

3-1. form state에 register함수로 등록하기

: register함수에서 받아온 값중에서 ref를 분리하는 이유는
useRef에 input참조값을 담아서 button을 눌렀을때 input에 접근할 수 있게 하기 위해서!

const {ref: profileRegisterRef, ...rest} = register('profile'); // ----> 1. form state에 등록할 값 중에서 ref 분리

return (
....
<Input
	id="profile"
	label="프로필"
	type="file"
	style={{display: 'none'}}
    {...rest}
	ref={(e) => {
	profileRegisterRef(e); // ----> 2. input 참조를 register함수에서 받아온 ref에 넘겨주기(등록하기)
	}}
/>
<img src="" alt="" />
<button type="button">업로드</button>
... 
)

3-2. button과 input 연결하기

: 업로드 버튼을 눌렀을때
마치 input을 클릭한것처럼 작동할 수 있도록 하기 위해서!

const hiddenInputRef = useRef<HTMLInputElement | null>(null); // 1. button과 input 연결하기 위해서

const handleUploadButton = () => {
	hiddenInputRef.current?.click(); // ----> 4. 마치 input을 클릭한것과 같게
}
  
return (
....
<Input
	id="profile"
	label="프로필"
	type="file"
	style={{display: 'none'}}
    {...rest}
	ref={(e) => {
	profileRegisterRef(e); 
	hiddenInputRef.current = e; // ----> 2. input 참조를 변수(hiddenInputRef)에 담아두기
	}}
/>
<img src="" alt="" />
<button type="button" onClick={handleUploadButton}>업로드</button> // ----> 3. 버튼 클릭하면 
... 
)

4. 프로필이미지 미리보기

4-1. src와 file

: 업로드한 파일은 "미리보기용"과 "파일형태용"으로 구분해둔다.
인코딩을 거치고 백엔드로 전송하게 되면 이미지가 깨지게 되어 Formdata로 전송해야한다고 함.

const [previewImg, setPrevieImg] = useState('');
const [fileImg, setFileImg] = useState<File | null>(null);

(1) src

: 이미지를 사용자한테 미리보기 형태로 보여주기 위해서 필요하다

(2) file

: 업로드한 이미지는 파일 형태로 서버로 보내서 다른 페이지에서도 유저의 프로필 이미지를 사용할 수 있게 해줘야 한다.

4-2. 미리보기 기능 구현 : createObjectUrl 사용

const handleUploadProfile = (event: React.ChangeEvent<HTMLInputElement>) => {
    const file: File | undefined = event.target.files?.[0];
    
    if (file) {      
      setPreviewImg(URL.createObjectURL(file));
    }
  }

5. 알게 된 것들

5-1. input[type="file"] 입력값은 typescript에서 뭐라고 정의할까?

✅ FileList!

// 예시
interface JoinInputs {
  joinId: string;
  joinPassword: string;
  joinPasswordConfirm: string;
  name: string;
  email: string;
  profile: FileList // ===========> 여기!
}

5-2. onChange의 event는 무슨 type일까?

React.ChangeEvent<HTMLInputElement>

const handleUploadProfile = (event: React.ChangeEvent<HTMLInputElement>) => {
	...
}

5-3. useRef의 타입

✅ <HTMLInputElement | null>

const hiddenInputRef = useRef<HTMLInputElement | null>(null);

5-4. onChange가 두번이상 지정되었다는 알림

✅ onChange이벤트를 register함수 다음에 위치시켜야 함

register함수에 onChange가 내장되어있어서 그런것같다.

❌ 오류 나는 케이스

⭕️ 수정한 케이스

※ rest는 const {ref: profileRegisterRef, ...rest} = register('profile'); 여기에서 받아온 값임

5-5. fileReader, createObjectURL

: TIL에서 별도로 정리함!

참고

label태그로 커스터마이징하는 방법
react-hook-form-프로필-이미지-업로더
uploading-files-with-react-hook-form
[React/TS] 이벤트 핸들러 함수의 타입
TS-useRef의-여러가지-타입
typescript-onchange-is-specified-more-than-once-so-this-usage-will-be
createObjectURL을 사용해서 이미지 업로드 후 미리보기
FileReader
[React] FileReader, FileAsDataUrl 를 이용해 프로필 사진 입력받아 미리 보여주고, 서버에 전송하기

profile
삐약

0개의 댓글

관련 채용 정보