-
Cloudinary와 React-Dropzone을 연동하여 만들어본 이미지 업로드 기능을 모멘텀 프로젝트에 추가해 보았다!
현재 모멘텀에 배경으로 깔리는 이미지는 기본으로 세팅해 놓은 기본 샘플 이미지들을 랜덤으로 가져와서 적용되는 방식이다.
// background.bg export const bg = ["bg1.jpg", "bg2.jpg", "bg3.jpg", "bg4.jpg", "bg5.jpg", "bg6.jpg", "bg7.jpg", "bg8.jpg", "bg9.jpg", "bg10.jpg"];
// Main.jsx import { bg } from "../../api/background"; const randomBg = () => { return bg[Math.floor(Math.random() * bg.length)]; }; return ( <section className={styles.container} style={{ backgroundImage: `url(images/${randomBg()})` }}> ... </section> );
이 배경들을 사용자가 직접 업로드하여 원하는 이미지를 배경으로 사용할 수 있게 하고 싶었다.
그래서 구현하고자 하는 사항을 아래처럼 정리해 보았다.
👉 화면 좌측 하단에 이미지 설정 버튼 추가 > 클릭 시 이미지 업로드를 할 수 있는 팝업 화면 노출
👉 이미지 업로드 시 바로 배경으로 설정하기
👉 업로드한 이미지를 삭제할 수 있게 삭제 버튼 기능 넣기
👉 사용자가 업로드한 이미지가 없을 경우에는 기본 샘플 이미지들이 노출되게 하기
이미지 업로드 기능만 구현했을 때는 모든 상태값들을
Dropzone
컴포넌트 안에서 다 관리했었다. (이미지 업로드 관련 로직이 다 여기에 있었기에..)근데 모멘텀에 적용하려고 하니, 상태값 일부를 해당 컴포넌트가 아닌 부모 컴포넌트에서 관리해야 할 필요성이 생겼다.
Dropzone
컴포넌트에서 업로드한 이미지 값들을Main
컴포넌트에서 접근을 하려면 상태값을 끌어올려야 했다.그래서 이 상태값을 어떻게 관리하면 좋을지 고민되었다. 🤔
❓ Context API를 사용해서 상태값을 전역으로 관리하기
❓ 상태값을 부모 컴포넌트에서 만들고 하위 컴포넌트에게
props
로 전달하기(prop drilling)Prop Drilling?
Prop Drilling 은 props를 오로지 하위 컴포넌트로 전달하는 용도로만 쓰이는 컴포넌트들을 거치면서 React Component 트리의 한 부분에서 다른 부분으로 데이터를 전달하는 과정이다.
위 두 가지 방법에 대해 고민하다가 두 번째 방법 (prop drilling)으로 해결하기로 했다.
prop
의 전달이 수많은 컴포넌트를 거치지 않고 단순히 1~2개 정도의 컴포넌트만 거쳐갈 것이고, 관리하려는 상태값이 이미지 데이터 하나이므로 굳이 Context를 쓸 필요가 없어 보였다.기존
Dropzone
컴포넌트에서 관리했던localFiles
를Main
컴포넌트로 옮겼다.// Main.jsx // localStorage에 값 존재 여부로 초기값 세팅 const getBg = () => { const bgimgs = localStorage.getItem("bg"); return bgimgs ? JSON.parse(bgimgs) : []; }; const [localFiles, setlocalFiles] = useState(getBg);
추가로
backgrounds
상태값을 선언하고useEffect
로localFiles
가 변경될 때마다localFiles
에서 이미지 경로값인secure_url
만 추출하여 별도로 관리하게 했다.const [backgrounds, setBackgrounds] = useState(); useEffect(() => { const bgUrls = localFiles.map(file => file.secure_url); setBackgrounds(bgUrls); }, [localFiles]);
Dropzone
컴포넌트에props
로localFiles
와onsetlocalFiles
함수를 넘겨주었다.<DropZone localFiles={localFiles} onsetlocalFiles={setlocalFiles} />
그리고 배경 이미지를 랜덤으로 추출하는 기존 로직을 수정하였다.
그래서 사용자가 업로드한 이미지가 있을 경우엔 업로드된 이미지에서 랜덤으로 나오고, 업로드한 이미지가 없을 경우엔 기본 샘플 이미지가 랜덤으로 나오게끔 수정하였다.
// 기존 로직 const randomBg = () => { return bg[Math.floor(Math.random() * bg.length)]; }; // 수정된 로직 const randomBg = () => { return backgrounds?.length ? backgrounds[Math.floor(Math.random() * backgrounds.length)] : `images/${bg[Math.floor(Math.random() * bg.length)]}`; }; return ( <section className={styles.container} style={{ backgroundImage: `url(${randomBg()})` }}> ... </section> );
구현된 화면 👇👇
이미지 삭제 기능
이미지를 성공적으로 업로드하면 바로 배경 이미지로 설정이 된다. 이때 설정되는 이미지는 랜덤으로 적용된다.
그리고 하단의 '나의 이미지 목록'에 업로드된 이미지들이 바로 업데이트가 되고, 업로드한 이미지를 삭제할 수 있도록 상단에 삭제 버튼을 추가하였다. 삭제 버튼을 클릭하면 뒤의 배경도 그에 맞춰 변경되는 걸 확인할 수 있다.
+ 추가 ) Error Handling
이미지 업로드가 실패했을 경우의 상황도 고려하여 오류 처리도 추가했다.
error
는 오류 상태를 저장하고errorImgLength
는 업로드 실패한 이미지의 개수를 표기해 줄 것이다.const [error, setError] = useState(false); const [errorImgLength, setErrorImgLength] = useState(0);
... try { const response = await axios.post(url, formData, config) const files = await response.data; setlocalFiles(prev => [...prev, files]) } catch (error) { setErrorImgLength(prev => prev + 1) setError(true); return; } ... return ( <section> {error && <p>업로드 실패 ❌<br /> 업로드 실패한 이미지가 있습니다! 업로드를 다시 시도해주세요.</p>} {error && errorImg > 0 && <p>업로드 실패 이미지 개수 ({errorImg})</p>} </section> )
일단 리액트 모멘텀 프로젝트를 기획하고 구현하고자 했던 기능들을 추가했지만 아직 부족한 점이 많은 것 같다.
코드도 좀 더 깔끔하게 리팩토링도 하고 싶고...
조금 더 공부해서 틈틈이 고도화를 해봐야겠다.
728x90'토이 프로젝트' 카테고리의 다른 글
Cloudinary 및 React-Dropzone으로 이미지 업로더 구현하기 (0) 2023.03.02 리액트로 크롬 모멘텀(Momentum) 구현하기 (0) 2023.01.29 댓글