주요기능
- 상품이미지 여러개 등록하기
- 이미지 클릭시 모달 창
- 이미지 업로드시 aws s3 저장
- form이 만족되지 않았을 경우 focus
<S.UploadImgInput
onChange={onChangeImgInput}
ref={uploadImgInput}
type="file"
accept="image/jpg, image/jpeg, image/png"
multiple
/>
먼저, type=file인 input을 만들고, display:none 설정
useRef로 업로드버튼 클릭시 input으로 작동할 수 있도록 만들었다.
const onChangeImgInput = async (e: any) => {
e.preventDefault();
const files = e.target.files;
const newFileList: string[] = [];
if (files.length > 3) {
alert("사진은 최대 3개까지 첨부할 수 있습니다");
return;
}
for (let i = 0; i < files.length; i++) {
const resizedImage = await resizeImage(files[i]);
newFileList.push(resizedImage as any);
}
setFileList([...fileList, ...newFileList]);
};
현재 filelist에 이미지 파일이 저장된다.
1. files에 업로드한 이미지를 저장
2. 3개가 넘을경우 alert
3. for문을 이용하여 이미지 리사이징
4. setFileList([...fileList, ...newFileList]) 이렇게 한 이유는 기존 fileList에 추가로 또 업로드 될 경우도 있으니 위와 같이 코드를 짰다.
react-modal을 사용해서 구현
// Upload.tsx
const onClickModalOpen = (idx: number) => {
setOnModal((prev) => !prev);
setSelectImg(URL.createObjectURL(fileList[idx] as any));
document.body.style.overflow = "hidden";
};
// 모달닫기
const closeModal = () => {
setOnModal(false);
document.body.style.overflow = "auto";
};
...
// 맨 밑부분에 추가
<Modal isOpen={onModal} onRequestClose={closeModal} selectImg={selectImg} />
Modal컴포넌트를 따로 만들어서 prop을 넘기는 식으로 진행했다
// Modal.tsx
const Modal = ({ isOpen, onRequestClose, selectImg }: any) => {
return ReactDOM.createPortal(
<ReactModal
isOpen={isOpen}
onRequestClose={onRequestClose}
style={{
overlay: {
position: "fixed",
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: "black",
zIndex: "999",
overflow: "hidden",
},
content: {
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
background: "black",
borderRadius: "4px",
border: "none",
overflow: "hidden",
padding: 0,
},
}}
shouldCloseOnOverlayClick={true}>
<S.Button>
<TiDelete onClick={onRequestClose} color="#fff" size={40} />
</S.Button>
<S.ImgWrap>
<img src={selectImg} alt="사진" />
</S.ImgWrap>
</ReactModal>,
document.body // 모달을 document의 body 아래에 렌더링
);
};
ReactDOM.createPortal을 이용해서 documet.body를 추가했다 이렇게 해야 모달이 젤 위로 와서 성공적으로? 모달을 보여줄 수 있게된다
modal을 할때는 레이아웃 부분이 굉장히 까다로웠따..
// submit했을 시
...
const upload = fileList.map((file, idx) => {
const params = {
Bucket: "ikw-market",
Key: `${Date.now()}.${idx}.webp`,
Body: file,
};
return new AWS.S3().upload(params).promise();
});
// 비동기로 upload 함수 실행 후 aws s3 이미지 링크 저장
const uploadResults = await Promise.all(upload);
const imageUrls = uploadResults.map((result) => result.Location);
...
submit과 동시에 aws s3에 저장한다. map을 이용해서 현재 파일리스트의 모든 이미지를 업로드 하고
Bucket - aws s3에서 사용하는 버킷명
Key - 저장할 파일이름 + 확장자도 붙일수있었음
Body - 저장할 파일 종류
원래는 기본적으로 제공되어지는 input을 사용했다 근데 여기서는 단점이 여럿존재한다.
예를들어 maxlength, minlength 같은것들을 설정했을때 개발자도구를 열어 dom 속성에서 지워서 사용가능하다는 나름 치명?적인 단점이있음
이를 보안하려고 useForm을 사용하였다.
const { register, handleSubmit, setValue } = useForm<IForm>();
...
<S.UploadInput
{...register("name", { required: true, minLength: 5, maxLength: 15 })}
placeholder="최소 5글자"
/>
register("name")로 첫번째로 오는 인자는 input의 name이다. 글고 두번재로 오는 인자들은 추가로 설정 할 수 있는 속성들이 있음
setValue("name",function)으로 input의 value를 조절할수있음