일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 백준
- 컴퓨터 네트워크
- 리액트 훅
- useState
- React JS
- 프로그래머스 완전탐색
- react firebase
- 장고
- 코딩테스트 고득점 Kit
- Java
- 프로그래머스
- 자바스크립트
- react hook
- 코틀린
- 디자인 패턴
- 코딩테스트 고득점 Kit 완전탐색
- design pattern
- react
- vanillaJS
- useEffect
- websocket
- 리액트
- 데이터모델링과마이닝
- NextJS
- JavaScript
- 프로그래밍 언어론
- 자바
- 자바 공부
- codesandbox
- 프로그래머스 자바
- Today
- Total
기록하는 개발자
[React, Firebase] TwitterCloneCoding 3.0 File Upload, Delete file 본문
[React, Firebase] TwitterCloneCoding 3.0 File Upload, Delete file
밍맹030 2021. 10. 17. 21:08< 구현할 화면 >
1. Home 화면에서 Tweet을 생성 할 때 file과 글을 함께 올릴 수 있다.
(글과 사진 중 하나만 올리는 것 또한 가능.)
2. 파일 선택 후 Clear Photo 버튼을 통해 선택을 취소할 수 있다.
3. 파일 선택 및 글 작성 후 Tweet 버튼을 클릭하면 글과 파일이 함께 post된다.
4. Delete Tweet 버튼을 클릭하면 Collection에서 tweet 객체가 삭제되고
해당 tweet 객체에 file이 있었다면, Storage에 올라간 file도 함께 삭제된다.
< firebase의 collection > : attachmentUrl의 이름으로 file의 url(경로) 항목이 추가된다.
< firebase 의 storage > : user가 upload한 file 관련 정보를 확인할 수 있다.
< fbase.js >
import { initializeApp } from "firebase/app";
import { getAuth } from "firebase/auth";
import { getFirestore } from "firebase/firestore";
import { getStorage } from "firebase/storage";
const firebaseConfig = {
apiKey: process.env.REACT_APP_API_KEY,
authDomain: process.env.REACT_APP_API_AUTH_DOMAIN,
projectId: process.env.REACT_APP_API_PROJECT_ID,
storageBucket: process.env.REACT_APP_API_STORAGE_BUCKET,
messagingSenderId: process.env.REACT_APP_API_MESSAGING_SENDER_ID,
appId: process.env.REACT_APP_API_APP_ID,
};
initializeApp(firebaseConfig);
export const authService = getAuth();
export const dbService = getFirestore();
export const storageService = getStorage();
- firebase의 storage 사용을 위해 import와 export를 해준다.
< Home.js >
import { dbService, storageService } from "fBase";
import React, { useEffect, useState } from "react";
import Tweet from "../components/Tweet";
import { collection, addDoc, query, onSnapshot, orderBy, serverTimestamp } from '@firebase/firestore'
import { ref, uploadString,getDownloadURL } from "@firebase/storage";
import {v4} from 'uuid';
const Home = ({userInfo}) => {
const [tweet, setTweet] = useState('');
const [tweets, setTweets] = useState([]);
const [attachment, setAttachment] = useState("");
useEffect(() => {
const q = query(collection(dbService, 'tweets'), orderBy('createdAt', 'desc'))
const unsubscribe = onSnapshot(q, (querySnapshot) => {
const nextTweets = querySnapshot.docs.map((document) => {
return {
id: document.id,
...document.data(),
}
})
setTweets(nextTweets);
})
return () => {
unsubscribe();
}
}, [])
const OnSubmit = async (e) => {
e.preventDefault();
let attachmentUrl = "";
if (attachment !== "") {
const fileRef = ref(storageService, `${userInfo[Object.keys(userInfo)[0]].uid}/${v4()}`);
const uploadFile = await uploadString(fileRef, attachment, "data_url");
attachmentUrl = await getDownloadURL(uploadFile.ref);
}
const tweetObj = {
text : tweet,
createdAt: serverTimestamp(),
creatorId : userInfo[Object.keys(userInfo)[0]].uid,
attachmentUrl
};
await addDoc(collection(dbService, "tweets"),tweetObj);
setTweet("");
setAttachment("");
}
const OnChange = (e) => {
const { target: { value } } = e
setTweet(value);
}
const onFileChange = (event) =>{
const {
target : {files},
} = event;
const theFile = files[0];
const reader = new FileReader();
reader.onloadend = (finishedEvent) =>{
const {
currentTarget : {result},
}=finishedEvent;
setAttachment(result);
}
reader.readAsDataURL(theFile);
}
const onClearAttachment=()=>setAttachment("");
return (
<div>
<form onSubmit={OnSubmit}>
<input type="text" placeholder="What's on your mind?"
maxLength={120} onChange={OnChange} value={tweet} />
<input type="file" accept="image/*" onChange={onFileChange}/>
<input type="submit" value="Tweet" />
{attachment&&(
<div>
<img src={attachment} width="100px" height="100px"/>
<button onClick={onClearAttachment}>Clear Photo</button>
</div>
)}
</form>
<div>
{tweets.map((tweet) => (
<Tweet key={tweet.id}
tweetObj={tweet}
isOwner={tweet.creatorId===userInfo[Object.keys(userInfo)[0]].uid}
/>
))}
</div>
</div>
)
}
export default Home;
1. 기존 코드에 추가된 import와 state
import { ref, uploadString,getDownloadURL } from "@firebase/storage";
import {v4} from 'uuid';
const Home = ({userInfo}) => {
const [tweet, setTweet] = useState('');
const [tweets, setTweets] = useState([]);
const [attachment, setAttachment] = useState("");
- import {v4} from 'uuid'; → "npm install uuid"를 통해 설치하며, 특정 식별자를 랜덤으로 생성해준다.
- const [attachment, setAttachment] = useState("");
: attachment는 사용자가 tweet 생서 시 file을 선택할 경우 해당 file의 url(경로)를 가진다.
2. onFileChange
const onFileChange = (event) =>{
const { // event의 target안으로 가서 files를 받아 변수로 저장
target : {files},
} = event;
const theFile = files[0];
const reader = new FileReader();
reader.onloadend = (finishedEvent) =>{
const { // finishedEvent currentTarget 가서 result 받아 변수로 저장
currentTarget : {result},
} = finishedEvent;
setAttachment(result);
}
reader.readAsDataURL(theFile);
}
- file이 선택된 경우 작용하는 event handler이다.
- theFile : user가 선택한 file 객체를 저장한 변수
- reader.readAsDataURL(theFile) : theFile 의 data를 Url형식의(base64로 인코딩된) 문자열로 가져온다.
- reader.onloadend = (finishedEvent) => { ... } : file의 읽기 작업이 완료되면 readyState가 DONE으로 변경되고 loadend가 trigger 된다. 이 때 reader의 result 속성은 URL형식의 data를 가진다. 이 result를 사용하여 attachment를 변경한다.
FileReader.readAsDataURL() https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsDataURL
FileReader.onload : https://developer.mozilla.org/en-US/docs/Web/API/FileReader/onload
3. onClearAttachment
const onClearAttachment=()=>setAttachment("");
- photo 선택 후, 선택 취소를 위해 clear photo 버튼을 click할 경우 작용하는 event handler로, setAttachment를 이용하여 attachment를 ""로 변경한다.
4. OnSubmit
const OnSubmit = async (e) => {
e.preventDefault();
let attachmentUrl = "";
if (attachment !== "") {
const fileRef = ref(storageService, `${userInfo[Object.keys(userInfo)[0]].uid}/${v4()}`);
const uploadFile = await uploadString(fileRef, attachment, "data_url");
attachmentUrl = await getDownloadURL(uploadFile.ref);
}
const tweetObj = {
text : tweet,
createdAt: serverTimestamp(),
creatorId : userInfo[Object.keys(userInfo)[0]].uid,
attachmentUrl
};
await addDoc(collection(dbService, "tweets"),tweetObj);
setTweet("");
setAttachment("");
}
-1) storage에 fileRef 의 이름으로 이미지 폴더를 생성한다.
const fileRef = ref(storageService, `${userInfo[Object.keys(userInfo)[0]].uid}/${v4()}`)
-2) 생성한 fileRef 폴더에 이미지를 넣는다.
fileRef : storage에 생성한 image 폴더
attachment : user가 선택한 file의 url
"data_url" : data_url로 인코딩된 문자열을 추가한다는 의미
const uploadFile = await uploadString(fileRef, attachment, "data_url");
-3) ref 속성을 쓰면 그 이미지의 Reference에 접근 가능, 이미지가 저장된 stroage 주소를 받을 수 있다.
attachmentUrl = await getDownloadURL(uploadFile.ref);
-4) 기존 tweetObj 에 file 경로를 추가한다.
-5) collection에 tweetObj 를 추가 완료하면 Tweet 입력 state와 file 입력 state를 ""로 초기화한다.
5.
return (
<div>
<form onSubmit={OnSubmit}>
<input type="text" placeholder="What's on your mind?"
maxLength={120} onChange={OnChange} value={tweet} />
<input type="file" accept="image/*" onChange={onFileChange}/>
<input type="submit" value="Tweet" />
{attachment&&(
<div>
<img src={attachment} width="100px" height="100px"/>
<button onClick={onClearAttachment}>Clear Photo</button>
</div>
)}
</form>
<div>
{tweets.map((tweet) => (
<Tweet key={tweet.id}
tweetObj={tweet}
isOwner={tweet.creatorId===userInfo[Object.keys(userInfo)[0]].uid}
/>
))}
</div>
</div>
)
}
export default Home;
- img 태그 추가 : user가 file을 선택하면 onFileChange event handler가 작용한다.
<input type="file" accept="image/*" onChange={onFileChange}/>
- attachment(file)이 존재하는 경우 미리보기 형식으로 form 태그 내에서 file을 보여준다.
{attachment&&(
<div>
<img src={attachment} width="100px" height="100px"/>
<button onClick={onClearAttachment}>Clear Photo</button>
</div>
)}
< Tweet.js > : 삭제 부분 및 tweetObj upload부분 수정
import React, {useState} from 'react';
import { deleteDoc, updateDoc, doc } from '@firebase/firestore'
import { dbService, storageService } from "fBase";
import { ref, deleteObject } from "@firebase/storage";
const Tweet = ({ tweetObj, isOwner})=>{
const TweetTextRef = doc(dbService, "tweets", `${tweetObj.id}`);
const [editing, setEditing]= useState(false);
const [newTweet, setNewTweet] = useState(tweetObj.text);
const onDeleteClick = async() =>{
const ok=window.confirm("Are you sure you wanna delete this tweet?");
if (ok) {
await deleteDoc(doc(dbService, `tweets/${tweetObj.id}`));
if(tweetObj.attachmentUrl)
await deleteObject(ref(storageService, tweetObj.attachmentUrl));
}
};
const onSubmit=async(e)=>{
e.preventDefault();
await updateDoc(TweetTextRef, { text: newTweet });
setEditing(false);
};
const onChange =(e)=>{
const{ target : {value} }= e;
setNewTweet(value);
};
const toggledEditing = () => setEditing((prev)=>!prev);
return(
<div>
{ editing ?
<>
<form onSubmit={onSubmit}>
<input type="text" placeholder="Edit your tweet"
value={newTweet} required onChange={onChange}/>
<input type="submit" value="Update Tweet"/>
</form>
<button onClick={toggledEditing}>Cancel</button>
</>
: <>
<h4>{tweetObj.text}</h4>
{tweetObj.attachmentUrl && <img src={tweetObj.attachmentUrl} width="100px" height="100px" /> }
{isOwner&&(
<>
<button onClick={onDeleteClick}>Delete Tweet</button>
<button onClick={toggledEditing}>Edit Tweet</button>
</>
)}
</>
}
</div>
);
}
export default Tweet;
1.
const onDeleteClick = async() =>{
const ok=window.confirm("Are you sure you wanna delete this tweet?");
if (ok) {
await deleteDoc(doc(dbService, `tweets/${tweetObj.id}`));
if(tweetObj.attachmentUrl)
await deleteObject(ref(storageService, tweetObj.attachmentUrl));
}
};
- tweetObj에 file이 존재하는 경우 tweetObj.attachmentUrl로부터에서 reference를 받아와, storage로부터 해당 id를 가진 tweet의 file을 삭제한다.
2.
return(
<div>
{ editing ?
<>
<form onSubmit={onSubmit}>
<input type="text" placeholder="Edit your tweet"
value={newTweet} required onChange={onChange}/>
<input type="submit" value="Update Tweet"/>
</form>
<button onClick={toggledEditing}>Cancel</button>
</>
: <>
<h4>{tweetObj.text}</h4>
{tweetObj.attachmentUrl && <img src={tweetObj.attachmentUrl} width="100px" height="100px" /> }
{isOwner&&(
<>
<button onClick={onDeleteClick}>Delete Tweet</button>
<button onClick={toggledEditing}>Edit Tweet</button>
</>
)}
</>
}
</div>
);
}
- tweetObj의 text를 출력하고 attachmentUrl이 존재한다면 해당 url에 대한 img를 함께 보여준다.
<h4>{tweetObj.text}</h4>
{tweetObj.attachmentUrl && <img src={tweetObj.attachmentUrl} width="100px" height="100px" /> }
'Web > React' 카테고리의 다른 글
<React 파헤치기-01> npm이랑 webpack은 무엇일까? (0) | 2021.12.09 |
---|---|
[React, Firebase] TwitterCloneCoding 4.0 Edit Profile (0) | 2021.10.18 |
[React, Firebase] TwitterCloneCoding 2.1 Tweet 삭제, 수정 (4) | 2021.10.12 |
[React, Firebase] TwitterCloneCoding 2.0 New Tweet 작성 후 database에 저장하기 (0) | 2021.10.12 |
[React, Firebase] TwitterCloneCoding 1.0 Authentication-Google 로그인 (0) | 2021.10.12 |