21.10.25 ๊ณต๋ถ€๊ธฐ๋ก๐Ÿง‘โ€๐Ÿ’ป

FireBase, React๋ฅผ ํ™œ์šฉํ•œ ํŠธ์œ„ํ„ฐ ํด๋ก ์ฝ”๋”ฉ๐Ÿง‘โ€๐Ÿ’ป


์˜ค๋Š˜ ์™„๋ฃŒํ•œ ๋ชฉ๋ก
1. ํŠธ์œ— ์‚ญ์ œ ๊ธฐ๋Šฅ
2. ํŠธ์œ— ์ˆ˜์ • ๊ธฐ๋Šฅ
3. ํŠธ์œ— ์ด๋ฏธ์ง€ ์ฒจ๋ถ€ ๋ฐ ์‚ญ์ œ ๊ธฐ๋Šฅ
(์•Œ๊ณ ๋ฆฌ์ฆ˜ ์•„์ฃผ ์กฐ๊ธˆ..)


1. ํŠธ์œ— ์‚ญ์ œ ๊ธฐ๋Šฅ

Nweet.js

    const NweetTextRef = doc(dbService,"nweets", `${nweetObj.id}`); 
    // ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ํŠธ์œ—์˜ ์•„์ด๋””๋ฅผ ์ฐพ๊ธฐ ์œ„ํ•œ ์ฝ”๋“œ.

    const onDeleteClick = async () => { // ํŠธ์œ— ์‚ญ์ œ ํ•จ์ˆ˜
        const ok = window.confirm("Are you sure you want to delete this tweet?");        
        if(ok){
           await deleteDoc(NweetTextRef); // ํŠธ์œ— ์‚ญ์ œ๊ธฐ๋Šฅ
        };
    }
    .
    .
    .
     <button onClick={onDeleteClick}>Delete tweet</button>

๋‚ด๊ฐ€ ์“ด ํŠธ์œ—์„ ์‚ญ์ œํ•˜๊ฑฐ๋‚˜ ์ˆ˜์ •ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋จผ์ € ํŒŒ์ด์–ด๋ฒ ์ด์Šค์— ๋‚ด๊ฐ€ ์“ด ํŠธ์œ—์˜ ์•„์ด๋””๋ฅผ ์ฐพ์•„์•ผํ–ˆ๋‹ค.
๋ฌธ์„œ ์•„์ด๋””๋Š” nweetObj.id์— ์žˆ๊ธฐ ๋•Œ๋ฌธ์— doc(dbService,"nweets", ${nweetObj.id})๋ฅผ ์ด์šฉํ•˜์—ฌ ํŠธ์œ—์˜ ์•„์ด๋””๋ฅผ ๋ฐ›์•„์™€ ๋ณ€์ˆ˜์— ์ €์žฅํ–ˆ๋‹ค.
๋ณ€์ˆ˜์— ๋„ฃ์€ ์ด์œ ๋Š” ํŠธ์œ—์•„์ด๋””๋ฅผ ๋ฐ›์•„์˜ค๋Š” ์ฝ”๋“œ๊ฐ€ ๋„ˆ๋ฌด ๊ธธ๊ณ  ๋ณต์žกํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ณด๊ธฐ ์‰ฝ๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด์„œ ๋ณ€์ˆ˜๋ฅผ ์ด์šฉํ–ˆ๋‹ค.

ํŠธ์œ—์„ ์‚ญ์ œ(๋ฌธ์„œ๋ฅผ ์‚ญ์ œ)ํ•˜๋Š” ํ•จ์ˆ˜๋Š” deleteDoc()ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ–ˆ๋Š”๋ฐ ์ด ํ•จ์ˆ˜๋Š” ํŒŒ์ด์–ด๋ฒ ์ด์Šค์—์„œ ์‚ฌ์šฉํ•˜๋Š” ํ•จ์ˆ˜๋‹ค. ๋ฒ„ํŠผ์— Onclick ์†์„ฑ์„ ์ค˜์„œ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด onDeleteClick ํ•จ์ˆ˜๊ฐ€ ๋ฐœ๋™ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋งŒ๋“ค์—ˆ๋‹ค.
๊ทธ๋ฆฌ๊ณ  ๋ฒ„ํŠผ์„ ๋ˆŒ๋ €์„ ๋•Œ ์ •๋ง ์‚ญ์ œํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?์™€ ๊ฐ™์€ ์ฐฝ์„ ๋„์–ด ํ™•์ธ์„ ๋ˆ„๋ฅด๋ฉด ์‚ญ์ œ๊ฐ€ ๋  ์ˆ˜ ์žˆ๊ฒŒ ์ฝ”๋“œ๋ฅผ ์งฐ๋‹ค.

์ฐธ๊ณ  : https://firebase.google.com/docs/firestore/manage-data/delete-data?hl=ko


2. ํŠธ์œ— ์ˆ˜์ • ๊ธฐ๋Šฅ

nweet.js


const [editing, setEditing] = useState(false);
    // ์ด ์ฝ”๋“œ๋Š” ์ˆ˜์ • ๋ฒ„ํŠผ์„ ํด๋ฆญํ–ˆ์„ ๋•Œ ์ž…๋ ฅ๋ž€๊ณผ ๋ฒ„ํŠผ์ด ๋œจ๊ฒŒํ•˜๋Š” ๊ธฐ์ค€์ ์ž„.
        const [newNweet, setNewNweet] = useState(nweetObj.nweet)
    // ์ˆ˜์ •ํ•  ๋•Œ ์ž…๋ ฅ๋ž€์— ๊ธฐ์กด์˜ ํŠธ์œ—์„ ๋‚˜ํƒ€๋‚ด๊ธฐ ์œ„ํ•œ state
    // .
    // .
    
const toggleEditing = () => {setEditing((prev) => !prev)}
    
const onSubmit =  async (event) => {
        event.preventDefault();
        await updateDoc(NweetTextRef,{
        nweet: newNweet,
        });

        setEditing(false);
    }
 const onChange = (event) => {
        const {
            target : {value},
        } = event;
        setNewNweet(value);
   }
    .
    .
     {editing ? (
            <>
            <form onSubmit={onSubmit}>
                <input onChange={onChange} type="text" placeholder ="Edit you tweet!" value={newNweet} required />
                <input type ="submit" value="Update tweet"/>
            </form>
            <button onClick={toggleEditing}>Cancel</button>
            </>
        ) :
    .
    .
    <button onClick={toggleEditing}>Edit Nweet</button>      

ํŠธ์œ— ์ˆ˜์ •๊ธฐ๋Šฅ์€ ์‚ญ์ œ๋ณด๋‹ค ๋” ๋ณต์žกํ–ˆ๋‹ค. ์ˆ˜์ •๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ๊ธ€์„ ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ๊ฒŒ submit์„ ์ž…๋ ฅํ–ˆ๋‹ค.
์ˆ˜์ • ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด submit๊ณผ ์ˆ˜์ •๋ฒ„ํŠผ, ์ทจ์†Œ๋ฒ„ํŠผ์ด ๋‚˜์˜ฌ ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๊ณ  ์‹ถ์—ˆ๋‹ค.
๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ๊ธฐ์ค€์ ์„ ๋งŒ๋“ค์–ด์•ผ ํ–ˆ๋‹ค. ๊ทธ๋ž˜์„œ useState๋ฅผ ์ด์šฉํ•˜์—ฌ setEditing์ด๋ผ๋Š” ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด
์ž…๋ ฅ๋ž€๊ณผ ๋ฒ„ํŠผ์ด ๋‚˜์˜ค๋Š” ๊ธฐ์ค€์ ์„ ๋งŒ๋“ค์—ˆ๋‹ค.
๊ทธ๋ฆฌ๊ณ  ์ˆ˜์ •ํ•˜๊ธฐ ์ „์— ๊ธฐ์กด์˜ ํŠธ์œ— ๊ธ€์ด ๋‚˜ํƒ€๋‚˜๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด setNewNweet๋„ ๋งŒ๋“ค์—ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  onChangeํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์—ˆ๋Š”๋ฐ ์ž…๋ ฅ๋ž€์— ํƒ์ŠคํŠธ๋ฅผ ์ž…๋ ฅํ•ด์•ผํ•˜๋Š” ๊ฒฝ์šฐ onchangeํ”„๋กญ์Šค, ํ•จ์ˆ˜ ์ž‘์—…์„ ํ•ด์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋งŒ๋“ค์—ˆ๋‹ค. ๋งŒ์•ฝ onChange๊ฐ€ ์—†์œผ๋ฉด ์ž…๋ ฅ๋ž€์— ๊ธ€์„ ์ž…๋ ฅํ•  ์ˆ˜ ์—†๊ฒŒ ๋œ๋‹ค.

onChange ํ•จ์ˆ˜ ์•ˆ์— setNewNweet(value);๊ฐ€ ์จ์žˆ๋Š”๋ฐ, ์ด๋Š” ํ‚ค๋ณด๋“œ ์ž…๋ ฅํ•  ๋•Œ ํ•จ์ˆ˜์˜ value์ธ์ž๋กœ ์ž…๋ ฅ๊ฐ’์ด ๋„˜์–ด๊ฐ€๊ณ  value๊ฐ’์„ newNweet์— ๋ฐ˜์˜ํ•ด์„œ ์ž…๋ ฅ๋ž€์— ์ž…๋ ฅํ•œ ํ…์ŠคํŠธ๊ฐ€ ๋ณด์ด๊ฒŒ ํ–ˆ๋‹ค.

const toggleEditing = () => {setEditing((prev) => !prev)} 

์ด ์ฝ”๋“œ๋Š” ์ด์ „ ์ƒํƒœ๋ฅผ setEditing์— ๋„˜๊ฒจ์ค€ ์ฒซ๋ฒˆ์งธ ์ธ์ž๋กœ ๋ฐ›์€ ๋‹ค์Œ !์—ฐ์‚ฐ์ž๋ฅผ ์ด์šฉํ•˜์—ฌ ์ด์ „์ƒํƒœ๋ฅผ ๊ด€๋ฆฌ ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ–ˆ๋‹ค.

{editing ? (
           <>
           <form onSubmit={onSubmit}>
               <input onChange={onChange} type="text" placeholder ="Edit you tweet!" value={newNweet} required />
               <input type ="submit" value="Update tweet"/>
           </form>
           <button onClick={toggleEditing}>Cancel</button>
           </>
       ) :

์ˆ˜์ • ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด editing์ด false์—์„œ true๋กœ ๋ฐ”๋€Œ๋ฉด์„œ ์ž…๋ ฅ๋ž€๊ณผ ๋ฒ„ํŠผ์ด ๋‚˜์˜ฌ ์ˆ˜ ์žˆ๊ฒŒ ํ–ˆ๋‹ค.

const onSubmit =  async (event) => {
       event.preventDefault();
       await updateDoc(NweetTextRef,{
       nweet: newNweet,
       });

       setEditing(false);
   }

๋‚œ onSubmit์ด ํ•ต์‹ฌ์ด๋ผ๊ณ  ์ƒ๊ฐํ•œ๋‹ค, ๋จผ์ € event.preventDefault();๋Š” submit์„ ํ–ˆ์„ ๋•Œ ์ƒˆ๋กœ๊ณ ์นจ์ด ๋˜์ง€ ์•Š๊ฒŒ ๋„ฃ์—ˆ๊ณ  updateDoc๋ผ๋Š” ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•˜์—ฌ ์ˆ˜์ •์„ ํ–ˆ๋‹ค.

ํŒŒ์ด์–ด๋ฒ ์ด์Šค์— ๋‚˜์™€์žˆ๋Š” ์‚ฌ์šฉ๋ฒ•์ด๋‹ค.
NweetTextRef๋Š” ์•„๊นŒ ์œ„์—์„œ ์„ค๋ช…ํ•œ ๊ฒƒ ์ฒ˜๋Ÿผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ํŠธ์œ— ์•„์ด๋””๋ฅผ ์ฐพ๊ธฐ ์œ„ํ•œ ๋ณ€์ˆ˜๋‹ค.
nweet : newNweet๋ฅผ ์ž…๋ ฅํ•œ ๊ฒƒ์€ ์ž…๋ ฅ๋ž€์— ์ƒˆ๋กœ์šด ํŠธ์œ—์„ ์ž…๋ ฅํ•˜๊ธฐ ์œ„ํ•ด ์ƒํƒœ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•œ newNweet ์—…๋ฐ์ดํŠธ๋ฅผ ์‹œ์ผœ์ค˜ ์ˆ˜์ •์„ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ–ˆ๋‹ค.
๊ทธ๋ฆฌ๊ณ  submit์ด ์™„๋ฃŒ๊ฐ€ ๋˜๋ฉด ์ž…๋ ฅ๋ž€๊ณผ ์ˆ˜์ •๋ฒ„ํŠผ์ด ์‚ฌ๋ผ์ง€๊ฒŒ setEditing์„ false๋กœ ๋งŒ๋“ค์–ด์คฌ๋‹ค.


3. ํŠธ์œ— ์ด๋ฏธ์ง€ ์ฒจ๋ถ€ ๋ฐ ์‚ญ์ œ ๊ธฐ๋Šฅ

ํŠธ์œ— ์ด๋ฏธ์ง€ ์ฒจ๋ถ€

home.js

const [attachment, setAttachment] = useState("");
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); 
   }; 
   .
   .
   .
   <input type="file" accept="image/*" onChange={onFileChange}/>

์ด๋ฏธ์ง€๋ฅผ ์ฒจ๋ถ€ํ•˜๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด input ํƒ€์ž…์„ ํŒŒ์ผ๋กœ ๋ฐ›์•„ ์ด๋ฏธ์ง€๋ฅผ ๋ฐ›์•„์˜ฌ ์ˆ˜ ์žˆ๊ฒŒ ์ฝ”๋“œ๋ฅผ ๋จผ์ € ์งฐ๋‹ค.
์ด๋ฏธ์ง€๋ฅผ ๋‚˜ํƒ€๋‚ด๊ธฐ ์œ„ํ•ด์„œ๋Š” ์ด๋ฏธ์ง€ ํŒŒ์ผ์˜ ์ •ํ™•ํ•œ target์„ ์•Œ์•„์•ผํ–ˆ๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์—
console.log๋กœ files๋ฐฐ์—ด์ด ํƒ€๊ฒŸ์ด๋ผ๋Š” ๊ฒƒ์„ ์•Œ์•˜๋‹ค.
files ๋ฐฐ์—ด์„ theFile ๋ณ€์ˆ˜์— ์ €์žฅํ•ด๋†จ๋‹ค.
ํ•˜์ง€๋งŒ ํŒŒ์ผ์„ ์›น๋ธŒ๋ผ์šฐ์ €์— ์ถœ๋ ฅํ•˜๋ ค๋ฉด ๋ธŒ๋ผ์šฐ์ € API๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ์•˜๋‹ค.
๊ทธ๋ž˜์„œ FileReader ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ–ˆ๋‹ค.
(์‚ฌ์šฉ๋ฒ• https://developer.mozilla.org/ko/docs/Web/API/FileReader)
๊ทธ ํ›„ reader ๋ณ€์ˆ˜์— ๋„ฃ์–ด์ค€ ํ›„ readAsDataURL()ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•˜์—ฌ ํŒŒ์ผ ์ •๋ณด๋ฅผ ์ธ์ž๋กœ ๋ฐ›์•„ ํŒŒ์ผ ์œ„์น˜๋ฅผ url๋กœ ๋ฐ˜ํ™˜ํ•ด์คฌ๋‹ค.
์ด ํ•จ์ˆ˜๋Š” ๋‹จ์ˆœํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜๋Š” ์—†๊ณ  ์›น๋ธŒ๋ผ์šฐ์ €๊ฐ€ ํŒŒ์ผ์„ ์ธ์‹ํ•˜๊ณ  ์ธ์‹์ด ๋๋‚˜๋Š” ์‹œ์ ์„ ํฌํ•จํ•˜๊ณ  ์žˆ์–ด ๊ทธ ์‹œ์ ์„ ํ•จ๊ป˜ ๊ด€๋ฆฌํ•ด์ค˜์•ผ url์„ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.

(FileReader.readAsDataURL()
์ง€์ •๋œ ์˜ ๋‚ด์šฉ ์ฝ๊ธฐ๋ฅผ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค. Blob์™„๋ฃŒ๋˜๋ฉด result์†์„ฑ data:์— ํŒŒ์ผ ๋ฐ์ดํ„ฐ๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” URL์ด ํฌํ•จ ๋ฉ๋‹ˆ๋‹ค.)

๊ทธ๋ฆฌ๊ณ  onloadendํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•˜์—ฌ ๊ฒฐ๊ณผ ๊ฐ’์ด ๋‚˜์˜จ ์ดํ›„ ์ธ์‹์ด ๋๋‚˜๋Š” ์‹œ์  ์ดํ›„์˜ ์ด๋ฒคํŠธ ๊ฐ’์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์คฌ๋‹ค. ์ด ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•˜์—ฌ ํŒŒ์ผ์˜ url์ด ์žˆ๋Š” result๋ผ๋Š” ํƒ€๊ฒŸ์„ ์ฐพ์•˜๋‹ค.

const [attachment, setAttachment] = useState("");

์‚ฌ์ง„์˜ Url์„ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ์ƒํƒœ๊ฐ€ ์žˆ์–ด์•ผ ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— useState๋ฅผ ์ด์šฉํ•ด setAttachment ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด result(url)๋ฅผ ์ €์žฅํ–ˆ๋‹ค.

{attachment && (
          <div>
            <img src={attachment} width="50px" height="50px" />
          </div>
        )}

๊ทธ๋ž˜์„œ ์ด๋ฏธ์ง€ url์ด ์žˆ์„ ๋•Œ๋งŒ ์ด๋ฏธ์ง€๋ฅผ ์ฒจ๋ถ€ํ•  ์ˆ˜ ์žˆ๊ฒŒ src์— attachment๋ฅผ ๋„ฃ์–ด์คฌ๋‹ค.

์ด๋ฏธ์ง€ ์‚ญ์ œ ๊ธฐ๋Šฅ

import {useRef} from React;

const fileInput = useRef(); 
const onClearPhoto= () =>{
        setAttachment("");
        fileInput.current.value = "";
    }

 <input type="file" accept="image/*" onChange={onFileChange} ref={fileInput} />
 <button onClick={onClearPhoto}>Clear</button>

์ด๋ฏธ์ง€ ์‚ญ์ œ๋ฅผ ํ•˜๊ธฐ ์œ„ํ•ด ์‚ญ์ œ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด์คฌ๋Š”๋ฐ, ๊ฐ„๋‹จํ•˜๊ฒŒ ์„ค๋ช…ํ•˜์ž๋ฉด ์ด๋ฏธ์ง€ url๋ฅผ ์ €์žฅํ•˜๋Š” setAttachmentํ•จ์ˆ˜์— Null๊ฐ’์„ ๋„ฃ์–ด url์„ ์—†๊ฒŒ ํ–ˆ๋‹ค.
ํ•˜์ง€๋งŒ ์‚ญ์ œ ์ดํ›„์— ์ด๋ฏธ์ง€ ํŒŒ์ผ๋ช…์ด ๋‚จ์•„์žˆ์–ด ์ง€์šฐ๊ธฐ ์œ„ํ•ด useRef()ํ›…์„ ์ด์šฉํ•˜์—ฌ fileInput๋ณ€์ˆ˜๋ฅผ ๋งŒ๋“ค๊ณ 
clear์„ ๋ˆŒ๋ €์„ ๋•Œ fileInput์•ˆ์— ์žˆ๋Š” current.value์˜ ๊ฐ’์„ ๊ฐ€์ ธ์™€์„œ ๋น„์›Œ์ฃผ๊ฒŒ ํ–ˆ๋‹ค.

(์‚ฌ์šฉ๋ฒ• : https://ko.reactjs.org/docs/hooks-reference.html#useref)


์ „์ฒด ์ฝ”๋“œ

home.js

import { dbService } from 'fbase';
import React,{useState, useEffect, useRef} from "react";
import {addDoc, collection,query, onSnapshot,orderBy} from "firebase/firestore";
import Nweet from "components/Nweet"

const Home = ({userObj}) => {
    const [nweet, setNweet] = useState("");
    const [nweets, setNweets] = useState([]); //  ํŠธ์œ—๋“ค์„  ์ƒํƒœ๋กœ ๋ฐ›์•„์„œ ๋ณด๊ด€ํ•ด์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฐฐ์—ด๋กœ usestate ์ƒ์„ฑ
    const [attachment, setAttachment] = useState(""); // ์‚ฌ์ง„ํŒŒ์ผ url์„ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ state
    useEffect(() => {
        onSnapshot( // OnSnapshot ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•˜์—ฌ ๋ชจ๋“  ์Šค๋ƒ…์ƒท์„ ๋ฐ˜ํ™˜ํ•จ.
        query(collection(dbService, "nweets"), orderBy("createdAt", "desc")),
        (snapshot) => {
        const nweetArray = snapshot.docs.map((doc) => ({
        id: doc.id, // map ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•˜์—ฌ ์Šค๋ƒ…์ƒท์—์„œ ์›ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋งŒ ๋ฝ‘์•„์„œ ๋ฐฐ์—ดํ™” ์‹œํ‚จ ํ›„ ํ™”๋ฉด์— ๋‚˜ํƒ€๋ƒ„.
        ...doc.data(), // ์ „์— ์‚ฌ์šฉํ–ˆ๋˜ Foreachํ•จ์ˆ˜๋Š” ๋งค ์ˆœํšŒ๋งˆ๋‹ค setNweets๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•˜์ง€๋งŒ, mapํ•จ์ˆ˜๋Š” ์ˆœํšŒํ•˜๋ฉด์„œ ๋งŒ๋“  ๋ฐฐ์—ด์„ ๋ฐ˜ํ™˜ํ•˜๋ฏ€๋กœ
                        // ๋ฐ˜ํ™˜ํ•œ ๋ฐฐ์—ด์„ 1๋ฒˆ๋งŒ setNweetํ•จ์ˆ˜์— ์ „๋‹ฌํ•˜๋ฉด ๋˜๊ธฐ ๋•Œ๋ฌธ์— ํ›จ์”ฌ ํšจ์œจ์ ์ด๋‹ค.
        }));
        setNweets(nweetArray);
        }
        );
        }, []);


    const onSubmit = async (e) => {
        try{
        e.preventDefault();
        const docRef = await addDoc(collection(dbService, "nweets"),
            {
            nweet,
            createdAt: Date.now(),
            creatorId : userObj.uid, // db์— ์œ ์ €์•„์ด๋”” ์ถ”๊ฐ€
        }); // ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ƒ์„ฑ 
        console.log("Document Written with Id:", docRef.id);
        }catch(error){
            console.log("Error adding document", error)
        }

        setNweet("");
    };
    const onChange = (event) =>{
        const {target : {value},
        }= event;
        setNweet(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); // ํŒŒ์ผ ์ •๋ณด๋ฅผ ์ธ์ž๋กœ ๋ฐ›์•„ ํŒŒ์ผ ์œ„์น˜๋ฅผ url๋กœ ๋ฐ˜ํ™˜ํ•ด์คŒ.
    }; // ์‚ฌ์ง„ ํŒŒ์ผ ์—…๋กœ๋“œ ์ฝ”๋“œ
    
    const fileInput = useRef(); //  ์ด๋ฏธ์ง€ ํŒŒ์ผ๋ช…์„ ์ง€์šฐ๊ธฐ ์œ„ํ•ด useRef ํ›… ์‚ฌ์šฉ

    const onClearPhoto= () =>{
        setAttachment("");
        fileInput.current.value = "";
    } 
    



return (
 <>   
<div>
    <form onSubmit={onSubmit}>
        <input value={nweet} onChange={onChange} type = "text" placeholder ="what's on your mind" maxLength={120} />
        <input type="file" accept="image/*" onChange={onFileChange} ref={fileInput} />
        <input type="submit" value="nweet"/>
        
         {attachment && (
          <div>
            <img src={attachment} width="50px" height="50px" />
            <button onClick={onClearPhoto}>Clear</button>
          </div>
        )}

    </form>
    <div> 
        {nweets.map((nweet) => (
// map ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•˜์—ฌ nweets ๋ฐฐ์—ด์„ ์ˆœํšŒํ•˜๋ฉด์„œ jsx๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ฒŒ ๋งŒ๋“ค์–ด์„œ ํŠธ์œ— ๋ฐฐ์—ด๋“ค์„ ์›น์— ๋‚˜ํƒ€๋ƒ„.
        <Nweet 
        key={nweet.id} 
        nweetObj={nweet}
        isOwner={nweet.creatorId === userObj.uid}
         // isOwner  nweet.creatorId === userObj.uid๊ฐ€ ๊ฐ™์•„์•ผ ๊ถŒํ•œ์„ ์ค„ ์ˆ˜ ์žˆ๊ฒŒ ์„ค์ •.
        />
            ))}
    </div>
</div>
</>
    );
};



export default Home;

nweet.js

import React ,{useState}from "react";
import { dbService } from 'fbase';
import {doc,deleteDoc,updateDoc} from "firebase/firestore";



const Nweet = ({nweetObj, isOwner}) =>{
    const [editing, setEditing] = useState(false);
    // ์ด ์ฝ”๋“œ๋Š” ์ˆ˜์ • ๋ฒ„ํŠผ์„ ํด๋ฆญํ–ˆ์„ ๋•Œ ์ž…๋ ฅ๋ž€๊ณผ ๋ฒ„ํŠผ์ด ๋œจ๊ฒŒํ•˜๋Š” ๊ธฐ์ค€์ ์ž„.
    const [newNweet, setNewNweet] = useState(nweetObj.nweet)
    // ์ˆ˜์ •ํ•  ๋•Œ ์ž…๋ ฅ๋ž€์— ๊ธฐ์กด์˜ ํŠธ์œ—์„ ๋‚˜ํƒ€๋‚ด๊ธฐ ์œ„ํ•œ state
    const NweetTextRef = doc(dbService,"nweets", `${nweetObj.id}`); 
    // ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ํŠธ์œ—์˜ ์•„์ด๋””๋ฅผ ์ฐพ๊ธฐ ์œ„ํ•œ ์ฝ”๋“œ.

    const onDeleteClick = async () => { // ํŠธ์œ— ์‚ญ์ œ ํ•จ์ˆ˜
        const ok = window.confirm("Are you sure you want to delete this tweet?");        
        if(ok){
           await deleteDoc(NweetTextRef); // ํŠธ์œ— ์‚ญ์ œ๊ธฐ๋Šฅ
        };
    }

    const toggleEditing = () => {setEditing((prev) => !prev)}
    
    const onSubmit =  async (event) => {
        event.preventDefault();
        await updateDoc(NweetTextRef,{
        nweet: newNweet,
        });

        setEditing(false);
        console.log(nweetObj.id, newNweet);
    }
    const onChange = (event) => {
        const {
            target : {value},
        } = event;
        setNewNweet(value);
    } // ์ž…๋ ฅ๋ž€์— ํƒ์ŠคํŠธ๋ฅผ ์ž…๋ ฅํ•ด์•ผํ•˜๋Š” ๊ฒฝ์šฐ onchangeํ”„๋กญ์Šค, ํ•จ์ˆ˜ ์ž‘์—…์„ ํ•ด์•ผํ•œ๋‹ค.


    return (
    <div>
        {editing ? (
            <>
            <form onSubmit={onSubmit}>
                <input onChange={onChange} type="text" placeholder ="Edit you tweet!" value={newNweet} required />
                <input type ="submit" value="Update tweet"/>
            </form>
            <button onClick={toggleEditing}>Cancel</button>
            </>
        ) : (
            <>
            <h4>{nweetObj.nweet}</h4>
            {isOwner && (
                <>
                    <button onClick={onDeleteClick}>Delete tweet</button>
                    <button onClick={toggleEditing}>Edit Nweet</button>            
                </>
            )}
            </>
        )}
    </div>
    );
};

export default Nweet;

์˜ค๋Š˜ ๊ฒฐ๋ก 

๊ณต๋ถ€ํ•  ์ˆ˜๋ก useState์˜ ์ค‘์š”ํ•จ์„ ๋Š๋‚€๋‹ค.. ์ฒ˜์Œ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ๋ฌด์Šจ ๋ง์ธ์ง€ ๋„์ €ํžˆ ์ดํ•ด๊ฐ€ ์•ˆ ๊ฐ”๋Š”๋ฐ
์‚ฌ์šฉํ•ด๋ณผ ์ˆ˜๋ก ์–ด๋Š์ •๋„ ๊ฐ์ด ์˜จ๋‹ค, ๋นจ๋ฆฌ ํŠธ์œ„ํ„ฐ ํด๋ก ์ฝ”๋”ฉ์„ ๋๋‚ด๊ณ  es6๋ž‘ ๋ฆฌ์•กํŠธ๋ฅผ ๋” ๊ณต๋ถ€ํ•˜๊ณ ์‹ถ๋‹ค..

์ˆ˜์ •๊ธฐ๋Šฅ์—์„œ ์—๋Ÿฌ๊ฐ€ ๋งŽ์•„์„œ ๋„ˆ๋ฌด ๋งŽ์€ ์‹œ๊ฐ„์„ ์†Œ๋ชจํ–ˆ๋‹ค..๐Ÿฅฒ
์ง‘์ค‘๋ ฅ์„ ๋†’์ด๊ธฐ ์œ„ํ•ด ๋‚ด์ผ์€ ์นดํŽ˜์—์„œ ๊ณต๋ถ€ํ•˜๋ ค๊ณ  ํ•œ๋‹ค!

์ข‹์€ ์›นํŽ˜์ด์ง€ ์ฆ๊ฒจ์ฐพ๊ธฐ