- Day 6 - 누이터 핵심 기능 만들기2024년 09월 11일 15시 57분 32초에 업로드 된 글입니다.작성자: 삶은고구마
- 트윗 등록 기능 만들기
- 실시간 DB로 트윗 목록 보여주기
- 트윗 삭제
트윗 수정- 사진 미리 보기 기능 만들기
- 사진 저장 기능 만들기
1.트윗 등록 기능 만들기
트윗을 위한 폼을 만들고, 폼에서 전송한 데이터를 파이어베이서 DB에 저장한다.
1)트윗을 위한 폼 만들기
구성은 회원가입폼과 유사하다.
input 엘리먼트에서 입력받을 텍스트를 상태로 관리하기 위해 useState 사용, onChange함수로 상태연결함.
트윗글자는 120으로 제한, form 엘리먼트는 onSubimt함수로 원래의 이벤트가 발생하지않도록 함.(새로고침 방지)
Home.js
import {useState} from "react"; const Home = () => { const [nweet,setNweet] = useState(""); const onSubmit = (event) =>{ //새로고침 방지 event.preventDefault(); }; const onChange = (event) =>{ event.preventDefault(); const{ target:{value}, }=event; setNweet(value); }; return ( <form onSubmit={onSubmit}> <input value={nweet} onChange={onChange} type="text" placeholder="What's on your mind?" maxLength={120} /> <input type="submit" value="Nweet"/> </form> ); }; export default Home;
로그인 후 home화면 2)트윗을 위한 파이어베이스 데이터베이스 생성하기
파이어베이스 nwitter - cloud firestore 선택
데이터베이스 만들기 선택
위치 asia-northeast3 / 테스트모드 선택 후 만들기
설정완료
3)샘플 데이터 저장하기
파이어베이스 DB는 NoSQL기반 DB이다.
컬렉션/문서 체계 (폴더/파일과 유사)
필드 추가로 필드를 계속 추가할 수있으며, 문서id는 우측에 자동id 버튼을 누르면 자동으로 생성해줌
웹에서 이렇게 데이터 등록/삭제 가능함.
이 기능을 코드로 작성해야 한다.
등록한 컬렉션은 다시 삭제하자.
4)리액트에서 파이어베이스 데이터베이스 사용하기
리액트에서 파이어베이스 db를 사용하려면 fbase.js파일을 수정해야 한다.
회원가입에 필요한 authService를 사용했던 것 처럼 dbService를 사용할 것이다.
fbase.js
import firebase from "firebase/compat/app"; // import "firebase/auth"; // fb 9 이상에서 다른 방식으로 임포트하고 사용해야한다.. import "firebase/compat/auth"; import "firebase/compat/firestore"; const firebaseConfig = { apiKey: process.env.REACT_APP_API_KEY, authDomain: process.env.REACT_APP_AUTH_DOMAIN, projectId: process.env.REACT_APP_PROJECT_ID, storageBucket: process.env.REACT_APP_STORAGE_BUCKET, messagingSenderId: process.env.REACT_APP_MESSAGING_SENDER_ID, appId: process.env.REACT_APP_APP_ID }; firebase.initializeApp(firebaseConfig); export const firebaseInstance = firebase; export const authService = firebase.auth(); export const dbService = firebase.firestore();
서버 실행 중 코드 변경했다면 재실행 을 해야한다.
5)파이어스토어에 데이터 저장하기 Create
dbService.collection..은 promise를 반환하므로 async - await 문 사용함.
dbService.collection("nweets")로 컬렉션을 생성하고
.add 이하는 해당 컬렉션의 문서를 생성한다.
저장을 완료했다면 setNweet("");로 nweet상태를 빈 문자열로 초기화한다.
Home.js
import { dbService } from "fbase"; import {useState} from "react"; const Home = () => { const [nweet,setNweet] = useState(""); const onSubmit = async (event) =>{ //새로고침 방지 event.preventDefault(); await dbService.collection("nweets").add({ text: nweet, createdAt : Date.now(), }); setNweet(""); }; ..이하생략
실행결과
6)파이어스토어에서 문서 읽어오기 Read
get 함수 사용
단, add함수 처럼 1번만 실행해서 읽어올 수 없음.
문서의 갯수가 많으면 forEach 함수로 가공해야 한다.
컴포넌트가 모두 마운트 된 이후 useEffect를 사용하여 getNweets 함수 실행.
단, 아래 코드처럼 useEffect로 실행하려는 함수에 async - await 구문이 포함되어있으면 함수를 따로 정의해야함.
get함수는 문서목록과 여러정보를 한꺼번에 반환한다.
Home.js
//트윗 읽기 const getNweets = async() =>{ const dbNweets = await dbService.collection("nweets").get(); //console.log(dbNweets); dbNweets.forEach((document)=>console.log(document.data())); }; useEffect(()=>{ getNweets(); },[]);
콘솔로그를 보면 데이터를 가져온건 분명한데 내가 알기엔 애매한/모르는 데이터들이다.
쿼리스냅샷(스냅샷)이라고 한다. 파이어스토어에서 원본을 사진찍듯이 찍어 출력한 것이다.
이것을 내가 알기 쉽게 foreach문으로 걸러내야 한다.
document.data()를 출력하면 아래와 같이 출력된다.
7)받은 데이터로 게시물 목록 만들기
빈 배열로 초기화한 상태 nweets를 만들고,
foreach함수와 setNweets로 파이어스토어의 데이터를 저장한다.
Home.js
const [nweets,setNweets] = useState([]); //트윗 읽기 const getNweets = async() =>{ const dbNweets = await dbService.collection("nweets").get(); dbNweets.forEach((document)=> setNweets((prev)=>[document.data(), ...prev]) ); }; useEffect(()=>{ getNweets(); },[]); console.log(nweets);
8)트윗 아이디 저장하기
아이디는 파이어스토어에서 데이터를 구별할 때 사용한다.
아이디 없이 업데이트,삭제 명령을 받아들이지 않을 것이다.(어떤걸 삭제,수정하라는건지 모르니까)
문서에는 id속성이 있는데 이 값은 스냅샷을 순회하며 document.id로 얻을 수 있다.
Home.js
const getNweets = async() =>{ const dbNweets = await dbService.collection("nweets").get(); dbNweets.forEach((document)=>{ const nweetObject ={...document.data(), id:document.id()}; setNweets((prev)=>[nweetObject, ...prev]) }); };
id출력 -문제점 발견-
트윗이 2번 중복출력 되는 현상을 확인.
분명 home.js에서는 getNweet을 1번만 호출하는데..그럼 home 자체가 두번 호출이 된다는 것인가?
의심을 갖게 됨.
index.js 를 살펴 보았다.
찾아보니 react18부터는 주석처리 한 방식이 아닌, root.render(<App />); 방식으로 렌더링한다구 한다..
에러없이 실행되었지만 이런 사소한 문제가 있었던 것이다. 수정 후 재실행 하니 정상적으로 1번만 실행된다.
const root = ReactDOM.createRoot(document.getElementById('root')); // root.render( // <React.StrictMode> // <App /> // </React.StrictMode>, // document.getElementById("root"), // ); root.render(<App />);
9)트윗 목록 출력하기
배열을 Home 컴포넌트에 출력하면 된다.
return ( <> <form onSubmit={onSubmit}> <input value={nweet} onChange={onChange} type="text" placeholder="What's on your mind?" maxLength={120} /> <input type="submit" value="Nweet"/> </form> <div> {nweets.map((nweet)=>( <div key={nweet.id}> <h4>{nweet.text}</h4> </div> ))} </div> </> );
10)누가 쓴 트윗인지?
본인이 쓴 트윗만 수정,삭제 해야하므로 어떤 유저가 쓴 트윗인지 알아야 한다.
App.js의 authService.onAuthStateChanged 함수를 사용해서 로그인 사람의 정보를 확인할 수 있다.
이 값을 다른 컴포넌트로 보내서 사용한다.
로그인한 사람의 정보를 관리하기 위해 useState를 하나 더 추가함.setUserObj
userObj는 다양한 곳에 사용될 것이다.
home,profile,editprofile 컴포넌트 등등.. 페이지 중간 역할인 AppRouter 컴포넌트로 userObj를 보낸다.
AppRouter 컴포넌트에 userObj를 프롭스로 전달했다!
참고로 리액트에서는 프롭스를 여러 컴포넌트를 거치지 않게 하는 것이 좋다. 유지보수에 좋지 않다.
지금처럼 한두단계 정도는 괜찮다.
App.js
function App() { const[init,setInit] = useState(false); const [isLoggedIn,setIsLoggedIn] = useState(false); const [userObj,setUserObj] = useState(null); useEffect(()=>{ authService.onAuthStateChanged((user)=>{ if(user) //여기서 로그인 상태 isLoggedIn 변경 { setIsLoggedIn(user); setUserObj(user); }else ... {init? ( <AppRouter isLoggedIn={isLoggedIn} userObj={userObj}/> ): ("initializing...") }
Router.js
// 상위 컴포넌트에서 받은 프롭스를 구조분해 할당으로 사용한다. const AppRouter = ({isLoggedIn,userObj}) => { return( <Router> {isLoggedIn && <Navigation/>} <Routes> {isLoggedIn ? ( <> <Route exact path="/" element={<Home userObj={userObj}/>}>
Home.js
const Home = ({userObj}) => { console.log(userObj);
로 수정 한후 log확인하면 uid가 출력되는데 이것이 현재 로그인한 user의 id 이다.
Home.js
await dbService.collection("nweets").add({ text: nweet, createdAt : Date.now(), creatorId : userObj.uid, });
파이어스토어에 저장할 항목을 객체로 넣으면된다.
유저아이디도 저장해야하므로 creatorId에 userObj.uid 값을 사용.
컬렉션을 수동으로 삭제 후, 아무 트윗이나 쓰고 다시 확인해보자!
확인 해보니 uid도 잘 들어간다.
2.실시간 DB로 트윗 목록 보여주기
현재 get함수는 맨 처음 화면을 렌더링할때 실행하므로 실시간으로 반영되지 않는다.
이런 절차 없이 실시간 트윗 목록을 출력하는 방법은 바로 실시간 데이터베이스를 도입하는 것!
채팅 어플리케이션 등에 유용하다.
실시간 데이터베이스는 데이터베이스의 변화를 실시간으로 감지하고 변화가 감지되면 곧바로
파이어베이스 라이브러리 함수를 실행한다..
이방법을 사용하면 async-await를 사용하지 않아도 된다.
1)getNweets 함수 삭제
Home.js
const Home = ({userObj}) => { //console.log(userObj); const [nweet,setNweet] = useState(""); const [nweets,setNweets] = useState([]); useEffect(()=>{ },[]);
2)onSnapShot 함수 적용
map함수를 쓰면 코드 효율이 높아짐
forEach함수는 배열 요소를 순회하면서 매 순회마다 setNweet 함수를 사용하지만
map함수는 순회하면 만든 배열을 반환하므로 반환한 배열을 1번만 setNweets함수에 전달한다.
여기까지 수정했다면 트윗 등록시 실시간으로 새 트윗이 홈에 출력된다.
Home.js
//실시간 db 위한 onSnapshot함수적용 useEffect(()=>{ dbService.collection("nweets").onSnapshot((snapshot)=>{ const newArray = snapshot.docs.map((document)=>({ id : document.id,...document.data(), })); setNweets(newArray); }); },[]);
3.트윗 삭제 기능
1)트윗 컴포넌트 분리하기
트윗 바로 아래에 수정,삭제 버튼을 두려면 컴포넌트에 jsx를 추가 하는 작업이 필요하다.
이 작업을 home 컴포넌트에 하기엔 코드의 덩치가 커져서 분리하는것이 좋다.
Home.js
이 부분이 트윗을 출력하는 코드인데 이것을 컴포넌트로 따로 분리하자.
작업을 이어서 한다면 컴포넌트 덩치가 커지므로..
<div> {nweets.map((nweet)=>( <div key={nweet.id}> <h4>{nweet.text}</h4> </div> ))} </div>
컴포넌트 하위에 Nweet.js 파일을 새로 생성한다.
Nweets.js는 기존의 홈 컴포넌트의 개별트윗 출력 부분과 완전히 같은 역할을 한다.
Nweet.js
const Nweet = ({nweetObj})=>{ return( <div> <h4>{nweetObj.text}</h4> </div> ); }; export default Nweet;
다시 Home컴포넌트에 새로만든 Nweets 컴포넌트를 넣어보자.
Home.js
<div> {nweets.map((nweet)=>( // <div key={nweet.id}> // <h4>{nweet.text}</h4> // </div> <Nweet key = {nweet.id} nweetObj={nweet}/> ))} </div>
2)수정,삭제 버튼 추가하기
Nweet.js
const Nweet = ({nweetObj})=>{ return( <div> <h4>{nweetObj.text}</h4> <button>삭제</button> <button>수정</button> </div> ); }; export default Nweet;
트윗 수정/삭제 하는 방법
현재 로그인한 사람과 해당 트윗을 작성한 사람의 uid가 일치하면 작업을 진행한다.
3)내가 쓴 트윗만 수정/삭제 하도록 하기
현재 로그인한 유저의 정보는 home 컴포넌트에서 userObj 프롭스로 받고 있다.
이것을 활용하면 원트윗의 주인인지 아닌지 알 수있음!
오너값이 true/false인지 확인해서 true일때만 삭제수정버튼을 보이도록한다.
Home.js
<Nweet key = {nweet.id} nweetObj={nweet} isOwner={nweet.creatorId === userObj.uid} />
Nweet.js
return( <div> <h4>{nweetObj.text}</h4> {isOwner &&( <> <button>삭제</button> </> ) } </div> );
아이디 두개로 트윗작성/본인것이 아니면 삭제 버튼이 나타니지 않음 4)버튼에 삭제 기능 추가
삭제에 필요한 것
문서의 고윳값인 문서 아이디가 필요하다
home->Nweet으로 넘긴 nweetObj에있음 . 삭제 전에 alert으로 삭제 여부를 물어봐주자.
확인을 누르면 true , 취소를 누르면 false가 반환되고 로그에 출력된다.
Nweet.js
const Nweet = ({nweetObj,isOwner})=>{ const onDeleteClick = () =>{ const ok = window.confirm("삭제하시겠습니까?"); console.log(ok); } return( <div> <h4>{nweetObj.text}</h4> {isOwner &&( <> <button onClick={onDeleteClick}>삭제</button> </> ) } </div> ); }; export default Nweet;
이제 true/false에 따라 삭제여부를 판단하여 삭제하는 기능을 붙여보자.
실시간 db 변화를 감지하는 onSnapshot 함수에는 async-await를 안붙여도 된다 했지만
붙여도 똑같이 동작은 한다..!
아래에서 붙인 이유는 해당 함수를 다른 함수로 교체할 일에 대비하여 미리 붙여둔것이라 함(저자가)
Nweet.js
import { dbService } from "fbase"; const Nweet = ({nweetObj,isOwner})=>{ const onDeleteClick = async() =>{ const ok = window.confirm("삭제하시겠습니까?"); console.log(ok); if(ok) { console.log(nweetObj.id); const data = await dbService.doc(`nweets/${nweetObj.id}`).delete(); console.log(data); } } return( <div> <h4>{nweetObj.text}</h4> {isOwner &&( <> <button onClick={onDeleteClick}>삭제</button> </> ) } </div> ); }; export default Nweet;
4.사진 미리 보기 기능 만들기
사진은 파이어스토어에 저장할 수 없다.
사진의 경우 파이어베이스 스토리지를 사용해야 한다.
파이어베이스 스토리지는 파일을 저장하고 불러오는 저장소 역할을 한다.(말그대로 스토리지, 저장소!)
이 저장소를 이용하면 누이터에 사진 첨부 기능을 구현 할 수 있다.
사진 미리보기 / 사진 등록 / 사진 취소를 구현하자.
1)파일 첨부 양식 만들기
웹 브라우저에 파일을 올리려면 input 엘리먼트의 type을 file로 지정해야 한다.
file로 지정하면 내 컴퓨터에 저장된 파일을 선택 할 수 있다.
만약 사진만 등록하고 싶다면 추가로 accept 속성을 사용해야 한다. (파일 종류 제한)
Home.js
<form onSubmit={onSubmit}> <input value={nweet} onChange={onChange} type="text" placeholder="What's on your mind?" maxLength={120} /> <input type="file" accept="image/*"/> <input type="submit" value="Nweet"/> </form>
accept로 이미지 속성만 선택할 수 있어서
실제로 문서파일과 이미지 파일이 섞인 폴더에서 이미지만 택할 수 있다.
그리고 선택된 파일 없음 란에 선택한 이미지 파일명이 출력됨!
2)화면 깔끔하게 정리하기
파이어베이스에서 모든 트윗을 삭제하고 footer 엘리먼트도 삭제한다.
나는 주석처리를 하였다.
App.js
{init? ( <AppRouter isLoggedIn={isLoggedIn} userObj={userObj}/> ): ("initializing...") } {/* jsx에 js 사용시 중괄호로 감싸기 */} {/* <footer>© {new Date().getFullYear()}Nwitter</footer> */} </>
3)첨부 파일 정보 출력하기
파일을 선택하면 input엘리먼트에 파일 이벤트가 발생한다.
이 이벤트는 onChange 속성, 함수로 감지가 가능하다. log로 확인해보자
event.target.value로도 파일경로가 출력되지만 보안정책상 경로를 표시하면 안되므로 숨겨진 상태로 출력됨.
파일관련 내용을 출력하고싶다면 target.files를 사용한다.
Home.js
const onFileChange = (event) => { console.log(event.target.files); } ... <input type="file" accept ="image/*" onChange={onFileChange}/>
위의 로그처럼 출력되었다면 이 정보를 활용할 수있다.
즉, 이 값을 이용하여 파일미리보기/파일등록기능을 구현할 것이다.
아래처럼 코드를 수정하면 theFile에 대한 정보가 저장될 것이다.
files가 배열인 이유는 input엘리먼트의 multiple 속성을 대비하기 위함이다.
파일을 여러개 선택하면 배열로 파일 정보를 보여줘야 한다.
현재 예제에선 1개만 선택했지만 여러개 선택에 대비하여 배열로 선언한 후,
files[0]과 같이 배열을 풀어 theFile에 저장한다.
Home.js
const onFileChange = (event) => { // console.log(event.target.files); const{ target:{files}, }=event; const theFile = files[0]; }
4)웹 브라우저에서 사진 출력하기
이제 미리보기 기능을 구현하자.
브라우저 api를 사용하면 가능하다.FileReader다.
FileReader는 new 키워드와 함께 사용해줘야 한다. 그리고 readAsDataURL 함수를 사용할 수 있다 :)
readAsDataURL 함수는 파일 정보를 인자로 받아서 (theFile) 파일 위치를 URL로 전환해준다.
이렇게 하면 파일의 URL 과 img 엘리먼트를 사용해 사진을 웹브라우저에 출력 할 수 있다.
but!!
이 함수는 리액트 생명주기 함수처럼 파일 선택 후
"웹 브라우저가 파일을 인식하는 시점" , "웹 브라우저 파일 인식이 끝난 시점"을 포함하고있어서
시점까지 함께 관리해줘야 URL을 얻을 수 있다.
무슨 말인지 아리송해서 검색을 해보았다..
readAsDataURL 함수는 "비동기"적으로 파일을 읽기때문에 파일이 읽힌 후 실행되는 콜백이 필요하다.
파일 읽기 작업은 즉시 완료되는게 아니고 읽기까지 시간이 걸리기 때문이다.
onload : 파일을 성공적으로 읽은 후 호출
onerror : 파일 읽기에 실패한 경우 호출
onprogress : 파일 용량이 큰 경우 이 이벤트를 사용하여 사용자에게 진행 상황을 알려줌.
비동기 특성을 고려하여 파일을 읽기전에 데이터를 사용하면 안된다. onload 이후 처리한다.
아래의 코드를 보면 onloadend는 파일이 함수로 들어간 이후 결과값이 나온 다음 상황을 감지한다.
그 때 이벤트 값을 사용할 수있게 해준다. 이 이벤트 값에는 내가 원하는 파일 url 값이 있다.
Home.js
const onFileChange = (event) => { // console.log(event.target.files); const{ target:{files}, }=event; const theFile = files[0]; const reader = new FileReader(); reader.onloadend = (finishedEvent)=>{ console.log(finishedEvent); }; reader.readAsDataURL(theFile); }
result에 나온 값을 복사해서 인터넷 주소창에 붙여넣기한 후 실행하면 내가 택한 이미지가 출력된다.
성공이다!
과정이 다소 복잡한데 이유는 해킹/보안문제땜에 필수라고 한다.
5)사진 미리 보기 구현하자
URL을 사용해서 사진 미리보기를 구현해보자.
사진 미리보기는 파이어베이스에 사진을 저장하기전에 "확인"하는 기능이므로
컴퓨터에서 위치만 참조하면 된다.URL을 관리하기 위한 상태만 있으면 ok.
useState로 상태를 추가하자.
Home.js
const Home = ({userObj}) => { //console.log(userObj); const [nweet,setNweet] = useState(""); const [nweets,setNweets] = useState([]); const [attachment, setAttachment] = useState("");
이어서 URL을 얻는 코드에서 setAttachment 함수로 상태를 업데이트 해보자.
reader.onloadend = (finishedEvent)=>{ // console.log(finishedEvent); const{ currentTarget:{result}, }=finishedEvent; setAttachment(result); };
currentTarget 항목에 이미지 URL을 포함한 result 항목이 있었다.
이 값을 바로 얻기 위해 구조 분해 할당을 사용했다.
이어서 attachment가 있는 경우에만 img 엘리먼트를 출력하도록 코드를 작성한다.
attachment 값이 있다면 가로세로100px로 출력
{attachment && <img src={attachment} width="100px" height="100px"/>}
6)파일 선택 취소 버튼 만들기
{attachment && ( <div> <img src={attachment} width="100px" height="100px"/> <br/> <button onClick={onClaerAttachment}>Clear</button> </div>
5.사진 저장 기능 만들기
파이어스토어는 파일저장에 적합하지 않다.
파이어베이스 스토리지를 사용하자. (사진,동영상 저장 가능)
1)파이어베이스 스토리지 임포트하기
수정 후 npm start로 리액트 서버 재구동.
fbase.js
// 사진 저장을 위한 파이어베이스 스토리지 임포트 import "firebase/compat/storage"; ... export const storageService = firebase.storage();
2)파이어베이스 스토리지 간단하게 사용하기
스토리지에 집중하도록 파이어스토어 관련 코드 주석 처리
Home.js
const onSubmit = async (event) =>{ //새로고침 방지 event.preventDefault(); // await dbService.collection("nweets").add({ // text: nweet, // createdAt : Date.now(), // creatorId : userObj.uid, // }); // setNweet(""); };
3)고유 식별자를 만들어주는 UUID 라이브러리 설치
스토리지는 문서에 아이디를 자동으로 만들어 주지 않아서
고유 식별자를 직접 만들어서 저장할 데이터와 짝을 지어줘야 한다.
랜덤함수를 사용해도 되겠지만~ UUID 라이브러리를 사용하겠다.
터미널
npm install uuid
4)UUID 임포트 하기
https://github.com/uuidjs/uuid#readme
GitHub - uuidjs/uuid: Generate RFC-compliant UUIDs in JavaScript
Generate RFC-compliant UUIDs in JavaScript. Contribute to uuidjs/uuid development by creating an account on GitHub.
github.com
위의 readme 문서를 확인 하여 아래의 항목 참조.
저 방식대로 임포트 하자.
임포트 한 이후 UUID 사용법은 간단하다.
uuidv4 함수를 실행하면 된다.
Home.js
import {v4 as uuidv4} from "uuid";
5)스토리지 레퍼런스 사용하기
스토리지는 레퍼런스라는 경로 시스템을 가지고 있다.
레퍼런스를 통해 경로를 생성하면 이 경로를 이용해 파일을 업로드/삭제 작업을 진행 할 수있음.
파이어스토어처럼 자동등록이 안되어 위에서 언급했듯이 내가 직접 아이디를 만들어 줘야한다.
-레퍼런스를 통해 어떻게 경로를 만들고
-사진을 업로드할 수 있는지?
Home.js
import { dbService,storageService } from "fbase";
스토리지는 쉽게 생각해서 컴퓨터에 있는 하드 디스크와 비슷하다.
폴더를 만들고, 파일을 저장하는 것처럼 사용하면 된다.
스토리지에 사용자 아이디로 폴더를 생성하고 uuid로 파일 이름을 정해서 저장하는 것이다.
이 작업들은 레퍼런스 함수를 통해서 하는 것이고.
storageService.ref().child를 사용하면 폴더,파일 이름 설정 가능.
아래의 코드를 보면 사용자 명인 uid로 폴더명을 지정했고, 파일명은 uuidv4 이다.
파일확장자는 과정에서 자동으로 설정되어서 생략함.
Home.js
const onSubmit = async (event) =>{ //새로고침 방지 event.preventDefault(); // await dbService.collection("nweets").add({ // text: nweet, // createdAt : Date.now(), // creatorId : userObj.uid, // }); // setNweet(""); storageService.ref().child(`${userObj.uid}/${uuidv4()}`); };
6)스토리지에 사진 저장하기
스토리지 제공 함수 putString 에 URL을 인자로 전달하며녀 해당 파일이 스토리지에 저장된다.
URL은 attachment에 저장되어있다.
Home.js
const onSubmit = async (event) =>{ //새로고침 방지 event.preventDefault(); // await dbService.collection("nweets").add({ // text: nweet, // createdAt : Date.now(), // creatorId : userObj.uid, // }); // setNweet(""); const attachmentRef = storageService.ref().child(`${userObj.uid}/${uuidv4()}`); const response = await attachmentRef.putString(attachment,"data_url"); console.log(response); };
사진을 선택하고 Nweet으로 전송(submit)하면 아래와 같은 log가 출력되고 스토리지에 저장이 된다.
실제로 내가 선택한 이미지가 저장되어있다.
7)스토리지에서 사진 불러오기
response.ref.getDownloadURL 함수를 사용한다.
Home.js
console.log(await response.ref.getDownloadURL());
이 링크를 클릭하면 내가 업로드한 이미지가 웹브라우저에 출력됨.
8)사진을 포함한 트윗 결과 화면에 출력하기
저자의 경우 파이어스토어에 파일 URL을 포함시켰다.
주석했던 부분을 다시 풀어주고, img 엘리먼트 추가
Home.js
const onSubmit = async (event) =>{ //새로고침 방지 event.preventDefault(); const attachmentRef = storageService.ref().child(`${userObj.uid}/${uuidv4()}`); const response = await attachmentRef.putString(attachment,"data_url"); const attachmentUrl = await response.ref.getDownloadURL(); await dbService.collection("nweets").add({ text: nweet, createdAt : Date.now(), creatorId : userObj.uid, attachmentUrl, }); setNweet(""); setAttachment(""); //초기화 // console.log(await response.ref.getDownloadURL()); };
nweet.js
return( <div> <h4>{nweetObj.text}</h4> {nweetObj.attachmentUrl && ( <img src={nweetObj.attachmentUrl} width="100px" height="100px"/> )} {isOwner &&( <> <button onClick={onDeleteClick}>삭제</button> </> ) } </div> );
글과 이미지 둘다 출력된다. 9)코드 다듬기
지금 작성된 코드는 사진 파일 유무와 상관없이 레퍼런스를 생성하므로 사진을 선택했을때만
즉, attachment가 있는 경우에만 생성하도록 하자..if문 조건을 걸면된다.
Home.js
const onSubmit = async (event) =>{ //새로고침 방지 event.preventDefault(); let attachmentUrl=""; if(attachment!==""){ const attachmentRef = storageService.ref().child(`${userObj.uid}/${uuidv4()}`); const response = await attachmentRef.putString(attachment,"data_url"); attachmentUrl = await response.ref.getDownloadURL(); }
10)트윗 삭제 시 사진도 함께 스토리지에서 삭제
현재 트윗을 삭제하면 사진은 삭제되지 않음.
트윗은 파이어스토어 / 사진은 파이어베이스 스토리지에 있기 때문이다.
현재 파일URL만 알고 있는 상태다.
하지만 파일URL이 스토리지 파일위치는 아니다. 이 때 이 함수를 사용 하면 된다.
storageService.refFromURL()
refFromURL 함수를 사용하면 attachmentUrl만으로도 스토리지에서 삭제가 가능하다!
attachmentUrl이 있다면 삭제한다는 조건.
Nweet.js
import { dbService,storageService } from "fbase"; ..중략 if(ok) { // console.log(nweetObj.id); await dbService.doc(`nweets/${nweetObj.id}`).delete(); // console.log(data); if(nweetObj.attachmentUrl !=="") await storageService.refFromURL(nweetObj.attachmentUrl).delete(); }
이렇게 하면 트윗 삭제 시 스토리지에 저장된 이미지도 삭제가 된다!
-6일차 깃헙 커밋 완료-
다음글이 없습니다.이전글이 없습니다.댓글