file 요소란?
file 유형의 <input> 요소에는 저장 장치의 파일을 하나 혹은 여러 개 선택할 수 있습니다. 그 후, 양식을 제출해 서버로 전송하거나, File API를 사용한 JavaScript 코드로 조작할 수 있습니다.
<input type="file">
<input type="file"> - HTML: Hypertext Markup Language | MDN
file 유형의 <input> 요소에는 저장 장치의 파일을 하나 혹은 여러 개 선택할 수 있습니다. 그 후, 양식을 제출해 서버로 전송하거나, File API를 사용한 JavaScript 코드로 조작할 수 있습니다.
developer.mozilla.org
파일 업로드 기능 구현 하기
1. 파일 요소 추가
- 가장 기본 파일 업로드 요소 추가
<input type="file">
2. 파일 스타일 적용
- Label 사용
1. Label 요소를 추가해준다. label의 htmlFor과 input의 id는 동일해야 한다.
<label htmlFor="avatar">파일 선택</label>
<input type="file" id="avatar" />
2. css로 <input type="file"> 태그를 display: none 속성으로 숨김 처리하고 Label을 원하는 방식으로 커스텀 한다.
label {
width: 100px;
padding: 10px;
background-color: #e2e2e2;
}
#avatar {
display: none
}
3. 선택한 파일을 저장하는 state 변수를 생성한다음, onChange 핸들러 이벤트를 추가한다.
import React, { useState } from 'react';
const FileUploadWithLabel: React.FC = () => {
const [selectedFile, setSelectedFile] = useState<File | null>(null);
// 파일 선택 핸들러
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files ? e.target.files[0] : null; // 선택한 파일
setSelectedFile(file);
};
return (
<div>
<label htmlFor="avatar">
파일 선택
</label>
<input type="file" id="avatar" onChange={handleFileChange} />
<div>
{selectedFile ? (
<p>선택한 파일: {selectedFile.name}</p>
) : (
<p>파일이 선택되지 않았습니다.</p>
)}
</div>
</div>
);
};
export default FileUploadWithLabel;
- Button 과 onClick 사용
1. 버튼 요소를 추가하고 onclick 핸들러를 추가한다.
// 버튼 클릭 시 파일 선택 창 열기
const handleUpload = () => {
fileInputRef.current?.click();
};
<button type="button" onClick={handleUpload}>
파일 선택
</button>
<input type="file" id="avatar" />
2. css로 <input type="file"> 태그를 display: none 속성으로 숨김 처리하고 button을 원하는 방식으로 커스텀 한다.
button {
width: 100px;
padding: 10px;
background-color: #e2e2e2;
}
#avatar {
display: none
}
3. 선택한 파일을 저장하는 state 변수를 생성한다음, onChange 핸들러 이벤트를 추가한다.
import React, { useState, useRef } from 'react';
const FileUploadWithButton: React.FC = () => {
const [selectedFile, setSelectedFile] = useState<File | null>(null);
const fileInputRef = useRef<HTMLInputElement>(null);
const handleUpload = () => {
fileInputRef.current?.click();
};
// 파일 선택 핸들러
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files ? e.target.files[0] : null; // 선택한 파일
setSelectedFile(file);
if(fileInputRef.current) {
fileInputRef.current.value = '';
}
};
return (
<div>
<button type="button" onClick={handleUpload}>
파일 선택
</button>
<input type="file" id="avatar" ref={fileInputRef} onChange={handleFileChange} />
<div>
{selectedFile ? (
<p>선택한 파일: {selectedFile.name}</p>
) : (
<p>파일이 선택되지 않았습니다.</p>
)}
</div>
</div>
);
};
export default FileUploadWithButton;
3. 드래그 앤 드랍 기능 적용
- 나는 버튼으로 작업을 진행하겠다.
- onDragOver : 드래그 이벤트
- onDrop : 파일 드래그 후 드랍시 핸들러
1. 드래그 앤 드랍의 기본 브라우저 이벤트를 취소하는 핸들러 추가
// 드래그 앤 드롭 핸들러
const handleDragOver = (e: React.DragEvent) => {
e.preventDefault();
};
2. 드래그 이벤트 핸들러 추가
const handleDrop = (e: React.DragEvent) => {
e.preventDefault();
const file = e.dataTransfer.files ? e.dataTransfer.files[0] : null;
setSelectedFile(file);
};
3. html 태그에 드래그 영역 추가
<div ref={dropzoneRef} onDragOver={handleDragOver} onDrop={handleDrop}
style={{ border: '2px dashed gray', padding: '20px', marginTop: '10px', textAlign: 'center' }}
>
파일을 이곳에 드래그 앤 드롭하세요
</div>
전체 코드
import React, { useState, useRef } from 'react';
const FileUploadWithButton: React.FC = () => {
const [selectedFile, setSelectedFile] = useState<File | null>(null);
const fileInputRef = useRef<HTMLInputElement>(null);
const dropzoneRef = useRef<HTMLDivElement>(null);
const handleButtonClick = () => {
fileInputRef.current?.click();
};
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files ? e.target.files[0] : null;
setSelectedFile(file);
};
const handleDrop = (e: React.DragEvent) => {
e.preventDefault();
const file = e.dataTransfer.files ? e.dataTransfer.files[0] : null;
setSelectedFile(file);
};
const handleDragOver = (e: React.DragEvent) => {
e.preventDefault();
};
return (
<div>
<button type="button" onClick={handleButtonClick}>
파일 선택
</button>
<input type="file" id="avatar" ref={fileInputRef} onChange={handleFileChange} />
<div ref={dropzoneRef} onDragOver={handleDragOver} onDrop={handleDrop}
style={{ border: '2px dashed gray', padding: '20px', marginTop: '10px', textAlign: 'center' }}
>
파일을 이곳에 드래그 앤 드롭하세요
</div>
<div>
{selectedFile ? (
<p>선택한 파일: {selectedFile.name}</p>
) : (
<p>파일이 선택되지 않았습니다.</p>
)}
</div>
</div>
);
};
export default FileUploadWithButton;
4. 미리보기 기능 적용
- 이미지 경로를 저장하는 state 변수 추가
const [preview, setPreview] = useState<string | null>(null);
- 선택한 파일을 파일 타입으로 변환하여 preview 변수 저장하는 핸들러 추가
const handlePreview = (file: any) => {
if (file) {
const reader = new FileReader();
reader.onloadend = () => {
setPreview(reader.result as string);
};
reader.readAsDataURL(file);
} else {
setPreview(null);
}
}
- handleFileChange, handleDrop 핸들러에 handlePreview() 로직 추가
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files ? e.target.files[0] : null;
setSelectedFile(file);
handlePreview(file);
};
const handleDrop = (e: React.DragEvent) => {
e.preventDefault();
const file = e.dataTransfer.files ? e.dataTransfer.files[0] : null;
setSelectedFile(file);
handlePreview(file);
};
- html 태그에 미리보기 영역 추가
{preview && <img src={preview} alt="미리보기" style={{ maxWidth: '200px', marginTop: '10px' }} />}
전체 코드
import React, { useState, useRef } from 'react';
const FileUploadWithButton: React.FC = () => {
const [selectedFile, setSelectedFile] = useState<File | null>(null);
const [preview, setPreview] = useState<string | null>(null);
const fileInputRef = useRef<HTMLInputElement>(null);
const dropzoneRef = useRef<HTMLDivElement>(null);
const handlePreview = (file: any) => {
if (file) {
const reader = new FileReader();
reader.onloadend = () => {
setPreview(reader.result as string);
};
reader.readAsDataURL(file);
} else {
setPreview(null);
}
}
const handleButtonClick = () => {
fileInputRef.current?.click();
};
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files ? e.target.files[0] : null;
setSelectedFile(file);
handlePreview(file);
};
const handleDrop = (e: React.DragEvent) => {
e.preventDefault();
const file = e.dataTransfer.files ? e.dataTransfer.files[0] : null;
setSelectedFile(file);
handlePreview(file);
};
const handleDragOver = (e: React.DragEvent) => {
e.preventDefault();
};
return (
<div>
<button type="button" onClick={handleButtonClick}>
파일 선택
</button>
<input type="file" ref={fileInputRef} onChange={handleFileChange} />
<div ref={dropzoneRef} onDragOver={handleDragOver} onDrop={handleDrop}
style={{ border: '2px dashed gray', padding: '20px', marginTop: '10px', textAlign: 'center' }}
>
파일을 이곳에 드래그 앤 드롭하세요
</div>
<div>
{selectedFile ? (
<>
<p>선택한 파일: {selectedFile.name}</p>
{preview && <img src={preview} alt="미리보기" style={{ maxWidth: '200px', marginTop: '10px' }} />}
</>
) : (
<p>파일이 선택되지 않았습니다.</p>
)}
</div>
</div>
);