Input 태그 file type
input 태그의 type을 file로 설정해주면 이미지를 포함하여 로컬에 있는 파일들을 입력받을 수 있다.
보통 사진을 업로드하는 용도로 많이 사용한다.
<input type='file' />
그러나 기본 스타일의 경우
다음과 같이 마구 커스텀해주고 싶게 생겼다. .....
다양하게 커스텀해보자 !
파일 선택 버튼만 변경
file-selector-button 선택자를 사용하여 css 속성을 설정해주면 파일 선택 버튼의 스타일을 변경할 수 있다.
&::file-selector-button{
background-color: #F8F9FC;
border-radius: 10px;
border: 1px solid rgba(0, 0, 0, 0.06);
width: 10rem;
height: 5rem;
};
label 태그를 이용하여 커스텀하기
label 태그는 input 태그와 같이 사용자 인터페이스를 제공하는 요소의 이름표와 같은 역할을 한다.
즉 input 태그를 label 태그와 연관시켜주면 label 태그를 눌러도 이벤트가 작동하게 된다.
먼저 input 태그에 display: none 속성을 추가하여 요소가 화면에서 안 보이도록 한다.
//styled-component
export const FileInput = styled.input`
display: none;
`;
이후 label 태그를 생성해준다.
이때 htmlFor 속성값을 input 태그의 id 속성값과 동일하게 작성해준다.
<S.Label
htmlFor='imgInput'
>
<IcPlus />
사진추가
</S.Label>
<S.FileInput
type='file'
id='imgInput'
/>
그리고 label 태그의 스타일을 자유롭게 작성해주면 된다.
export const Label = styled.label`
${({ theme: { mixin}}) => mixin.flexCenter({})};
${({ theme }) => theme.fonts.m_13_500};
background-color: ${({ theme }) => theme.colors.gray[50]};
color: ${({ theme }) => theme.colors.gray[500]};
text-align: center;
width: 31.5rem;
height: 13rem;
border-radius: 10px;
margin: 1.2rem 0;
gap: 1rem;
`;
label 태그의 자식 요소로 텍스트나 아이콘 등을 다양하게 배치할 수 있다.
파일명 나타나는 부분 커스텀하기
Input 태그를 사용하여 위와 같이 파일명이 나타나는 부분도 커스텀할 수 있다.
먼저 위에서 설명한 것과 동일하게 기존 input 태그의 display를 none으로 설정하고 이와 대응하는 label 태그를 생성해
업로드 버튼을 커스텀해준다.
<S.ImgLabel htmlFor='imgFile'>파일 선택</S.ImgLabel>
<S.ImgInput
type='file'
id='imgFile'
/>
파일명을 보일 input 태그를 생성한다.
이때 readOnly 속성을 작성해주면 input 태그에 값을 입력하는 일을 방지할 수 있다.
const [file, setFile] = useState<string>('');
return (
<S.TempWrapper>
<S.TextInput
value={file}
placeholder='사진을 첨부하세요'
readOnly
/>
<S.ImgLabel htmlFor='imgFile'>파일 선택</S.ImgLabel>
<S.ImgInput
type='file'
id='imgFile'
value={file}
onChange={(e) => setFile(e.target.value)}
/>
</S.TempWrapper>
);
value 값을 파일을 받는 input 태그와 동일하게 하여 해당 경로를 보이도록 하였다.
첨부한 이미지 미리보기
label 태그를 다음과 같이 설정해주고, width값과 height 값이 동일한 img 태그를 만들어준다.
이후 file이 존재한다면 해당 이미지 파일을 보여주는 img 태그를, 존재하지 않는다면 빈 화면을 보여주면 된다.
const [imgUrl, setImgUrl] = useState('');
return (
<S.TempWrapper>
<S.ImgLabel htmlFor='imgFile'>
{
imgUrl ?
<S.PreviewImg src={imgUrl} alt='preview' />
:
'사진을 첨부하세요'
}
</S.ImgLabel>
<S.ImgInput
type='file'
id='imgFile'
onChange={e => setImgUrl(e.target.value)}
/>
</S.TempWrapper>
);
그러나 위 코드는 이미지를 렌더링하지 못하고 오류가 발생하는데, 클라이언트에서 직접적으로 로컬 경로로 접근하였기 때문이다. 때문에 이미지 파일 경로를 파일 형식으로 변환해주어야한다.
const handleImage = (e: ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (file) {
const imgUrl = URL.createObjectURL(file);
setImgUrl(imgUrl);
}
};
input 태그의 onChange 속성값으로 작성해줄 이벤트 핸들러이다.
입력받은 파일을 File 객체로 받은 후, URL 객체의 메소드를 통하여 blob 형식으로 변환해준다.
입력받은 파일 경로를 파일 URL 객체의 메소드를 통하여 파일 객체로 변경해주었다.
이제 파일 경로가 로컬 서버에서 접근할 수 있는 임시 URL로 변환되어 이미지 파일이 렌더링될 수 있다.
해당 방법 외에도 fileReader 객체를 사용하는 방법도 존재한다.
const handleImage = (e: ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
const reader = new FileReader();
if(file){
reader.readAsDataURL(file);
reader.onloadend = () => {
if (reader.result && typeof reader.result === 'string') {
setImgUrl(reader.result);
}
};
}
};
다만 fileReader의 경우 변환된 코드가 매우 길고 복잡하기 때문에,, 서버에 전송해줄 경우 백엔드에서 다루기 어려울 수 있다.
정리해보자면
const handleImage = (e: ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (file) {
const imgUrl = URL.createObjectURL(file);
setImgUrl(imgUrl);
}
console.log(e.target.files)
};
파일을 업로드하면 FileList 객체에 업로드한 파일이 입력된다.
console.log(file);
해당 객체는 File 객체를 지니고 있으며, 이 File 객체가 사용자가 업로드한 파일이라 할 수 있다.
브라우저는 이를 안전하게 렌더링하기 위해 로컬 경로에서의 접근을 제한하고 있어서 해당 File 객체를 URL 객체의 메소드나 fieReader 객체를 통해 변환하여 사용하여야 한다.
const [imgUrl, setImgUrl] = useState('');
const handleImage = (e: ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (file) {
const imgUrl = URL.createObjectURL(file);
setImgUrl(imgUrl);
}
console.log(file)
};
return (
<S.TempWrapper>
<S.ImgLabel htmlFor='imgFile'>
{
imgUrl ?
<S.PreviewImg src={imgUrl} alt='preview' />
:
'사진을 첨부하세요'
}
</S.ImgLabel>
<S.ImgInput
type='file'
id='imgFile'
onChange={handleImage}
/>
</S.TempWrapper>
);
'웹 > React' 카테고리의 다른 글
[React/리액트] useQuery를 활용한 닉네임 중복 여부 구현 (1) | 2024.12.30 |
---|---|
[React/리액트] 소셜 로그인 구현하기 (0) | 2024.12.30 |
[React/리액트] TS 초기세팅 - tsconfig.json 주요 옵션들 및 tsconfig.node.json (1) | 2024.09.13 |
[React/리액트] Vercel로 배포하기 (0) | 2024.09.13 |
[React/리액트] 반복되는 요소를 배열을 통해 렌더링하기 (0) | 2024.09.10 |