여러 사이트에서 볼 수 있는 이미지 등록 기능!
보통 프로필 편집 페이지에서 가장 많이 사용되는 듯 하다.
전체 코드는 가장 밑에 있다.
이번에 진행하는 팀 프로젝트는 뉴스피드 사이트 만들기 인데, 우리 조는 요리 레시피를 공유하는 컨셉의 사이트를 만들기로 했다. 나는 그 중에서도 게시글 작성 파트를 담당하게 되어서! 요리 사진을 올리는 부분에 미리보기를 넣었다.
<ImageBox>
<UploadButton>이미지 업로드</UploadButton>
<input
type="file"
accept="image/*"
style={{ display: "none" }}
/>
<MinText>1:1 비율의 사진이 예쁘게 나와요❤️</MinText>
</ImageBox>

처음에 작성했던 코드는 이랬다. 여기저기 검색해봤는데, input을 숨긴 다음 div나 버튼에 useRef로 인풋을 연결하는? 형태의 코드를 가장 많이 사용하는 것 같아서 일단 display:none; 으로 인풋을 숨겨놓고 버튼을 세팅했다.
const fileInputRef = useRef(null);
const handleUploadButtonClick = () => {
fileInputRef.current.click();
};
return 아래 연결
<ImageBox onClick={handleUploadButtonClick}>
<input
type="file"
accept="image/*"
ref={fileInputRef}
style={{ display: "none" }}
/>
참고로 useRef는 담고 있는 값이 변경되어도 리랜더링 되지 않는다. 반대로 리랜더링 되어도 기존에 담고 있던 값이 변경되지는 않는다.
// useRef는 current 라는 프로퍼티를 기본적으로 갖고 있다.
const fileInputRef = useRef(null);
= { current : null } 형태
ref={fileInputRef}
= { current : <input> }
이렇게 요소가 로딩될 때 참조값을 변경해주어도 이것 때문에 리랜더링 되거나 하지 않는다는 것이다. 마찬가지로 연결해놓은 input은 페이지가 리랜더링 될 때에도 변경되지 않는다.
여기까진 순조로웠다...
미리보기 기능을 구현할 때 사용자가 등록한 이미지를 URL 형태로 바꾸어서 그걸 img src로 보여줘야 하는데 이게 검색을 해서 코드를 30분 동안 들여보고 있어도 이해가 잘 안 됐다. 완전 처음보는 게 나와서🥹
const reader = new FileReader();
바로 요 FileReader 라는 자식이다.
FileReader 객체는 웹 애플리케이션이 비동기적으로 데이터를 읽기 위하여 읽을 파일을 가리키는File 혹은 Blob 객체를 이용해 파일의 내용을(혹은 raw data버퍼로) 읽고 사용자의 컴퓨터에 저장하는 것을 가능하게 해줍니다.
const [imageSrc, setImageSrc] = useState(null);
const handleImageUpload = (event) => {
const file = event.target.files[0];
if (file) {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onloadend = () => {
setImageSrc(reader.result);
};
}
}
<ImageBox onClick={handleUploadButtonClick}>
{imageSrc
? (<ImagePreview src={imageSrc} alt="미리보기 이미지" />)
: (<UploadButton>이미지 업로드</UploadButton>)}
<input
type="file"
accept="image/*"
ref={fileInputRef}
style={{ display: "none" }}
onChange={handleImageUpload}
/>
<MinText>1:1 비율의 사진이 예쁘게 나와요❤️</MinText>
</ImageBox>
위에서부터 한 줄씩 설명하자면
const [imageSrc, setImageSrc] = useState(null);
- 이미지 주소를 저장하는 state
- 사용자가 이미지를 재업로드 하는 경우에 미리보기 이미지가 변경된다.
- return 문에서 이미지 div를 생성하는 조건이기도 함.
const handleImageUpload = (event) =>
- 파일을 읽고 url을 state에 저장하는 함수
const file = event.target.files[0];
- 사용자가 업로드한 files 배열의 0번 인덱스 항목 (한 장만 올리는 거라 0번 인덱스 그냥 쓰면 됨)
if (file)
- file 이 존재한다면 = 사용자가 선택한 이미지가 정상적으로 올라왔다면
const reader = new FileReader();
- FileReader 객체 생성
reader.readAsDataURL(file);
- Starts reading the contents of the specified Blob, once finished, the result attribute contains a data: URL representing the file's data.
- 지정한 데이터를 읽고 이를 URL 형태로 반환하는 메소드.
reader.onloadend = () =>
- 이 이벤트는 읽기 동작이 성공적으로 완료 되었을 때마다 발생합니다.
- readAsDataURL 메소드로 파일 URL을 로딩하면 실행되는 함수.
setImageSrc(reader.result);
- FileReader.result : 파일의 컨텐츠. (읽기 작업이 완료되고 읽기 작업의 초기화에 사용한 방식으로 결정된 데이터의 포맷이 정해진 후에 유효합니다.)
- readAsDataURL 에서 반환받은 URL을 state로 전달.
imageSrc ? ImagePreview : UploadButton
- imageSrc 가 존재하는 경우 src = imageSrc 인 미리보기 div를 보여준다.
- imageSrc 가 존재하지 않으면 기존에 있던 업로드 버튼을 보여준다.
const MainImage = () => {
const [imageSrc, setImageSrc] = useState(null);
const fileInputRef = useRef(null);
const handleImageUpload = (event) => {
const file = event.target.files[0];
if (file) {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onloadend = () => {
setImageSrc(reader.result);
};
}
};
const handleUploadButtonClick = () => {
fileInputRef.current.click();
};
return (
<>
<ImageBox onClick={handleUploadButtonClick}>
{imageSrc ? (
<ImagePreview src={imageSrc} alt="미리보기 이미지" />
) : (
<UploadButton>이미지 업로드</UploadButton>
)}
<input
type="file"
accept="image/*"
ref={fileInputRef}
style={{ display: "none" }}
onChange={handleImageUpload}
/>
<MinText>1:1 비율의 사진이 예쁘게 나와요❤️</MinText>
</ImageBox>
</>
);
};