최근 아래처럼 트위터 계정의 프로필 정보를 불러오는 폼을 만들고 있다. 이때 불러온 프로필 이미지를 사용자가 직접 변경할 수 있게 하는 기능을 추가했다!
그래서 프로필 이미지를 업로드 할 수 있는 기능에 대해서 포스팅해보려고 한다.
react-hook-form으로 폼을 관리하고 있었기에 react-hook-form을 활용해서 만들기로 했다. 프로필 이미지 업로더를 만들 때 아래 링크를 참고했다.
참고: https://dreamix.eu/insights/uploading-files-with-react-hook-form/
결과부터 미리 보자면, 이렇게 선택한 이미지로 프로필을 업데이트 할 수 있다.
프로필 사진을 업로드하는 부분의 코드는 총 세 영역으로 나누어져있다.
프로필 사진이 보이는 부분
업로드할 이미지를 선택하는 부분
display: none
)hiddenInputRef.current?.click()
)이미지 변경하기 버튼
import { useRef } from 'react';
import { useForm } from 'react-hook-form';
const hiddenInputRef = useRef<HTMLInputElement | null>(null);
const twitterImage = watch('twitterImage');
// ref를 register에서 따로 꺼내기
const { ref: registerRef, ...rest } = register('twitterImage');
const fileUploadHandler = (e: React.ChangeEvent<HTMLInputElement>) => {
// 업로드한 파일 가져오기
const file = e.target.files?.[0];
if (file) {
const binaryData = [file];
const urlImage = URL.createObjectURL(new Blob(binaryData, ㅇ{ type: 'image' }));
// 이미지 URL을 src로 설정
setValue('twitterImage', urlImage);
}
};
return (
{/* 프로필 사진이 보이는 부분 */}
{twitterImage && <img src={twitterImage} alt="프로필 이미지" className="w-40 h-40" />}
{/* 업로드할 이미지를 선택하는 부분 */}
<input
{...rest}
type="file"
ref={(e) => {
// registerRef를 통해 react-hook-form이 이 input의 ref를 추적할 수 있다
registerRef(e);
// hiddenInputRef를 사용하여 외부에서 이 input에 접근할 수 있다
hiddenInputRef.current = e;
}}
className="hidden input h-10 w-full max-w-xs shadow-sm placeholder:text-xs"
name="twitterImage"
onChange={fileUploadHandler}
/>
{/* 이미지 변경하기 버튼 부분*/}
<button
type="button"
className="btn btn-success text-white btn-sm text-xs w-1/2 mt-1"
onClick={() => {
hiddenInputRef.current?.click();
}}
>
이미지 변경하기
</button>
)
이미지를 변경하면 자동으로 폼이 제출되는 오류가 있었다. 그래서 차근차근 살펴보았다.
1. fileUploadHandler 안의 코드를 주석처리 해보았을 때, 이 문제는 아니었다.
const fileUploadHandler = (e: React.ChangeEvent<HTMLInputElement>) => {
console.log('fileUploadHandler 실행');
// // 업로드한 파일 가져오기
// const file = e.target.files?.[0];
// // 파일을 로컬에서 url로 변환
// // TODO: 서버에 프로필 파일 업로드해서 URL 받아오기
// if (file) {
// const binaryData = [file];
// const urlImage = URL.createObjectURL(new Blob(binaryData, { type: 'image' }));
// // 이미지 URL을 src로 설정
// setValue('twitterImage', urlImage);
// }
};
<button
className="btn btn-success text-white btn-sm text-xs w-1/2 mt-1"
// onClick={() => {
// hiddenInputRef.current?.click();
// }}
>
type=button
이라고 안써줘서 자동으로 제출이 됐던 거였다. button은 따로 설정하지 않아도 기본적으로 type=submit
속성을 가지고 있기 때문이다.<button
type="button"
className="btn btn-success text-white btn-sm text-xs w-1/2 mt-1"
onClick={() => {
hiddenInputRef.current?.click();
}}
>
하나의 input에 두개의 ref가 접근하는 것이 이해가 가지 않았다. 지금도 정확히 이해가 가지는 않지만 아래처럼 이해하고 있다.
<input
{...rest}
type="file"
ref={(e) => {
registerRef(e);
hiddenInputRef.current = e;
}}
onChange={fileUploadHandler}
/>
버튼 뿐만 아니라 버튼 외부에 있는 이미지나 제목을 눌렀을 때도 파일을 선택하는 창이 열리는 오류가 있었다. 이는 라벨로 제목만이 아니라 이미지와 버튼까지 잘못 감싸고 있어서 생기는 오류였다.
로컬에 있는 이미지를 선택하여 프로필 이미지를 변경하는 것에 성공했다!
폼을 관리하기 위한 라이브러리 react-hook-form을 최근 배우기 시작했다. 실제로 폼을 만들면서 직접 사용해보며 많이 알아가고 있는 것 같아 기쁘다!
react-hook-form으로 프로필 이미지를 업로드하는 포스팅을 한국어로는 찾지 못해서 자세하게 써보았다. 다음에는 이번에 만든 프로필 이미지 업로더 부분을 포함하는 전체 폼에 대해서 포스팅해보고 싶다.